TweetFollow Us on Twitter

MACINTOSH C

Demonstration Program

Go to Contents
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// List.h
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// 
// This program allows the user to open a window and a movable modal dialog by choosing
// the relevant items in the Demonstration menu.  The window and the dialog box both
// contain two lists.
//
// The cells of one list in the window, and of both lists in the dialog box, contain
// text. The cells of the second list in the window contain icons.
//
// The text lists use the default list definition function.  The list with the icons uses
// a custom list definition function.
//
// The currently active list is indicated by a keyboard focus frame, and can be changed 
// by clicking in the non-active list or by pressing the tab key.
//
// The text list in the window uses the default cell-selection algorithm; accordingly, 
// multiple cells, including discontiguous multiple cells, may be selected.  The cell-
// selection algorithm for the other lists is customised so as to allow the selection of
// only one cell at a time.
//
// All lists support arrow key selection.  The text list in the window and one of the 
// lists in the dialog box support type selection.
//
// The window is provided with an "Extract" push button.  When this button is clicked, or
// when the user double clicks in one of the lists, the current selections in the lists
// are extracted and displayed in the bottom half of the window.  In the dialog box, the
// user's selections are displayed in static text fields embedded in placards below each
// list.
//
// The program utilises the following resources:
//
// Ą  An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit and Demonstration
//    menus (preload, non-purgeable).
//
// Ą  A 'WIND' resource (purgeable) (initially not visible).
//
// Ą  A'DLOG' resource (purgeable) (initially not visible) and associated 'dlgx', 'dftb',
//    and 'DITL' resources (purgeable).
//
// Ą  'CNTL' resources (purgeable) for various controls in both the window and dialog box,
//    including the list controls for the dialog box.
//
// Ą  'ldes' resources associated with the list controls for the dialog box.
//
// Ą  'STR#' resources (purgeable) containing the text strings for the text lists and
//    for the titles of the icons.
//
// Ą  An icon suite (non-purgeable) containing the icons for icon list.
//
// Ą  An 'LDEF' resource (preload, locked, non-purgeable) containing the custom list
//    definition function  used by the icon list.
//
// Ą  'hrct' and 'hwin' (purgeable) resources for balloon help.
//
// Ą  A 'SIZE' resource with the acceptSuspendResumeEvents, doesActivateOnFGSwitch, and
//    is32BitCompatible flags set.
//
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

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

#include <Appearance.h>
#include <ControlDefinitions.h>
#include <Devices.h>
#include <LowMem.h>
#include <Sound.h>
#include <ToolUtils.h>

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

#define rMenubar                 128
#define mApple                   128
#define  iAbout                  1
#define mFile                    129
#define  iQuit                   11
#define mDemonstration           131
#define  iHandMadeLists          1
#define  iListControlLists       2
#define rListsWindow             128
#define  cExtractButton          128
#define  cGroupBox1              129
#define  cGroupBox2              130
#define  cBalloonHelpStaticText  131
#define  cSoftwareStaticText     132
#define  cHardwareStaticText     133
#define  rTextListStrings        128
#define  rIconListIconSuiteBase  128
#define  rIconListStrings        129
#define rListsDialog             128
#define  iDateFormatList         5
#define  iWatermarkList          6
#define  iDateFormatStaticText   8
#define  iWatermarkStaticText    10
#define  rDateFormatStrings      130
#define  rWatermarkStrings       131
#define kUpArrow                 0x1e
#define kDownArrow               0x1f
#define kTab                     0x09
#define kScrollBarWidth          15
#define kMaxKeyThresh            120
#define kSystemLDEF              0
#define kCustomLDEF              128
#define MAXLONG                  0x7FFFFFFF

// ............................................................................. typedefs

typedef struct
{
  ListHandle     textListHdl;
  ListHandle     iconListHdl;
  ControlHandle  extractButtonHdl;
} docStructure, **docStructureHandle;

typedef struct
{
  RGBColor       backColour;
  PixPatHandle   backPixelPattern;
  Pattern        backBitPattern;
} backColourPattern;

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

void            main                         (void);
void            doInitManagers               (void);
void            doEvents                     (EventRecord *);
void            doAdjustMenus                (void);
void            doMenuChoice                 (SInt32);
void            doGetDepthAndDevice          (void);
void            doSaveBackground             (backColourPattern *);
void            doRestoreBackground          (backColourPattern *);
void            doSetBackgroundWhite         (void);
void            doOpenListsWindow            (void);
void            doKeyDown                    (SInt8,EventRecord *);
void            doUpdate                     (EventRecord *);
void            doActivate                   (EventRecord *);
void            doActivateWindow             (WindowPtr,Boolean);
void            doInContent                  (EventRecord *);
ListHandle      doCreateTextList             (DialogPtr,Rect,SInt16,SInt16);
void            doAddRowsAndDataToTextList   (ListHandle,SInt16,SInt16);
void            doAddTextItemAlphabetically  (ListHandle,Str255);
ListHandle      doCreateIconList             (DialogPtr,Rect,SInt16,SInt16);
void            doAddRowsAndDataToIconList   (ListHandle,SInt16);
void            doHandleArrowKey             (SInt8,EventRecord *,Boolean);
void            doArrowKeyMoveSelection      (ListHandle,SInt8,Boolean);
void            doArrowKeyExtendSelection    (ListHandle,SInt8,Boolean);
void            doTypeSelectSearch           (ListHandle,EventRecord *);
pascal SInt16   searchPartialMatch           (Ptr,Ptr,SInt16,SInt16);
Boolean         doFindFirstSelectedCell      (ListHandle,Cell *);
void            doFindLastSelectedCell       (ListHandle,Cell *);
void            doFindNewCellLoc             (ListHandle,Cell,Cell *,SInt8,Boolean);
void            doSelectOneCell              (ListHandle,Cell);
void            doMakeCellVisible            (ListHandle,Cell);
void            doResetTypeSelection         (void);
void            doRotateCurrentList          (void);
void            doDrawFrameAndFocus          (ListHandle,Boolean);
void            doExtractSelections          (void);
void            doDrawSelections             (void);
void            doListsDialog                (void);
pascal Boolean  eventFilter                  (DialogPtr,EventRecord *,SInt16 *);

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// Lists.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

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

#include "List.h"

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

ListSearchUPP      gSearchPartialMatchUPP;
SInt16             gPixelDepth;  
Boolean            gIsColourDevice  = false;
Boolean            gDone;
Boolean            gInBackground;
backColourPattern  gBackColourPattern;

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

void  main(void)
{
  Handle         menubarHdl;
  MenuHandle     menuHdl;
  EventRecord    eventStructure;

  // ................................................................ initialise managers

  doInitManagers();
  
  // .......................................................... create routine descriptor

  gSearchPartialMatchUPP = NewListSearchProc((ProcPtr) searchPartialMatch);

  // .......................................................... set up menu bar and menus
  
  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    ExitToShell();
  SetMenuBar(menubarHdl);
  DrawMenuBar();

  menuHdl = GetMenuHandle(mApple);
  if(menuHdl == NULL)
    ExitToShell();
  else
    AppendResMenu(menuHdl,'DRVR');

  // ......... get pixel depth and whether colour device for certain Appearance functions   
  
  doGetDepthAndDevice();

  // .................................................................... enter eventLoop
  
  gDone = false;
  
  while(!gDone)
  {
    if(WaitNextEvent(everyEvent,&eventStructure,MAXLONG,NULL))
      doEvents(&eventStructure);
  }
}

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

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

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

  InitCursor();
  FlushEvents(everyEvent,0);

  RegisterAppearanceClient();
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doEvents

void  doEvents(EventRecord *eventStrucPtr)
{
  WindowPtr            windowPtr;
  SInt16               partCode;
  docStructureHandle   docStrucHdl;
  SInt8                charCode;

  switch(eventStrucPtr->what)
  {
    case mouseDown:
      partCode = FindWindow(eventStrucPtr->where,&windowPtr);

      switch(partCode)
      {
        case inMenuBar:
          doAdjustMenus();
          doMenuChoice(MenuSelect(eventStrucPtr->where));
          break;

        case inContent:
          if(windowPtr != FrontWindow())
            SelectWindow(windowPtr);
          else
            doInContent(eventStrucPtr);
          break;
      
        case inDrag:
          DragWindow(windowPtr,eventStrucPtr->where,&qd.screenBits.bounds);
          break;
          
        case inGoAway:
          docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
          LDispose((*docStrucHdl)->textListHdl);
          LDispose((*docStrucHdl)->iconListHdl);
          DisposeHandle((Handle) docStrucHdl);
          DisposeWindow(windowPtr);
          break;
      }
      break;

    case keyDown:
    case autoKey:
      charCode = eventStrucPtr->message & charCodeMask;
      if((eventStrucPtr->modifiers & cmdKey) != 0)
      {
        doAdjustMenus();
        doMenuChoice(MenuEvent(eventStrucPtr));
      }
      if(FrontWindow())
        doKeyDown(charCode,eventStrucPtr);
      break;

    case updateEvt:
      doUpdate(eventStrucPtr);
      break;

    case activateEvt:
      doActivate(eventStrucPtr);
      break;

    case osEvt:
      switch((eventStrucPtr->message >> 24) & 0x000000FF)
      {
        case suspendResumeMessage:
          if(FrontWindow())
          {
            gInBackground = (eventStrucPtr->message & resumeFlag) == 0;
            doActivateWindow(FrontWindow(),!gInBackground);
          }
          break;
      }
      HiliteMenu(0);
      break;
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAdjustMenus

void  doAdjustMenus(void)
{
  MenuHandle  demoMenuHdl;

  demoMenuHdl = GetMenuHandle(mDemonstration);

  if(FrontWindow())
    DisableItem(demoMenuHdl,1);
  else
    EnableItem(demoMenuHdl,1);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMenuChoice

void  doMenuChoice(SInt32 menuChoice)
{
  SInt16  menuID, menuItem;
  Str255  itemName;
  SInt16  daDriverRefNum;

  menuID = HiWord(menuChoice);
  menuItem = LoWord(menuChoice);

  if(menuID == 0)
    return;

  switch(menuID)
  {
    case mApple:
      if(menuItem == iAbout)
        SysBeep(10);
      else
      {
        GetMenuItemText(GetMenuHandle(mApple),menuItem,itemName);
        daDriverRefNum = OpenDeskAcc(itemName);
      }
      break;

    case mFile:
      if(menuItem == iQuit)
        gDone = true;
      break;

    case mDemonstration:
      if(menuItem == iHandMadeLists)
        doOpenListsWindow();
      else if(menuItem == iListControlLists)
        doListsDialog();
      break;
  }

  HiliteMenu(0);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doGetDepthAndDevice

void doGetDepthAndDevice(void)
{
  GDHandle  deviceHdl;

  deviceHdl = LMGetMainDevice();
  gPixelDepth = (*(*deviceHdl)->gdPMap)->pixelSize;
  if(BitTst(&(*deviceHdl)->gdFlags,gdDevType))
    gIsColourDevice = true;
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSaveBackground

void  doSaveBackground(backColourPattern *gBackColourPattern)
{
  GrafPtr  currentPort;
  
  GetPort(¤tPort);

  GetBackColor(&gBackColourPattern->backColour);
  gBackColourPattern->backPixelPattern  = NULL;

  if((**((CGrafPtr) currentPort)->bkPixPat).patType != 0)
    gBackColourPattern->backPixelPattern = ((CGrafPtr) currentPort)->bkPixPat;
  else
    gBackColourPattern->backBitPattern = 
                             *(PatPtr) (*(**((CGrafPtr) currentPort)->bkPixPat).patData);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doRestoreBackground

void  doRestoreBackground(backColourPattern *gBackColourPattern)
{
  RGBBackColor(&gBackColourPattern->backColour);

  if(gBackColourPattern->backPixelPattern)
    BackPixPat(gBackColourPattern->backPixelPattern);
  else
    BackPat(&gBackColourPattern->backBitPattern);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSetBackgroundWhite

void  doSetBackgroundWhite(void)
{
  RGBColor  whiteColour = { 0xFFFF, 0xFFFF, 0xFFFF };

  RGBBackColor(&whiteColour);
  BackPat(&qd.white);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// WindowList.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

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

#include "List.h"

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

ListHandle  gCurrentListHdl;
Str255      gTSString;  
SInt16      gTSResetThreshold;  
SInt32      gTSLastKeyTime;
ListHandle  gTSLastListHit;  
Str255      gStringArray[16];

extern ListSearchUPP      gSearchPartialMatchUPP;
extern SInt16             gPixelDepth;  
extern Boolean            gIsColourDevice;
extern backColourPattern  gBackColourPattern;

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doOpenListsWindow

void  doOpenListsWindow(void)
{  
  WindowPtr           windowPtr;
  docStructureHandle  docStrucHdl;
  ControlHandle       controlHdl;
  Str255              software = "\pSoftware:";
  Str255              hardware = "\pHardware:";
  Str255              balloon  = "\pBalloon help is available";
  SInt16              fontNum;
  Rect                textListRect, pictListRect;
  ListHandle          textListHdl, iconListHdl;

  // ..... open window, attach document structure, set and save background colour/pattern

  if(!(windowPtr = GetNewCWindow(rListsWindow,NULL,(WindowPtr) -1)))
      ExitToShell();

  if(!(docStrucHdl = (docStructureHandle) NewHandle(sizeof(docStructure))))
    ExitToShell();
  SetWRefCon(windowPtr,(SInt32) docStrucHdl);

  SetPort(windowPtr);

  SetThemeWindowBackground(windowPtr,kThemeBrushDialogBackgroundActive,true);
  doSaveBackground(&gBackColourPattern);

  // ........................................................... create window's controls

  CreateRootControl(windowPtr,&controlHdl);

  if(!((*docStrucHdl)->extractButtonHdl = GetNewControl(cExtractButton,windowPtr)))
    ExitToShell();

  if(!(controlHdl = GetNewControl(cGroupBox1,windowPtr)))
    ExitToShell();
  if(!(controlHdl = GetNewControl(cBalloonHelpStaticText,windowPtr)))
    ExitToShell();
  SetControlData(controlHdl,kControlNoPart,kControlStaticTextTextTag,balloon[0],
                 (Ptr) &balloon[1]);

  if(!(controlHdl = GetNewControl(cSoftwareStaticText,windowPtr)))
    ExitToShell();
  SetControlData(controlHdl,kControlNoPart,kControlStaticTextTextTag,software[0],
                 (Ptr) &software[1]);

  if(!(controlHdl = GetNewControl(cHardwareStaticText,windowPtr)))
    ExitToShell();
  SetControlData(controlHdl,kControlNoPart,kControlStaticTextTextTag,hardware[0],
                 (Ptr) &hardware[1]);

  if(!(controlHdl = GetNewControl(cGroupBox2,windowPtr)))
    ExitToShell();

  // .................................................................. set window's font
  
  GetFNum("\pGeneva",&fontNum);
  TextFont(fontNum);
  TextSize(10);

  // .......................... create lists, assign handles to document structure fields

  SetRect(&textListRect,20,32,150,136);  
  SetRect(&pictListRect,171,32,238,136);  

  textListHdl = doCreateTextList(windowPtr,textListRect,1,kSystemLDEF);
  iconListHdl = doCreateIconList(windowPtr,pictListRect,1,kCustomLDEF);

  (*docStrucHdl)->textListHdl = textListHdl;
  (*docStrucHdl)->iconListHdl  = iconListHdl;

  // ..................... assign handles to list structure refCon fields for linked ring

  (*textListHdl)->refCon = (SInt32) iconListHdl;
  (*iconListHdl)->refCon = (SInt32) textListHdl;

  // .................................................... make text list the current list

  gCurrentListHdl = textListHdl;

  // ........................................................................ show window

  ShowWindow(windowPtr);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doKeyDown

void  doKeyDown(SInt8 charCode,EventRecord *eventStrucPtr)
{
  docStructureHandle   docStrucHdl;
  Boolean              allowExtendSelect;

  docStrucHdl = (docStructureHandle) GetWRefCon(FrontWindow());

  if(charCode == kTab)
  {
    doRotateCurrentList();
  } 
  else if(charCode == kUpArrow || charCode == kDownArrow)
  {
    if(gCurrentListHdl == (*docStrucHdl)->textListHdl)
      allowExtendSelect = true;
    else
      allowExtendSelect = false;
      
    doHandleArrowKey(charCode,eventStrucPtr,allowExtendSelect);
  }
  else
  {
    if(gCurrentListHdl == (*docStrucHdl)->textListHdl)
      doTypeSelectSearch((*docStrucHdl)->textListHdl,eventStrucPtr);
  }
}

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

void  doUpdate(EventRecord *eventStrucPtr)
{
  WindowPtr            windowPtr;
  Rect                 theRect;
  docStructureHandle   docStrucHdl;
  ListHandle           textListHdl, iconListHdl;
  
  windowPtr = (WindowPtr) eventStrucPtr->message;
  SetPort(windowPtr);

  BeginUpdate(windowPtr);

  doRestoreBackground(&gBackColourPattern);
  SetRect(&theRect,20,188,237,381);
  EraseRect(&theRect);
  UpdateControls(windowPtr,windowPtr->visRgn);
  doSetBackgroundWhite();

  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
  textListHdl = (*docStrucHdl)->textListHdl;
  iconListHdl = (*docStrucHdl)->iconListHdl;

  LUpdate(windowPtr->visRgn,textListHdl);
  LUpdate(windowPtr->visRgn,iconListHdl);

  doDrawFrameAndFocus(textListHdl,((WindowPeek) windowPtr)->hilited);
  doDrawFrameAndFocus(iconListHdl,((WindowPeek) windowPtr)->hilited);

  doDrawSelections();

  EndUpdate(windowPtr);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doActivate

void  doActivate(EventRecord *eventStrucPtr)
{
  WindowPtr  windowPtr;
  Boolean    becomingActive;

  windowPtr = (WindowPtr) eventStrucPtr->message;
  becomingActive = ((eventStrucPtr->modifiers & activeFlag) == activeFlag);
  doActivateWindow(windowPtr,becomingActive);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doActivateWindow

void  doActivateWindow(WindowPtr windowPtr,Boolean becomingActive)
{
  ControlHandle        controlHdl;
  docStructureHandle   docStrucHdl;
  ListHandle           textListHdl, iconListHdl;

  GetRootControl(windowPtr,&controlHdl);
      
  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
  textListHdl = (*docStrucHdl)->textListHdl;
  iconListHdl = (*docStrucHdl)->iconListHdl;
  
  if(becomingActive)
  {
    LActivate(true,textListHdl);
    LActivate(true,iconListHdl);
    
    SetThemeTextColor(kThemeTextColorListView,gPixelDepth,gIsColourDevice);
    LUpdate(windowPtr->visRgn,textListHdl);
    LUpdate(windowPtr->visRgn,iconListHdl);

    doDrawFrameAndFocus(textListHdl,true);
    doDrawFrameAndFocus(iconListHdl,true);

    doResetTypeSelection();

    doRestoreBackground(&gBackColourPattern);
    ActivateControl(controlHdl);
    doSetBackgroundWhite();

    doDrawSelections();
  }
  else
  {
    LActivate(false,textListHdl);
    LActivate(false,iconListHdl);
    
    SetThemeTextColor(kThemeTextColorDialogInactive,gPixelDepth,gIsColourDevice);
    LUpdate(windowPtr->visRgn,textListHdl);
    LUpdate(windowPtr->visRgn,iconListHdl);

    doDrawFrameAndFocus(textListHdl,false);
    doDrawFrameAndFocus(iconListHdl,false);

    doRestoreBackground(&gBackColourPattern);
    DeactivateControl(controlHdl);
    doSetBackgroundWhite();

    doDrawSelections();
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doInContent

void  doInContent(EventRecord *eventStrucPtr)
{
  WindowPtr            windowPtr;
  docStructureHandle   docStrucHdl;
  ListHandle           textListHdl, iconListHdl;
  Rect                 textListRect, pictListRect;
  Point                mouseXY;
  ControlHandle        controlHdl;
  Boolean              isDoubleClick;
  
  windowPtr = FrontWindow();
  docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
  textListHdl = (*docStrucHdl)->textListHdl;
  iconListHdl = (*docStrucHdl)->iconListHdl;

  textListRect = (*(*docStrucHdl)->textListHdl)->rView;
  pictListRect = (*(*docStrucHdl)->iconListHdl)->rView;
  textListRect.right += kScrollBarWidth;  
  pictListRect.right += kScrollBarWidth;  

  SetPort(windowPtr);
  mouseXY = eventStrucPtr->where;
  GlobalToLocal(&mouseXY);

  if(PtInRect(mouseXY,&textListRect) || PtInRect(mouseXY,&pictListRect))
  {
    if((PtInRect(mouseXY,&textListRect) && gCurrentListHdl != textListHdl) ||
       (PtInRect(mouseXY,&pictListRect) && gCurrentListHdl != iconListHdl))
    {
      doRotateCurrentList();
    }
  
    isDoubleClick = LClick(mouseXY,eventStrucPtr->modifiers,gCurrentListHdl);
    if(isDoubleClick)
      doExtractSelections();
  }
  else if(FindControl(mouseXY,windowPtr,&controlHdl))
  {
    if(TrackControl(controlHdl,mouseXY,NULL))
    {
      if(controlHdl == (*docStrucHdl)->extractButtonHdl)
        doExtractSelections();
    }
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doCreateTextList

ListHandle  doCreateTextList(WindowPtr windowPtr,Rect listRect,SInt16 numCols,
                             SInt16 lDef)
{
  Rect         dataBounds;
  Point        cellSize;
  ListHandle   textListHdl;
  Cell         theCell;

  SetRect(&dataBounds,0,0,numCols,0);
  SetPt(&cellSize,0,0);

  listRect.right = listRect.right - kScrollBarWidth;

  textListHdl = LNew(&listRect,&dataBounds,cellSize,lDef,windowPtr,true,false,false,true);

  doAddRowsAndDataToTextList(textListHdl,rTextListStrings,15);

  SetPt(&theCell,0,0);
  LSetSelect(true,theCell,textListHdl);

  doResetTypeSelection();

  return(textListHdl);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAddRowsAndDataToTextList

void  doAddRowsAndDataToTextList(ListHandle textListHdl,SInt16 stringListID,
                                 SInt16 numberOfStrings)
{
  SInt16  stringIndex;
  Str255  theString;

  for(stringIndex = 1;stringIndex < numberOfStrings + 1;stringIndex++) 
  {
    GetIndString(theString,stringListID,stringIndex);
    doAddTextItemAlphabetically(textListHdl,theString);  
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAddTextItemAlphabetically

void  doAddTextItemAlphabetically(ListHandle listHdl,Str255 theString)
{
  Boolean  found;
  SInt16   totalRows, currentRow, cellDataOffset, cellDataLength;
  Cell     aCell;

  found = false;

  totalRows = (*listHdl)->dataBounds.bottom - (*listHdl)->dataBounds.top;
  currentRow = -1;

  while(!found)
  {
    currentRow += 1;
    if(currentRow == totalRows)
      found = true;
    else
    {
      SetPt(&aCell,0,currentRow);
      LGetCellDataLocation(&cellDataOffset,&cellDataLength,aCell,listHdl);

      MoveHHi((Handle) (*listHdl)->cells);
      HLock((Handle) (*listHdl)->cells);

      if(CompareText((Ptr) theString + 1,((Ptr) (*listHdl)->cells[0] + cellDataOffset),
                      StrLength(theString),cellDataLength,NULL) == -1)
      {
        found = true;
      }

      HUnlock((Handle)(*listHdl)->cells);
    }
  }

  currentRow = LAddRow(1,currentRow,listHdl);
  SetPt(&aCell,0,currentRow);

  LSetCell((Ptr) theString + 1,(SInt16) StrLength(theString),aCell,listHdl);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doCreateIconList

ListHandle  doCreateIconList(WindowPtr windowPtr,Rect listRect,SInt16 numCols,SInt16 lDef)
{
  Rect         dataBounds;
  Point        cellSize;
  ListHandle   iconListHdl;
  Cell         theCell;
    
  SetRect(&dataBounds,0,0,numCols,0);
  SetPt(&cellSize,52,52);

  listRect.right = listRect.right - kScrollBarWidth;

  iconListHdl = LNew(&listRect,&dataBounds,cellSize,lDef,windowPtr,true,false,false,true);

  (*iconListHdl)->selFlags = lOnlyOne;

  doAddRowsAndDataToIconList(iconListHdl,rIconListIconSuiteBase);

  SetPt(&theCell,0,0);
  LSetSelect(true,theCell,iconListHdl);

  return(iconListHdl);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAddRowsAndDataToIconList

void  doAddRowsAndDataToIconList(ListHandle iconListHdl,SInt16 iconSuiteBase)
{
  SInt16    rowNumber, suiteIndex, index = 0;
  Handle    iconSuiteHdl;
  Cell      theCell;

  rowNumber = (*iconListHdl)->dataBounds.bottom;

  for(suiteIndex = iconSuiteBase;suiteIndex < (iconSuiteBase + 8);suiteIndex++)
  {  
    GetIconSuite(&iconSuiteHdl,suiteIndex,kSelectorAllLargeData);

    rowNumber = LAddRow(1,rowNumber,iconListHdl);
    SetPt(&theCell,0,rowNumber);
    LSetCell(&iconSuiteHdl,sizeof(iconSuiteHdl),theCell,iconListHdl);
  
    rowNumber += 1;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doHandleArrowKey

void  doHandleArrowKey(SInt8 charCode,EventRecord *eventStrucPtr,Boolean allowExtendSelect)
{
  Boolean  moveToTopBottom = false;

  if(eventStrucPtr->modifiers & cmdKey)
    moveToTopBottom = true;

  if(allowExtendSelect && (eventStrucPtr->modifiers & shiftKey))
    doArrowKeyExtendSelection(gCurrentListHdl,charCode,moveToTopBottom);
  else
    doArrowKeyMoveSelection(gCurrentListHdl,charCode,moveToTopBottom);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doArrowKeyMoveSelection

void  doArrowKeyMoveSelection(ListHandle listHdl,SInt8 charCode,Boolean moveToTopBottom)
{
  Cell  currentSelection, newSelection;

  if(doFindFirstSelectedCell(listHdl,¤tSelection))
  {
    if(charCode == kDownArrow)
      doFindLastSelectedCell(listHdl,¤tSelection);

    doFindNewCellLoc(listHdl,currentSelection,&newSelection,charCode,moveToTopBottom);

    doSelectOneCell(listHdl,newSelection);
    doMakeCellVisible(listHdl,newSelection);
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doArrowKeyExtendSelection

void  doArrowKeyExtendSelection(ListHandle listHdl,SInt8 charCode,Boolean moveToTopBottom)
{
  Cell  currentSelection, newSelection;

  if(doFindFirstSelectedCell(listHdl,¤tSelection))
  {
    if(charCode == kDownArrow)
      doFindLastSelectedCell(listHdl,¤tSelection);

    doFindNewCellLoc(listHdl,currentSelection,&newSelection,charCode,moveToTopBottom);

    if(!(LGetSelect(false,&newSelection,listHdl)))
      LSetSelect(true,newSelection,listHdl);

    doMakeCellVisible(listHdl,newSelection);
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doTypeSelectSearch

void  doTypeSelectSearch(ListHandle listHdl,EventRecord *eventStrucPtr)
{
  SInt8   newChar;
  Cell    theCell;

  newChar = eventStrucPtr->message & charCodeMask;

  if((gTSLastListHit != listHdl) || ((eventStrucPtr->when - gTSLastKeyTime) >= 
     gTSResetThreshold) || (StrLength(gTSString) == 255))
    doResetTypeSelection();

  gTSLastListHit = listHdl;
  gTSLastKeyTime = eventStrucPtr->when;

  gTSString[0] = (SInt8) (StrLength(gTSString) + 1);
  gTSString[StrLength(gTSString)] = newChar;

  SetPt(&theCell,0,0);

  if(LSearch((Ptr) gTSString+1,StrLength(gTSString),gSearchPartialMatchUPP,&theCell,
             listHdl))  
  {
    LSetSelect(true,theCell,listHdl);
    doSelectOneCell(listHdl,theCell);
    doMakeCellVisible(listHdl,theCell);
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× searchPartialMatch

pascal SInt16  searchPartialMatch(Ptr searchDataPtr,Ptr cellDataPtr,SInt16 cellDataLen,
                                    SInt16 searchDataLen)
{
  SInt16  result;

  if((cellDataLen > 0) && (cellDataLen >= searchDataLen))
    result = IdenticalText(cellDataPtr,searchDataPtr,searchDataLen,searchDataLen,NULL);
  else
    result = 1;

  return(result);
}  

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doFindFirstSelectedCell

Boolean  doFindFirstSelectedCell(ListHandle listHdl,Cell *theCell)
{
  Boolean  result;

  SetPt(theCell,0,0);
  result = LGetSelect(true,theCell,listHdl);

  return(result);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doFindLastSelectedCell

void  doFindLastSelectedCell(ListHandle listHdl,Cell *theCell)
{
  Cell     aCell;
  Boolean  moreCellsInList;

  if(doFindFirstSelectedCell(listHdl,&aCell))
  {
    while(LGetSelect(true,&aCell,listHdl))
    {
      *theCell = aCell;
      moreCellsInList = LNextCell(true,true,&aCell,listHdl);
    } 
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doFindNewCellLoc

void  doFindNewCellLoc(ListHandle listHdl,Cell oldCellLoc,Cell *newCellLoc,SInt8 charCode,
                       Boolean moveToTopBottom)
{
  SInt16  listRows;

  listRows = (*listHdl)->dataBounds.bottom - (*listHdl)->dataBounds.top;
  *newCellLoc = oldCellLoc;

  if(moveToTopBottom)
  {
    if(charCode == kUpArrow)
      (*newCellLoc).v = 0;
    else if(charCode == kDownArrow)
      (*newCellLoc).v = listRows - 1;
  }
  else
  {
    if(charCode ==  kUpArrow)
    {
      if(oldCellLoc.v != 0)
        (*newCellLoc).v = oldCellLoc.v - 1;
    }  
    else if(charCode == kDownArrow)
    {
      if(oldCellLoc.v != listRows - 1)
        (*newCellLoc).v = oldCellLoc.v + 1;
    }
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doSelectOneCell

void  doSelectOneCell(ListHandle listHdl,Cell theCell) 
{
  Cell     nextSelectedCell;
  Boolean  moreCellsInList;

  if(doFindFirstSelectedCell(listHdl,&nextSelectedCell))
  {
    while(LGetSelect(true,&nextSelectedCell,listHdl))
    {
      if(nextSelectedCell.v != theCell.v)
        LSetSelect(false,nextSelectedCell,listHdl);
      else
        moreCellsInList = LNextCell(true,true,&nextSelectedCell,listHdl);
    } 

    LSetSelect(true,theCell,listHdl);
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMakeCellVisible

void  doMakeCellVisible(ListHandle listHdl,Cell newSelection)
{
  Rect    visibleRect;
  SInt16  dRows;

  visibleRect = (*listHdl)->visible;

  if(!(PtInRect(newSelection,&visibleRect)))
  {
    if(newSelection.v > visibleRect.bottom - 1)
      dRows = newSelection.v - visibleRect.bottom + 1;
    else if(newSelection.v < visibleRect.top)
      dRows = newSelection.v - visibleRect.top;

    LScroll(0,dRows,listHdl);
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doResetTypeSelection

void  doResetTypeSelection(void)
{  
  gTSString[0] = 0;
  gTSLastListHit = NULL;
  gTSLastKeyTime = 0;
  gTSResetThreshold = 2 * LMGetKeyThresh();  
  if(gTSResetThreshold > kMaxKeyThresh)
    gTSResetThreshold = kMaxKeyThresh;  
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doRotateCurrentList

void  doRotateCurrentList(void)
{
  ListHandle  oldListHdl, newListHdl;

  oldListHdl = gCurrentListHdl;
  newListHdl = (ListHandle) (*gCurrentListHdl)->refCon;
  gCurrentListHdl = newListHdl;

  doDrawFrameAndFocus(oldListHdl,true);
  doDrawFrameAndFocus(newListHdl,true);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDrawFrameAndFocus

void  doDrawFrameAndFocus(ListHandle listHdl,Boolean inState)
{
  Rect  borderRect;

  doRestoreBackground(&gBackColourPattern);

  borderRect = (*listHdl)->rView;
  borderRect.right += kScrollBarWidth;

  DrawThemeFocusRect(&borderRect,false);

  if(inState)
    DrawThemeListBoxFrame(&borderRect,kThemeStateActive);
  else
    DrawThemeListBoxFrame(&borderRect,kThemeStateDisabled);

  if(listHdl == gCurrentListHdl)
    DrawThemeFocusRect(&borderRect,inState);

  doSetBackgroundWhite();
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doExtractSelections

void  doExtractSelections(void)
{
  docStructureHandle  docStrucHdl;
  ListHandle          textListHdl, iconListHdl;
  SInt16              a, cellIndex, offset, dataLen;
  Cell                theCell;
  Rect                theRect;
  
  docStrucHdl = (docStructureHandle) GetWRefCon(FrontWindow());
  textListHdl = (*docStrucHdl)->textListHdl;
  iconListHdl = (*docStrucHdl)->iconListHdl;
  
  for(a=0;a<16;a++)
    gStringArray[a][0] = 0;
  
  for(cellIndex=0;cellIndex < (*textListHdl)->dataBounds.bottom;cellIndex++)
  {
    SetPt(&theCell,0,cellIndex);
    if(LGetSelect(false,&theCell,textListHdl))
    {
      LGetCellDataLocation(&offset,&dataLen,theCell,textListHdl);
      LGetCell((Ptr) gStringArray[cellIndex] + 1,&dataLen,theCell,textListHdl);
      gStringArray[cellIndex][0] = (SInt8) dataLen;
    }
  }

  SetPt(&theCell,0,0);
  LGetSelect(true,&theCell,iconListHdl);
  GetIndString(gStringArray[15],rIconListStrings,theCell.v + 1);

  SetRect(&theRect,20,188,237,381);
  InvalRect(&theRect);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDrawSelections

void  doDrawSelections(void)
{
  SInt16  a, nextLine = 193;

  for(a=0;a<15;a++)
  {
    if(gStringArray[a][0] != 0)
    {
      MoveTo(36,nextLine += 12);
      DrawString(gStringArray[a]);
    }
  }

  MoveTo(170,205);
  DrawString(gStringArray[15]);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// DialogLists.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

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

#include "List.h"

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doListsDialog

void  doListsDialog(void)
{
  DialogPtr        dialogPtr;
  GrafPtr          oldPort;
  ModalFilterUPP   eventFilterUPP;
  ControlHandle    dateFormatControlHdl, watermarkControlHdl, controlHdl;
  ListHandle       dateFormatListHdl, watermarkListHdl;
  SInt16           itemHit;
  Cell             theCell;
  SInt16           dataLen, offset;
  Str255           dateFormatString, watermarkString;
  Boolean          wasDoubleClick = false;

  // ..................... explicitly deactivate front window if it exists, create dialog

  if(FrontWindow())
    doActivateWindow(FrontWindow(),false);

  if(!(dialogPtr = GetNewDialog(rListsDialog,NULL,(WindowPtr) -1)))
    ExitToShell();

  GetPort(&oldPort);
  SetPort(dialogPtr);
  
  // ................................................ set default and cancel push buttons

  SetDialogDefaultItem(dialogPtr,kStdOkItemIndex);
  SetDialogCancelItem(dialogPtr,kStdCancelItemIndex);

  // ................................ create routine descriptor for event filter function

  eventFilterUPP = NewModalFilterProc((ProcPtr) eventFilter);

  // ...... add rows to lists, store data in their cells, modify cell selection algorithm

  GetDialogItemAsControl(dialogPtr,iDateFormatList,&dateFormatControlHdl);
  GetControlData(dateFormatControlHdl,kControlNoPart,kControlListBoxListHandleTag,
                 sizeof(dateFormatListHdl),(Ptr) &dateFormatListHdl,NULL);
                 
  doAddRowsAndDataToTextList(dateFormatListHdl,rDateFormatStrings,17);

  (*dateFormatListHdl)->selFlags = lOnlyOne;

  SetPt(&theCell,0,0);
  LSetSelect(true,theCell,dateFormatListHdl);
               
  GetDialogItemAsControl(dialogPtr,iWatermarkList,&watermarkControlHdl);
  GetControlData(watermarkControlHdl,kControlNoPart,kControlListBoxListHandleTag,
                 sizeof(watermarkListHdl),(Ptr) &watermarkListHdl,NULL);

  doAddRowsAndDataToTextList(watermarkListHdl,rWatermarkStrings,12);

  (*watermarkListHdl)->selFlags = lOnlyOne;

  SetPt(&theCell,0,0);
  LSetSelect(true,theCell,watermarkListHdl);

  // ................................................. show dialog and set keyboard focus

  ShowWindow(dialogPtr);

  SetKeyboardFocus(dialogPtr,watermarkControlHdl,1);
  SetKeyboardFocus(dialogPtr,dateFormatControlHdl,1);

  // ............................................................. enter ModalDialog loop

  do
  {
    ModalDialog(eventFilterUPP,&itemHit);

    if(itemHit == iDateFormatList)
    {
      SetPt(&theCell,0,0);
      LGetSelect(true,&theCell,dateFormatListHdl);
      LGetCellDataLocation(&offset,&dataLen,theCell,dateFormatListHdl);
      LGetCell((Ptr) dateFormatString + 1,&dataLen,theCell,dateFormatListHdl);
      dateFormatString[0] = (SInt8) dataLen;
  
      GetDialogItemAsControl(dialogPtr,iDateFormatStaticText,&controlHdl);
      SetControlData(controlHdl,kControlNoPart,kControlStaticTextTextTag,
                     dateFormatString[0],(Ptr) &dateFormatString[1]);
      Draw1Control(controlHdl);
  
      GetControlData(dateFormatControlHdl,kControlNoPart,kControlListBoxDoubleClickTag,
                     sizeof(wasDoubleClick),(Ptr) &wasDoubleClick,NULL);
    }
    else if(itemHit == iWatermarkList)
    {
      SetPt(&theCell,0,0);
      LGetSelect(true,&theCell,watermarkListHdl);
      LGetCellDataLocation(&offset,&dataLen,theCell,watermarkListHdl);
      LGetCell((Ptr) watermarkString + 1,&dataLen,theCell,watermarkListHdl);
      watermarkString[0] = (SInt8) dataLen;

      GetDialogItemAsControl(dialogPtr,iWatermarkStaticText,&controlHdl);
      SetControlData(controlHdl,kControlNoPart,kControlStaticTextTextTag,
                     watermarkString[0],(Ptr) &watermarkString[1]);
      Draw1Control(controlHdl);
      
      GetControlData(watermarkControlHdl,kControlNoPart,kControlListBoxDoubleClickTag,
                     sizeof(wasDoubleClick),(Ptr) &wasDoubleClick,NULL);
    }


  } while(itemHit != kStdOkItemIndex && itemHit != kStdCancelItemIndex
          && wasDoubleClick == false);

  // ........................................................................... clean up

  DisposeDialog(dialogPtr);
  DisposeRoutineDescriptor(eventFilterUPP);

  SetPort(oldPort);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× eventFilter

pascal Boolean  eventFilter(DialogPtr dialogPtr,EventRecord *eventStrucPtr,
                            SInt16 *itemHit)
{
  Boolean        handledEvent;
  GrafPtr        oldPort;
  SInt8          charCode;
  ControlHandle  controlHdl, focusControlHdl;
  ListHandle     watermarkListHdl, focusListHdl;
  Rect           listRect;
  
  handledEvent = false;
  
  if((eventStrucPtr->what == updateEvt) && 
     ((WindowPtr) eventStrucPtr->message != dialogPtr))
  {
    doUpdate(eventStrucPtr);
  }
  else if((eventStrucPtr->what == updateEvt) && 
          ((WindowPtr) eventStrucPtr->message == dialogPtr))
  {
    if(((WindowPeek) dialogPtr)->hilited)
    {
      GetPort(&oldPort);
      SetPort(dialogPtr);
    
      GetKeyboardFocus(dialogPtr,&focusControlHdl);
      GetControlData(focusControlHdl,kControlNoPart,kControlListBoxListHandleTag,
                     sizeof(focusListHdl),(Ptr) &focusListHdl,NULL);
      listRect = (*focusListHdl)->rView;
      listRect.right += kScrollBarWidth;
      DrawThemeFocusRect(&listRect,true);

      SetPort(oldPort);
    }
  }
  else
  {
    GetPort(&oldPort);
    SetPort(dialogPtr);

    if(eventStrucPtr->what == keyDown)
    {
      charCode = eventStrucPtr->message & charCodeMask;
      
      if(charCode != kUpArrow && charCode != kDownArrow && charCode != kTab)
      {
        GetDialogItemAsControl(dialogPtr,iWatermarkList,&controlHdl);
        GetControlData(controlHdl,kControlNoPart,kControlListBoxListHandleTag,
                   sizeof(watermarkListHdl),(Ptr) &watermarkListHdl,NULL);
        GetKeyboardFocus(dialogPtr,&focusControlHdl);
        if(controlHdl == focusControlHdl)
        {
          doTypeSelectSearch(watermarkListHdl,eventStrucPtr);
          Draw1Control(controlHdl);
        }
        
        handledEvent = true;
      }
    }
    else
    {
      handledEvent = StdFilterProc(dialogPtr,eventStrucPtr,itemHit);
    }
    
    SetPort(oldPort);
  }

  return(handledEvent);
}

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

Demonstration Program Comments

When this program is run, the user should open the window and movable modal dialog box by
choosing the relevant items in the Demonstration menu.  The user should manipulate the
lists in the window and dialog box, noting their behaviour in the following
circumstances:

*   Changing the active list (that is, the current target of mouse and keyboard activity)
    by clicking in the non-active list and by using the Tab key to cycle between the two
    lists.

*   Scrolling the active list using the vertical scroll bars, including dragging the
    scroll box and clicking in the scroll arrows and gray areas.

*   Clicking, and clicking and dragging, in the active list so as to select a particular
    cell, including dragging the cursor above and below the list to automatically scroll
    the list to the desired cell.

*   Pressing the Up-Arrow and Down-Arrow keys, noting that this action changes the
    selected cell and, where necessary, scrolls the list to make the newly-selected cell
    visible.

*   In the lists in the window:

    *   Double-clicking on a cell in the active list.

    *   Pressing the Command-key as well as the Up-Arrow and Down-Arrow keys, noting
        that, in both the text list and the picture list, this results in the top-most
        or bottom-most cell being selected.

*   In the text list in the window:

    *   Shift-clicking and dragging in the list to make contiguous multiple cell
        selections.

    *   Command-clicking and dragging in the list to make discontiguous multiple cell
        selections, noting the differing effects depending on whether the cell
        initially clicked is selected or not selected.

    *   Shift-clicking outside a block of multiple cell selections, including  between
        two fairly widely separated discontiguous selected cells.

    *   Pressing the Shift-key as well as the Up-Arrow and Down-Arrow keys, noting that
        this results in multiple cell selections.

*   When the text list in the window, or the right hand list in the dialog, is the
    active list, typing the text of a particular cell so as to select that cell by
    type selection, noting the effects of any excessive delay between keystrokes.

The user should also send the program to the background and bring it to the foreground
again, noting the list deactivation/activation effects.

List.h

#define

rListsWindow represents the resource ID of the window's 'WIND' resource.  The following
six constants represent the resource IDs of the window's controls.  The next three
constants represent the resource IDs of 'STR#' resource containing the strings for the
window's text list, the icon suite for the icon list, and the 'STR#' resource containing
the strings for the icon titles.

rListsDialog represents the resource ID of the dialog's 'DLOG', 'dlgx', and 'dftb'
resources.  The following four constants represent the item numbers of items in the
dialog's 'DITL' resource.  The next two constants represent the resource IDs of the
'STR#' resources containing the strings for the dialog's lists.

The next three constants represent the character codes returned by the Up Arrow, Down
Arrow, and Tab keys.  kScrollBarWidth represents the width of the lists' vertical scroll
bars.  kMaxKeyThresh is used in the type selection function.  kSystemLDEF and kCustomLDEF
represent the resource IDs of the default and custom list definition functions.

#typedef

A variables of type docStructure which will be used to store the handles to the two list
structures for the window and the handle to the window's push button.  The handle to this
structure will be assigned to the refCon field of the dialog box's window structure.

The backColourPattern data type will be used to save and restore the background colour
and pattern.

Lists.c

Lists.c is simply the basic "engine" which supports the demonstration.  There is little
in this file which has not featured in previous demonstration programs.

main

A routine descriptor is created for the match function used by LSearch in the
application's type selection function.

doEvents

In the inGoAway case in the mouseDown case, a handle to the window's document structure
is retrieved so as to be able to pass the handles to the window's two list structures in
the two calls to LDispose.  LDispose disposes of all memory associated with the specified
list.  DisposeHandle then disposes of the window's document structure and DisposeWindow
removes the window from the screen, removes it from the window list and discards all its
data storage.

doSaveBackground, doRestoreBackground, and doSetBackgroundWhite

doSaveBackground and doRestoreBackground are essentially the same as the functions
introduced at Chapter 12 - Drawing With QuickDraw for saving and restoring the drawing
environment.  In this case, the saving and restoring is limited to the background colour
and the background bit or pixel pattern.  doSetBackgroundWhite sets the background colour
to white and the background pattern to the pattern white.

WindowList.c

WindowList.c contains the functions pertaining to the lists in the window.

Global Variables

gCurrentListHandle will be assigned the handle to the list structure associated with the
currently active list in the window.  The next four global variables are associated with
the type selection functions.  gStringArray will be assigned strings representing the
selections from the lists.

doOpenListsWindow

doOpenListsWindow creates the window and its controls, and calls the application-defined
functions which creates the two lists for the window.

The call to GetNewCWindow creates the window.  The call to NewHandle creates a block for
the window's document structure.  SetWRefcon attaches the document structure to the
window.  SetThemeWindowBackground is called to set the window's background colour/pattern
and doSaveBackground is called to save this background colour pattern for later use.

CreateRootControl creates a root control for the window so as to simplify the task of
activating and deactivating the window's controls.  In the rest of this block, the
window's push button, group box, and static text field controls are created, and the
latter's text is set.

At the next block, the window's font and font size is set to the small system font so
that the text in the lists will appear in this font.

The lists are then created.  First, the rectangles in which the lists are to be displayed
are defined.  These are then passed in the calls to the application-defined functions
which create the lists.  The handles to the list structures returned by these functions
are then assigned to the relevant fields of the window's document structure.

The next block assigns the icon list's handle to the refCon field of the text list's list
structure and the text list's handle to the refCon field of the picture list's list
structure.  This establishes the "linked ring" which will be used to facilitate the
rotation of the active list via Tab key presses.

The penultimate line establishes the text list as the currently active list.  ShowWindow
is then called to display the window.

doKeyDown

The first line gets the handle to the document structure.

If the key pressed was the Tab key, an application-defined function is called to change
the currently active list.

If the key pressed was either the Up Arrow or the Down Arrow key, and if the current list
is the text list, a variable which specifies whether multiple cell selections via the
keyboard are permitted is set to true.  If the current list is the icon list, this
variable is set to false.  This variable is then passed as a parameter in a call to an
application-defined function which further processes the Arrow key event (Line 321).

If the key pressed was neither the Tab key, the Up Arrow key, or the Down Arrow key, and
if the active list is the text list, the event is passed to an application-defined type
selection function for further processing.

doUpdate

doUpdate handles update events.  Between the usual calls to BeginUpdate and 
EndUpdate, the rectangle in which the current list selections are drawn is erased, the 
list are updated (that is, redrawn), an application-defined function is called to draw 
the focus rectangles in the appropriate state, and an application-defined function is 
called to draw the current list selections in the rectangle at the bottom of the 
window.

doActivateWindow

doActivateWindow activates and deactivates the content area of the window.

GetRootControl gets the handle to the window's root control.  The next three lines get
the handles to the list structures.

If the window is becoming active, the following occurs.  For both lists, LActivate is
called with true passed in the first parameter so as to highlight the currently selected
cells.  The calls to LUpdate are necessary for Appearance purposes.  Immediately prior to
these calls, the window's text colour is set to that for list views.  LUpdate causes a
redraw of all of the list's text in that colour. The calls to doDrawFrameAndFocus draw
the list box frames in the active state and ensure that a keyboard focus frame is redrawn
around the currently active list.  The call to doResetTypeSelection resets certain
variables used by the type selection function.  (This latter is necessary because it is
possible that, while the application was in the background, the user may have changed the
"Delay Until Repeat" setting in the Keyboard control panel, a value which is used in the
type selection function.)  ActivateControl is called on the root control to activate all
the window's controls and redraw them in that state.  doDrawSelections redraws the
current list selections in the colour set by SetThemeTextColour.

Except for the call to doResetTypeSelection, much the same occurs if the window is
becoming inactive, except that LActivate removes highlighting from the currently selected
cells, LUpdate redraws the lists' text in a dimmed colour, doDrawFrameAndFocus removes
the keyboard focus frame from the active list and draws the list box frames in the
inactive state, DeactivateControl draws the controls in the inactive mode, and
doDrawSelections redraws the current list selections in the colour set by
SetThemeTextColour.

doInContent

doInContent further processes mouse-down events in the content region of the window.

In the first block, handles to the two lists are retrieved.  The first three lines of the
next block get copies of the lists' display rectangles.  Since these rectangles do not
include the scroll bars, they are then expanded to the right to encompass the scroll bar
areas.

The next block converts the mouse coordinates of the mouse-down to local coordinates
required by the following call to PtInRect.

If the mouse click was in one of the list rectangles, and if that rectangle is not the
current list's rectangle, the application-defined function which changes the currently
active list is called.  Next, LClick is called to handle all user action until the
mouse-button is released.  If LClick returns true, a double-click occurred, in which case
an application-defined function is called to extract the contents of the currently
selected cells.

If the mouse-down event was not within one of the list rectangles, FindControl is called
to determine if there is an enabled control under the mouse cursor.  If so, TrackControl
is called to handle user actions until the mouse button is released.  If the cursor is
still within the control when the mouse button is released, and if the control is the
window's single push button, an application defined function is called to extract the
contents of the currently selected cells.

doCreateTextList

doCreateTextList, supported by the two following functions, creates the text list.

SetRect sets the rectangle which will be passed as the rDataBnds parameter of the LNew
call to specify one column and (initially) no rows.  SetPt sets the variable that will be
passed as the cellSize parameter so as to specify that the List Manager should
automatically calculate the cell size.  The next line adjusts the received list rectangle
to eliminate the area occupied by the vertical scroll bar.

The call to LNew creates the list.  The parameters specify that the List Manager is to
calculate the cell size, the default list definition function is to be used, automatic
drawing mode is to be enabled, no room is to be left for a size box, the list is not to
have a horizontal scroll bar, and the list is to have a vertical scroll bar.

The next line calls an application-defined function which adds rows to the list and
stores data in its cells.

The next two lines set the cell at the topmost row as the initially-selected cell. 
doResetTypeSelection calls an application-defined function which initialises certain
variables used by the type selection function.  The last line returns the handle to the
list.

doAddRowsAndDataToTextList

doAddRowsAndDataToTextList adds rows to the text list and stores data in its cells.  The
data is retrieved from a 'STR#' resource.

The for loop copies the strings from the specified 'STR#' resource and passes each string
as a parameter in a call to an application-defined function which inserts a new row into
the list and copies the string to that cell.

Note at this point that the strings in the 'STR#' resource are not arranged
alphabetically.

doAddTextItemAlphabetically

doAddTextItemAlphabetically does the heavy work in the process of adding the rows to the
text list and storing the text.  The bulk of the code is concerned with building the list
in such a way that the cells are arranged in alphabetical order.

The first line sets the variable found to false.  The next line sets the variable
totalRows to the number of rows in the list.  (In this program, this is initially 0.) 
The next line sets the variable currentRow to -1.  The while loop executes until the
variable found is set to true.

Within the loop, the first line increments currentRow to 0.  The first time this function
is called, currentRow will equal totalRows at this point  and the loop will thus
immediately exit to the first line below the loop.  The call to LAddRow at this line adds
one row to the list, inserting it before the row specified by currentRow. The list now
has one row (cell (0,0)).  LSetCell copies the string to this cell.  The function then
exits, to be re-called another by doAddRowsAndDataToTextList for as many times as there
are remaining strings.

The second time the function is called, the first line in the while loop again sets
currentRow to 0.  This time, however, the if block does not execute because totalRows is
now 1.  Thus SetPt sets the variable aCell to (0,0) and LGetCellDataLocation retrieves
the offset and length of the data in cell (0,0).  This allows the string in this cell to
be alphabetically compared with the "incoming" string using CompareText.  If the incoming
string is "less than" the string in cell (0,0), CompareText returns -1, in which case:

*   The loop exits.  LaddRow inserts one row before cell(0,0) and the old cell (0,0)
    thus becomes cell(0,1).  The list now contains two rows.

*   SetPt sets cell (0,0) and LSetCell copies the "incoming" string to that cell.  The
    "incoming" string, which was alphabetically "less than" the first string, is thus
    assigned to the correct cell in the alphabetical sense.

*   The function then exits, to be re-called for as many times as there are remaining
    strings.

If, on the other hand, CompareText returns 0 (strings equal) or 1 ("incoming" string
"greater than" the string in cell (0,0), the loop repeats.  At the first line in the
loop, currentRow is incremented to 1, which is equal to totalRows.  Accordingly, the loop
exits immediately, LAddRow inserts a row before cell (0,1) (that is, cell (0,1) is
created), LSetCell copies the "incoming" string to that cell, and the function exits, to
be re-called for as many times as there are remaining strings.

The ultimate result of all this is an alphabetically ordered list.

doCreateIconList

doCreateIconList, supported by the following function, creates the icon list. 

SetRect sets the rectangle which will be passed as the rDataBnds parameter of the LNew
call to specify one column and (initially) no rows.  SetPt sets the variable which will
be passed as the cellSize parameter so as to specify that the List Manager should make
the cell size of all cells 52 by 52 pixels.  The next line adjusts the list rectangle to
reflect the area occupied by the vertical scroll bar.

The call to LNew creates the list.  The parameters specify that the List Manager is to
make all cell sizes 52 by 52 pixels, a custom list definition function is to be used,
automatic drawing mode is to be enabled, no room is to be left for a size box, the list
is not to have a horizontal scroll bar, and the list is to have a vertical scroll bar.

The next line assigns lOnlyOne to the selFlags field of the list structure, meaning that
the List manager's cell selection algorithm is modified so as to allow only one cell to
be selected at any one time.

The next line calls an application-defined function which adds rows to the list and
stores data in its cells.

The next two lines select the cell at the topmost row as the initially-selected cell. 
The last line returns the handle to the list.

doAddRowsAndDataToIconList

doAddRowsAndDataToIconList adds 8 rows to the icon list and stores a handle to an icon
suite in each of the 8 cells.

The first line sets the variable rowNumber to the current number of rows, which is 0.

The for loop executes 8 times.  Each time through the loop, the following occurs:

*   GetIconSuite creates a new icon family and fills it with icons with the specified
    resource ID and of the types specified in the last parameter (that is, large icons
    only).

*   LAddRow inserts a new row in the list at the location specified by the variable
    rowNumber.  SetPt sets this cell and LSetCell stores the handle to the icon suite
    as the cell's data.  The last line increments the variable rowNumber, which is
    passed in the SetPt call.

doHandleArrowKey

doHandleArrowKey further processes Down Arrow and Up Arrow key presses.  This is the
first of eleven functions dedicated to the handling of key-down events.

Recall that doHandleArrowKey's third parameter (allowExtendSelect) is set to true by the
calling function (doKeyDown) only if the text list is the currently active list.

The first line sets the variable moveToTopBottom to false, which can be regarded as the
default.  At the next two lines, if the Command key was also down at the time of the
Arrow key press, this variable is set to true.

If the text list is the currently active list, and if the Shift key was down, the
application-defined function doArrowKeyExtendSelection is called; otherwise, the
application-defined function doArrowKeyMoveSelection is called.

doArrowKeyMoveSelection

doArrowKeyMoveSelection further processes those Arrow key presses which occurred when
either list was the currently active list but the Shift key was not down.  The effect of
this function is to deselect all currently selected cells and to select the appropriate
cell according to, firstly, which Arrow key was pressed (Up or Down) and, secondly,
whether the Command key was down at the same time.

The if statement calls an application-defined function which searches for the first
selected cell in the specified list.  That function returns true if a selected cell is
found, or false if the list contains no selected cells.

If true is returned by that call, the variable currentSelection will hold the first
selected cell.  However, this could be changed by the second line within the if block if
the key pressed was the Down-Arrow. doFindLastSelectedCell finds the last selected cell
(which could, of course, well be the same cell as the first selected cell if only one
cell is currently selected).  Either way, the variable currentSelection will now hold
either the only cell currently selected, the first cell selected (if more than one cell
is currently selected and the key pressed was the Up Arrow), or the last cell selected
(if more than one cell is currently selected and the key pressed was the Down Arrow).

With that established, doFindNewCellLoc determines the next cell to select, which will
depend on, amongst other things, whether the Command key was down at the time of the key
press (that is, on whether the moveToTopBottom parameter is true or false).  The variable
newSelection will contain the results of that determination.

doSelectOneCell then deselects all currently selected cells and selects the cell
specified by the variable newSelection.

It is possible that the newly-selected cell will be outside the list's display rectangle. 
Accordingly, doMakeCellVisible, if necessary, scrolls the list until the newly-selected
cell appears at the top or the bottom of the display rectangle.

doArrowKeyExtendSelection

doArrowKeyExtendSelection is similar to the previous function except that it adds
additional cells to the currently selected cells.  This function is called only when the
text list is the currently active list and the Shift key was down at the time of the
Arrow key press.

By the fifth line, the variable currentSelection will hold either the only cell currently
selected, the first cell selected (if more than one cell is currently selected and the
key pressed was the Up Arrow), or the last cell selected (if more than one cell is
currently selected and the key pressed was the Down Arrow).

doFindNewCellLoc determines the next cell to select, which will depend on, amongst other
things, whether the Command key was down at the time of the key press (that is, on
whether the moveToTopBottom parameter is true or false).  The variable newSelection will
contain the results of that determination.  The similarities between this function and
doArrowKeyMoveSelection end there.

At the next line, LGetSelect is called to check whether the cell specified by the
variable newSelection is selected.  If it is not, LSetSelect selects it.  (This check by
LGetSelect is advisable because, for example, the first-selected cell as this function is
entered might be cell (0,0), that is, the very top row.  If the Up-Arrow was pressed in
this circumstance, and as will be seen, doFindNewCellLoc returns cell (0,0) in the
newSelection variable.  There is no point in selecting a cell which is already selected.)

It is possible that the newly-selected cell will be outside the list's display rectangle. 
Accordingly, doMakeCellVisible, if necessary, scrolls the list until the newly-selected
cell appears at the top or the bottom of the display rectangle.

doTypeSelectSearch

doTypeSelectSearch is the main type selection function.  It is called from doKeyDown
whenever a key-down or auto-key event is received and the key pressed is not the Tab key,
the Up Arrow key or the Down Arrow key.

The global variables gTSString, gTSResetThreshold, gTSLastKeyTime, and gTSLastListHit are
central to the operation of doTypeSelectSearch.  gTSString holds the current type
selection search string entered by the user.  gTSResetThreshold holds the number of ticks
which must elapse before type selection resets, and is dependent on the value the user
sets in the "Delay Until Repeat" section of the Keyboard control panel.  gTSLastKeyTime
holds the time in ticks of the last key press.  gTSLastListHit holds a handle to the last
list that type selection affected.

The first line extracts the character code from the message field of the event structure.

The next block will cause the application-defined function which resets type selection to
be called if either of the following situations prevail: if the list which is the target
of the current key press is not the same as the list which was the target of the previous
key press; if a number of ticks since the last key press is greater than the number
stored in gTSResetThreshold; if the current length of the type selection string is 255
characters.

The next line stores the handle to the list which is the target of the current key press
in gTSLastListHit so as to facilitate the comparison at the first if block the next time
the function is called.  The next line stores the time of the current key press in
gTSLastKeyTime for the same purpose.  

The next two lines increment the length byte of the type selection string and add the
received character to the type selection string.  That string now holds all the
characters received since the last type selection reset.

SetPt sets the variable theCell to represent the first cell in the list.  This is passed
as a parameter in the LSearch call, and specifies the first cell to examine.  LSearch
examines this cell and all subsequent cells in an attempt to find a match to the type
selection string.  If a match exists, the cell in which the first match is found will be
returned in theCell parameter, LSearch will return true and the following three lines
will execute.

Of those three lines, ordinarily only the call to LSetSelect (which deselects all
currently selected cells and selects the specified cell) and the last line (which, if
necessary, scrolls the list so that the newly-selected cell is visible in the display
rectangle) would be necessary.  However, because the application-defined function
doSelectOneCell has no effect unless there is currently at least one selected cell in the
list, the call to doSelectOneCell is included to account for the situation where the user
may have deselected all of the text list cells using Command-clicking or dragging.

The actual matching task is performed by the match (callback) function the universal
procedure pointer to which is passed in the third parameter to the LSearch call.  Note
that the default match function has been replaced by the custom callback function
doSearchPartialMatch.

doSearchPartialMatch

doSearchPartialMatch is the custom match function called by LSearch, in the previous
function, to attempt to find a match to the current type selection string.  For the
default function to return a match, the type selection string would have to match an
entire cell's text.  doSearchPartialMatch, however, only compares the characters of the
type selection string with the same number of characters in the cell's text.  For
example, if the type selection string is currently "be" and a cell with the text "Beams"
exists, doSearchPartialMatch  will report a match.

A comparison by IdenticalText (which returns 0 if the strings being compared are equal)
is only made if the cell contains data and the length of that data is greater than or
equal to the current length of the type selection string.  If these conditions do not
prevail, doSearchPartialMatch returns 1 (no match found).  If these conditions do
prevail, IdenticalText is called with, importantly, both the third and fourth parameters
set to the current length of the type selection string.  IdenticalText will return 0 if
the strings match or 1 if they do not match.

doFindFirstSelectedCell

doFindFirstSelectedCell and the following four functions are general utility functions
called by the previous Arrow key handling and type selection functions. 
doFindFirstSelectedCell searches for the first selected cell in a list, returning true if
a selected cell is found and providing the cell's coordinates to the calling function.

SetPt sets the starting cell for the LGetSelect call.  Since the first parameter in the
LGetSelect call is set to true, LGetSelect will continue to search the list until a
selected cell is found or until all cells have been examined.

doFindFirstSelectedCell returns true when and if a selected cell is found.

doFindLastSelectedCell

doFindLastSelectedCell finds the last selected cell in a list (which could, of course,
also be the first selected cell if only one cell is selected).

If the call to doFindFirstSelectedCell reveals that no cells are currently selected,
doFindlastSelectedCell simply returns.  If, however, doFindFirstSelectedCell finds a
selected cell, that cell is passed as the starting cell in the LGetSelect call.

As an example of how the rest of this function works, assume that the first selected cell
is (0,1), and that cell (0,4) is the only other selected cell.  LGetSelect examines this
cell and returns true, causing the loop to execute.  The first line in the while loop
thus assigns (0,1) to theCell and the next line increments aCell to (0,2).  LGetSelect
starts another search using (0,2) as the starting cell.  Because cells (0,2) and (0,3)
are not selected, LGetSelect advances to cell (0,4) before it returns.  Since it has
found another selected cell, LGetSelect again returns true, so the loop executes again. 
aCell now contains (0,4), and the first line in the while loop assigns that to theCell. 
Once again, LNextCell increments aCell, this time to (0,5).

This time, however, LGetSelect will return false because neither cell (0,5) nor any cell
below it is selected.  The loop thus terminates, theCell containing (0,4), which is the
last selected cell.

doFindNewCellLoc

doFindNewCellLoc finds the new cell to be selected in response to Arrow key presses. 
That cell will be either one up or one down from the cell specified in the oldCellLoc
parameter (if the Command key was not down at the time of the Arrow key press) or the top
or bottom cell (if the Command key was down).

The first line gets the number of rows in the list. (Recall that the List Manager sets
the dataBounds.bottom coordinate to one more than the vertical coordinate of the last
cell.)

If the Command key was down (moveToTopBottom is true) and the key pressed was the Up
Arrow, the new cell to be selected is the top cell in the list.  If the key pressed was
the Down Arrow key, the new cell to be selected is the bottom cell in the list.

If the Command key was not down and the key pressed was the Up Arrow key, and if the
first selected cell is the top cell in the list, the new cell to be selected remains as
set at the second line in the function; otherwise, the new cell to be selected is set as
the cell above the first selected cell.  If the key pressed was the Down Arrow key, and
if the last selected cell is the bottom cell in the list, the new cell to be selected
remains as set at the second line in the function; otherwise, the new cell to be selected
is set as the cell below the last selected cell.

doSelectOneCell

doSelectOneCell deselects all cells in the specified list and selects the specified cell.

If no cells in the list are selected, the function returns immediately.  Otherwise, the
first selected cell is passed as the starting cell in the call to LGetSelect.

The while loop will continue to execute while a selected cell exists between the starting
cell specified in the LGetSelect call and the end of the list.  Within the loop, if the
current LGetSelect starting cell is not the cell specified for selection, that cell is
deselected.  When the loop exits, LSetSelect selects the cell specified for selection.

Note that defeating the de-selection of the cell specified for selection if it is already
selected (the if statement within the while loop) prevents the unsightly flickering which
would occur as a result of that cell being deselected inside the loop and then selected
again after the loop exits.

doMakeCellVisible

doMakeCellVisible checks whether a specified cell is within the list's display rectangle
and, if not, scrolls the list until that cell is visible.

The first line gets a copy of the rectangle which encompasses the currently visible
cells.  (Note that this rectangle is in cell coordinates.)  The if statement tests
whether the specified cell is within this rectangle.  If it is not, the list is scrolled
as follows:

*   If the specified cell is "below" the bottom of the display rectangle, the variable
    dRows is set to the difference between the cell's v coordinate and the value in the
    bottom field of the display rectangle, plus 1.  (Recall that the List Manager sets
    the bottom field to one greater than the v coordinate of the last visible cell.)

*   If the specified cell is "above" the top of the display rectangle, the variable
    dRows is set to the difference between the cell's v coordinate and the value in
    the top field of the display rectangle.

With the number of cells to scroll, and the direction to scroll, established, LScroll is
called to effect the scroll.

doResetTypeSelection

doResetTypeSelection resets the global variables which are central to the operation of
the type selection function doTypeSelectSearch.

The first line, in effect, makes the type selection string an empty string.  The next
line sets the variable which holds the handle to the list which is the target of the
current key press to NULL.  The next line sets the variable which holds the number of
ticks since the last key press to 0.  The next line sets the variable which holds the
type selection reset threshold to twice the value stored in the low memory global
variable KeyThresh.  However, if this value is greater than the value represented by the
constant kMaxKeyThresh, the variable is made equal to kMaxKeyThresh.

doRotateCurrentList

doRotateCurrentList rotates the currently active list in response to the Tab key and to
mouse-downs in the non-active list.

The first line saves the handle to the currently active list.  The next line retrieves
the handle to the new list to be activated from the refCon field of the currently active
list's list structure.  The third line makes the new list the currently active list.

The last two lines cause the keyboard focus frame to be erased from the previously
current list, the list box frame to be drawn around the previously current list, and the
keyboard focus frame to be drawn around the new current list.

doDrawFrameAndFocus

doDrawFrameAndFocus is called by doUpdate, doActivateWindow, and doRotateCurrentList to
draw or erase the keyboard focus frame from the specified list, and to draw the list box
frame in either the activated or deactivated state.

The second and third lines get the list's rectangle from the rView field of the list
structure and expand it to the right by the width of the scroll bar.

The first call to DrawThemeFocusRect erases the keyboard focus frame, if it exists.

Depending on the value received in the inState formal parameter, the list box frame is
drawn in either the activated or deactivated state.  If the specified list is the current
list, DrawThemeFocusRect is called again, this time to draw the keyboard focus frame.

doExtractSelections

doExtractSelections is called when the user clicks the Extract push button or double
clicks an item in a list.

The first block gets the handles to the lists.  The next two lines initialise the Str255
array that will be used to hold the extracted strings.

The next block copies the data from the selected cells in the text list to the Str255
array.  The for loop which is traversed once for each cell in the list.  SetPt increments
the v coordinate of the variable theCell.  If the specified cell is selected
(LGetSelect), LGetCellDataLocation is called to get the length of the data in the cell,
and LGetCell is called to copy the cell's data into an element of the Str255 array.

The next block gets the selected cell in the icon list, retrieve the related string from
the specified STR# resource, and assign it to the 15th element of the Str255 array. 
SetPt sets the starting cell for the LGetSelect search.

The last two lines force an update event which will cause the function doDrawSelections
to draw the contents of the Str255 array in the group box at the bottom of the window.

doDrawSelections

doDrawSelections is called by doUpdate and DoActivateWindow to draw the contents of the
Str255 array "filled in" by the function doExtractSelections.

DialogList.c

doListsDialog contains the main functions pertaining to the lists in the movable modal
dialog box.

doListsDialog

doListsDialog creates a movable modal dialog box using 'DLOG', 'dlgx', 'dftb', and 'DITL'
resources.  The 'DITL' resource contains, amongst other items, two list controls.  Each
list control is supported by an 'ldes' resource.  Both 'ldes' resources specify no rows,
one column, a cell height of 14 pixels, a vertical scroll bar, and the system LDEF.  The
'dftb' resource specifies the small system font for the list controls.

At the first block, the window, if open, is explicitly deactivated.  The dialog is then
created.  At the next block, the Dialog Manager is told which items are the default and
Cancel items.

A custom event filter function is used.  The call to NewModalFilterProc creates the
associated routine descriptor.

At the next block, and for each list control, the handle to the list control is obtained,
the handle to the associated list structure is obtained, the application-defined function
doAddRowsAndDataToTextList is called to add the specified number of rows and the data to
the list's cells, the cell-selection algorithm is customised to allow the selection of
one cell only, and the first cell is selected.

ShowDialog is then called to display the dialog.  The first call to SetKeyboardFocus is
made to force the "Watermark" list's scroll bar to be drawn.  The second call is to set
the keyboard focus to the "Date Format" list.

The call to the application-defined function doFixKeyboardFocusArea is made to compensate
for a deficiency of the list control CDEF.  The width of the list box frame (drawn by the
CDEF) is two pixels.  The width of the keyboard focus frame is three pixels, that is, one
pixel outside the list box frame.  When the keyboard focus is moved to the other list,
the keyboard focus frame is erased from the previous list and the CDEF redraws the list
box frame.  The problem is that the keyboard focus frame is erased to white, not to the
background colour/pattern of the dialog box proper.  The result is a one-pixel-wide white
frame around the list box frame.  doFixKeyboardFocusArea simply erases the keyboard focus
frame to the correct background colour/pattern and redraws the list box frame.

Within the do-while loop, ModalDialog retains control until an enabled item is hit.  If
the push buttons are hit, or if the last click in one of the list boxes was a
double-click, the loop exits.

If the item hit is the "Date Format" list, SetPt sets the variable theCell to represent
the first cell in the list.  This is passed as a parameter in the LGetSelect call, which
searches the list until it finds a cell that is selected.  LGetDataLocation is called to
get the length of the data in that cell and LGetCell is called to copy the data (a
string) to a local Str255 variable.

At the next block, a handle to the static text field control associated with this list is
obtained and its text is set with the string obtained by LGetCell.  Draw1Control is then
called to draw the static text field control with this newly-set text.

doFixKeyboardFocusArea is called for the "Watermark" list for reasons previously
explained.  It accounts for the situation where the mouse-down changed the current list
to the "Date Format" list as well as making a selection within that list.

The last action is to check whether the last click in the list box was a double-click. 
If the last click was a double-click, the variable wasDoubleClick is set to true, causing
the loop to exit.

The same general procedure is followed in the event of a hit on the "Watermark" list.

When the OK or Cancel push button is hit, or one of the lists has been double-clicked,
the dialog and the routine descriptor are disposed of.


eventFilter

A custom event filter function is used for two reasons:

*   To ensure that the keyboard focus frame is redrawn after an overlaying balloon is
    removed.

*   To intercept keyDown events so as to support type selection in the "Watermark" list.

If the event is an update event for the dialog, and if the application is not currently
in the background, the following occurs.  GetKeyboardFocus is called to determine which
list control currently has the keyboard focus.  GetControlData is called to get the
handle to the associated list structure.  The list's rectangle is then obtained from that
list structure, adjusted by the width of the scroll bar and passed in the call to
DrawThemeFocusRect to draw the keyboard focus frame around that list.

If the event is a keyDown event, the character code is extracted from the event
structure's message field. 

If the key hit was not the Up-Arrow, Down-Arrow, or tab key, the following occurs. 
GetDialogItemAsControl is called to get the handle to the "Watermark" list control,
GetControlData is called to get the handle to the associated list, and GetKeyboardFocus
is called to get the handle to the control with keyboard focus.  If the "Watermark" list
control currently has the focus, the application-defined function doTypeSelectSearch is
called (to handle type selection) and Draw1Control is called on the list control to
ensure that the type-selected item is highlighted.  handledEvent is then set to true to
inform ModalDialog that the filter function handled the event.

(Apart from supporting type-selection in the "Watermark" list, this arrangement means
that the only keyDown events received by ModalDialog in respect of the "Date Format" list
will be Up-Arrow, Down-Arrow, and tab key events.)

doFixKeyboardFocus

doFixKeyboardFocus obtains the rectangle for the specified list, expands it to
accommodate the width of the scroll bar, and erases the keyboard focus frame.  Because
the background colour/pattern is that of the dialog proper when this function is called,
the erasure is to the correct colour/pattern.  Because the erasure also erases the list
box frame, DrawThemeListBoxFrame is called to redraw that frame.

LDEF.c

LDEF.c is the custom list definition function (LDEF) used by the window's icon list.

main

The List Manager sends a list definition function four types of messages in the message
parameter.  Only two of these are relevant to this list definition function.  The main
function calls the appropriate function to handle each message type.

doLDEFDraw

doLDEFDraw handles the lDrawMsg message, which relates to a specific cell.

The first two lines save the current colour graphics port and set the colour graphics
port to the port in which the list is drawn.  

EraseRect erases the cell rectangle.  The next line gets a copy of the 52 pixel by 52
pixel cell rectangle.  The next four lines adjust this rectangle to the size of a 32 by
32 pixel icon.

The if statement checks whether the cell's data is 4 bytes long (the size of a handle). 
If it is, LGetCell is called to get the cells's data into the variable iconSuiteHdl and
PlotIconSuite is called to draw the icon within the specified rectangle.  If the list is
active, kTransformNone is passed in the transform parameter, otherwise kTransformDisabled
is passed.  This latter causes the icon to be drawn in the disabled (dimmed) state.

GetIndString is then called to get the string corresponding to the icon.  The rectangle
used to draw the icon is adjusted and passed, together with the string, in a call to
TETextBox.  TETextBox draws the string underneath the icon.

If the lDrawMsg message indicated that the cell was selected, the cell highlighting
function is called.  The previously saved graphics port is then restored.

doLDEFHighlight

doLDEFHighlight handles the lHiliteMsg message and may also be called from doLDEFDraw.

A copy of the value in the low memory global HiliteMode is acquired, BitClr is called to
clear the highlight bit, and HiliteMode is set to this new value.  The last line
highlights the cell.

When this program is run under a version of the Mac OS earlier than Mac OS 
8.5, you may notice the following anomaly.

The width of the list box frame (drawn by the CDEF) is two pixels.  The width of the
keyboard focus frame is three pixels, that is, one pixel outside the list box frame.
When the keyboard focus is moved to the other list, the keyboard focus frame is 
erased from the previous list and the CDEF redraws the list box frame.  The problem 
is that the keyboard focus frame is erased to white, not to the background 
colour/pattern of the dialog box proper.  The result is a one-pixel-wide white frame
around the list box frame.

To compensate for this anomaly, proceed as follows:

In Lists.h, add this function prototype:

  void  doFixKeyboardFocusArea(DialogPtr, ListHandle);

In DialogLists.c, add this function, which simply erases the keyboard focus frame to
the correct background colour/pattern and redraws the list box frame:

  // ×××××××××××××××××××××××××××××××××××××××××××××××××××××××× doFixKeyboardFocusArea

  void  doFixKeyboardFocusArea(DialogPtr dialogPtr, ListHandle listHdl)
  {
  GrafPtr oldPort;
  Rect    listRect;

  GetPort(&oldPort);
  SetPort(dialogPtr);

  listRect = (*listHdl)->rView;
  listRect.right += kScrollBarWidth;
  DrawThemeFocusRect(&listRect,false);
  DrawThemeListBoxFrame(&listRect,true);

  SetPort(oldPort);
  }

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

In DialogLists.c, in the function doListsDialog, after the calls to SetKeyboardFocus,
add:

  doFixKeyboardFocusArea(dialogPtr,watermarkListHdl);

In DialogLists.c, in the function doListsDialog, after the first call to Draw1Control,
add:

  doFixKeyboardFocusArea(dialogPtr,watermarkListHdl);

In DialogLists.c, in the function doListsDialog, after the second call to Draw1Control,
add:

  doFixKeyboardFocusArea(dialogPtr,dateFormatListHdl);

doFixKeyboardFocus obtains the rectangle for the specified list, expands it to 
accommodate the width of the scroll bar, and erases the keyboard focus frame.  Because
the background colour/pattern is that of the dialog proper when this function is 
called, the erasure is to the correct colour/pattern.  Because the erasure also erases
the list box frame, DrawThemeListBoxFrame is called to redraw that frame.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

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 »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »
Explore some of BBCs' most iconic s...
Despite your personal opinion on the BBC at a managerial level, it is undeniable that it has overseen some fantastic British shows in the past, and now thanks to a partnership with Roblox, players will be able to interact with some of these... | Read more »

Price Scanner via MacPrices.net

You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13″ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13″ M3 MacBook Airs... Read more
Multiple Apple retailers are offering 13-inch...
Several Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices... Read more
Roundup of Verizon’s April Apple iPhone Promo...
Verizon is offering a number of iPhone deals for the month of April. Switch, and open a new of service, and you can qualify for a free iPhone 15 or heavy monthly discounts on other models: – 128GB... Read more

Jobs Board

Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
Liquor Stock Clerk - S. *Apple* St. - Idaho...
Liquor Stock Clerk - S. Apple St. Boise Posting Begin Date: 2023/10/10 Posting End Date: 2024/10/14 Category: Retail Sub Category: Customer Service Work Type: Part Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.