TweetFollow Us on Twitter

MACINTOSH C CARBON

Demonstration Program Controls1

Goto Contents

// *******************************************************************************************
// Controls1.c                                                             CLASSIC EVENT MODEL
// *******************************************************************************************
// 
// This program opens a kWindowFullZoomGrowDocumentProc window containing:
//
// o  Three pop-up menu buttons (fixed width, variable width and use window font variants).
//
// o  Three non-auto-toggling radio buttons auto-embedded in a primary group box (text title
//    variant).
//
// o  Three non-auto-toggling checkboxes auto-embedded in a primary group box (text title
//    variant).
//
// o  Four push buttons (two basic, one left colour icon variant, and one right colour icon
//     variant).
//
// o  A vertical scroll bar (non live-feedback variant) and a horizontal scroll bar (live-
//    feedback variant). 
//
// Some controls are created using 'CNTL' resources.  Others are created programmatically.
//
// The window also contains a window header frame in which is displayed:
//
// o  The menu items chosen from the pop-up menus.
//
// o  The identity of a push button when that push button is clicked.
//
// o  Scroll bar control values when the scroll arrows or gray areas/tracks of the scroll bars
//    are clicked and when the scroll box/scroller is dragged.
//
// The scroll bars are moved and resized when the user resizes or zooms the window; however,
// the scroll bars do not scroll the window contents.
//
// A Demonstration menu allows the user to deactivate the group boxes in which the radio
// buttons and checkboxes are embedded.
//
// The program utilises the following resources:
//
// o  A 'plst' resource.
//
// o  An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit, and Demonstration menus,
//    and the pop-up menus (preload, non-purgeable).  
//
// o  A 'WIND' resource (purgeable) (initially not visible).  
//
// o  'CNTL' resources for those controls not created programmatically.
//
// o  Two 'cicn' resources (purgeable) for the colour icon variant buttons.
//
// o  An 'hrct' resource and an 'hwin' resource (both purgeable), which provide help balloons
//    describing  the various controls.  
//
// o  A 'SIZE' resource with the acceptSuspendResumeEvents,  canBackground, 
//    doesActivateOnFGSwitch, and isHighLevelEventAware flags set.
//
// *******************************************************************************************

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

#include <Carbon.h>
#include <string.h>

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

#define rMenubar          128
#define rWindow           128
#define mAppleApplication 128
#define  iAbout           1
#define mFile             129
#define  iQuit            12
#define mDemonstration    131
#define  iColour          1
#define  iGrids           2
#define cPopupFixed       128
#define cPopupWinFont     129
#define cRadiobuttonRed   130
#define cRadiobuttonBlue  131
#define cCheckboxGrid     132
#define cCheckboxGridsnap 133
#define cGroupBoxColour   134
#define cButton           135
#define cButtonLeftIcon   136
#define cScrollbarVert    137
#define kScrollBarWidth   15
#define MAX_UINT32        0xFFFFFFFF
#define MIN(a,b)          ((a) < (b) ? (a) : (b))

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

typedef struct
{
  ControlRef popupFixedRef;
  ControlRef popupVariableRef;
  ControlRef popupWinFontRef;
  ControlRef groupboxColourRef;
  ControlRef groupboxGridsRef;
  ControlRef buttonRef;
  ControlRef buttonDefaultRef;
  ControlRef buttonLeftIconRef;
  ControlRef buttonRightIconRef;
  ControlRef radiobuttonRedRef;
  ControlRef radiobuttonWhiteRef;
  ControlRef radiobuttonBlueRef;
  ControlRef checkboxGridRef;
  ControlRef checkboxRulersRef;
  ControlRef checkboxGridSnapRef;
  ControlRef scrollbarVertRef;
  ControlRef scrollbarHorizRef;
} docStruc;

typedef docStruc **docStrucHandle;

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

ControlActionUPP gActionFunctionVertUPP;
ControlActionUPP gActionFunctionHorizUPP;
Boolean          gRunningOnX = false;
WindowRef        gWindowRef;
Boolean          gDone;
Boolean          gInBackground = false;
Str255           gCurrentString;

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

void  main                (void);
void  doPreliminaries     (void);
OSErr quitAppEventHandler (AppleEvent *,AppleEvent *,SInt32);
void  doGetControls       (WindowRef);
void  doEvents            (EventRecord *);
void  doMouseDown         (EventRecord *);
void  doMenuChoice        (SInt32);
void  doUpdate            (EventRecord *);
void  doActivate          (EventRecord *);
void  doActivateWindow    (WindowRef,Boolean);
void  doOSEvent           (EventRecord *);
void  doInContent         (EventRecord *,WindowRef);
void  doPopupMenuChoice   (WindowRef,ControlRef,SInt16);
void  doVertScrollbar     (ControlPartCode,WindowRef,ControlRef,Point);
void  actionFunctionVert  (ControlRef,ControlPartCode);
void  actionFunctionHoriz (ControlRef,ControlPartCode);
void  doMoveScrollBox     (ControlRef,SInt16);
void  doRadioButtons      (ControlRef,WindowRef);
void  doCheckboxes        (ControlRef);
void  doPushButtons       (ControlRef,WindowRef);
void  doAdjustScrollBars  (WindowRef);
void  doDrawMessage       (WindowRef,Boolean);
void  doConcatPStrings    (Str255,Str255);
void  doCopyPString       (Str255,Str255);
void  helpTags            (WindowRef);

// ************************************************************************************** main

void  main(void)
{
  MenuBarHandle  menubarHdl;
  SInt32         response;
  MenuRef        menuRef;
  docStrucHandle docStrucHdl;
  EventRecord    EventStructure;

  // ........................................................................ do preliminaries

  doPreliminaries();

  // ..................................................... create universal procedure pointers

  gActionFunctionVertUPP  = NewControlActionUPP((ControlActionProcPtr) actionFunctionVert);
  gActionFunctionHorizUPP = NewControlActionUPP((ControlActionProcPtr) actionFunctionHoriz);

  // ............................................................... set up menu bar and menus

  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    ExitToShell();
  SetMenuBar(menubarHdl);
  DrawMenuBar();

  Gestalt(gestaltMenuMgrAttr,&response);
  if(response & gestaltMenuMgrAquaLayoutMask)
  {
    menuRef = GetMenuRef(mFile);
    if(menuRef != NULL)
    {
      DeleteMenuItem(menuRef,iQuit);
      DeleteMenuItem(menuRef,iQuit - 1);
      DisableMenuItem(menuRef,0);
    }

    gRunningOnX = true;
  }

  // ................................................. initial advisory text for window header

  doCopyPString("\pBalloon and Help tag help is available",gCurrentString);

  // ........ open a window, set font size, set Appearance-compliant colour/pattern for window

  if(!(gWindowRef = GetNewCWindow(rWindow,NULL,(WindowRef) -1)))
    ExitToShell();

  SetPortWindowPort(gWindowRef);
  UseThemeFont(kThemeSmallSystemFont,smSystemScript);

  SetThemeWindowBackground(gWindowRef,kThemeBrushDialogBackgroundActive,false);

  // ........... get block for document structure, assign handle to window record refCon field

  if(!(docStrucHdl = (docStrucHandle) NewHandle(sizeof(docStruc))))
    ExitToShell();

  SetWRefCon(gWindowRef,(SInt32) docStrucHdl);

  // ........................ get controls, adjust scroll bars, get help tags, and show window

  doGetControls(gWindowRef);
  doAdjustScrollBars(gWindowRef);

  if(gRunningOnX)
    helpTags(gWindowRef);

  ShowWindow(gWindowRef);

// ......................................................................... enter eventLoop

  gDone = false;

  while(!gDone)
  {
    if(WaitNextEvent(everyEvent,&EventStructure,MAX_UINT32,NULL))
      doEvents(&EventStructure);
  }
}

// *************************************************************************** doPreliminaries

void  doPreliminaries(void)
{
  OSErr osError;

  MoreMasterPointers(128);
  InitCursor();
  FlushEvents(everyEvent,0);

  osError = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
                            NewAEEventHandlerUPP((AEEventHandlerProcPtr) quitAppEventHandler),
                            0L,false);
  if(osError != noErr)
    ExitToShell();
}

// **************************************************************************** doQuitAppEvent

OSErr  quitAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
  OSErr    osError;
  DescType returnedType;
  Size     actualSize;

  osError = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,NULL,0,
                              &actualSize);

  if(osError == errAEDescNotFound)
  {
    gDone = true;
    osError = noErr;
  } 
  else if(osError == noErr)
    osError = errAEParamMissed;

  return osError;
}

// ***************************************************************************** doGetControls

void  doGetControls(WindowRef windowRef)
{
  ControlRef               controlRef;
  docStrucHandle           docStrucHdl;
  OSStatus                 osError;
  Rect                     popupVariableRect    = { 73,  25, 93,245 };
  Rect                     radioButtonWhiteRect = { 183, 35,201, 92 };
  Rect                     checkboxRulersRect   = { 183,138,201,242 };
  Rect                     groupboxGridsRect    = { 136,123,236,252 };
  Rect                     buttonDefaultRect    = { 252,141,272,210 };
  Rect                     buttonRightIconRect  = { 285,141,305,220 };
  Rect                     scrollbarVertRect    = { 0,    0, 16,100 };
  ControlButtonContentInfo buttonContentInfo;
  Boolean                  booleanData = true;
  ControlFontStyleRec      controlFontStyleStruc;

  if(!gRunningOnX)
    CreateRootControl(windowRef,&controlRef);

  docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));

  // ...................................................................... popup menu buttons

  if(!((*docStrucHdl)->popupFixedRef = GetNewControl(cPopupFixed,windowRef)))
    ExitToShell();

  if((osError = CreatePopupButtonControl(windowRef,&popupVariableRect,CFSTR("Time Zone:"),
                                         132,true,76,popupTitleLeftJust,popupTitleNoStyle,
                                         &(*docStrucHdl)->popupVariableRef)) == noErr)
    ShowControl((*docStrucHdl)->popupVariableRef);
  else
    ExitToShell();

  if(!((*docStrucHdl)->popupWinFontRef = GetNewControl(cPopupWinFont,windowRef)))
    ExitToShell();

  // ........................................................................... radio buttons

  if(!((*docStrucHdl)->radiobuttonRedRef = GetNewControl(cRadiobuttonRed,windowRef)))
    ExitToShell();

  if((osError = CreateRadioButtonControl(windowRef,&radioButtonWhiteRect,CFSTR("White"),0,
                                        false,&(*docStrucHdl)->radiobuttonWhiteRef)) == noErr)
    ShowControl((*docStrucHdl)->radiobuttonWhiteRef);
  else
    ExitToShell();

  if(!((*docStrucHdl)->radiobuttonBlueRef = GetNewControl(cRadiobuttonBlue,windowRef)))
    ExitToShell();

  // .............................................................................. checkboxes

  if(!((*docStrucHdl)->checkboxGridRef = GetNewControl(cCheckboxGrid,windowRef)))
    ExitToShell();

  if((osError = CreateCheckBoxControl(windowRef,&checkboxRulersRect,CFSTR("Rulers"),0,false,
                                      &(*docStrucHdl)->checkboxRulersRef)) == noErr)
    ShowControl((*docStrucHdl)->checkboxRulersRef);
  else
    ExitToShell();

  if(!((*docStrucHdl)->checkboxGridSnapRef = GetNewControl(cCheckboxGridsnap,windowRef)))
    ExitToShell();

  // ............................................................................. group boxes

  if(!((*docStrucHdl)->groupboxColourRef = GetNewControl(cGroupBoxColour,windowRef)))
    ExitToShell();

  if((osError = CreateGroupBoxControl(windowRef,&groupboxGridsRect,CFSTR("Grids"),true,
                                      &(*docStrucHdl)->groupboxGridsRef)) == noErr)
    ShowControl((*docStrucHdl)->groupboxGridsRef);
  else
    ExitToShell();

  // ............................................................................ push buttons

  if(!((*docStrucHdl)->buttonRef = GetNewControl(cButton,windowRef)))
    ExitToShell();

  if((osError = CreatePushButtonControl(windowRef,&buttonDefaultRect,CFSTR("OK"),
                                        &(*docStrucHdl)->buttonDefaultRef)) == noErr)
    ShowControl((*docStrucHdl)->buttonDefaultRef);
  else
    ExitToShell();

  if(!((*docStrucHdl)->buttonLeftIconRef = GetNewControl(cButtonLeftIcon,windowRef)))
    ExitToShell();

  buttonContentInfo.contentType = kControlContentCIconRes;
  buttonContentInfo.u.resID = 101;
  if((osError = CreatePushButtonWithIconControl(windowRef,&buttonRightIconRect,
                                               CFSTR("Button"),&buttonContentInfo,
                                               kControlPushButtonIconOnRight,
                                               &(*docStrucHdl)->buttonRightIconRef)) == noErr)
    ShowControl((*docStrucHdl)->buttonRightIconRef);
  else
    ExitToShell();

  // ............................................................................. scroll bars

  if(!((*docStrucHdl)->scrollbarVertRef = GetNewControl(cScrollbarVert,windowRef)))
    ExitToShell();

  if((osError = CreateScrollBarControl(windowRef,&scrollbarVertRect,0,0,100,100,true,
                                       gActionFunctionHorizUPP,
                                       &(*docStrucHdl)->scrollbarHorizRef)) == noErr)
    ShowControl((*docStrucHdl)->scrollbarHorizRef);
  else
    ExitToShell();

  // .........................................................................................

  AutoEmbedControl((*docStrucHdl)->radiobuttonRedRef,windowRef);
  AutoEmbedControl((*docStrucHdl)->radiobuttonWhiteRef,windowRef);
  AutoEmbedControl((*docStrucHdl)->radiobuttonBlueRef,windowRef);
  AutoEmbedControl((*docStrucHdl)->checkboxGridRef,windowRef);
  AutoEmbedControl((*docStrucHdl)->checkboxRulersRef,windowRef);
  AutoEmbedControl((*docStrucHdl)->checkboxGridSnapRef,windowRef);

  SetControlData((*docStrucHdl)->buttonDefaultRef,kControlEntireControl,
                  kControlPushButtonDefaultTag,sizeof(booleanData),&booleanData);

  controlFontStyleStruc.flags = kControlUseFontMask;
  controlFontStyleStruc.font = kControlFontSmallSystemFont;
  SetControlFontStyle((*docStrucHdl)->buttonLeftIconRef,&controlFontStyleStruc);
  controlFontStyleStruc.font = kControlFontSmallBoldSystemFont;
  SetControlFontStyle((*docStrucHdl)->buttonRightIconRef,&controlFontStyleStruc);

  DeactivateControl((*docStrucHdl)->checkboxRulersRef);
}

// ********************************************************************************** doEvents

void  doEvents(EventRecord *eventStrucPtr)
{
  SInt32        menuChoice;
  MenuID        menuID;
  MenuItemIndex menuItem;

  switch(eventStrucPtr->what)
  {
    case kHighLevelEvent:
      AEProcessAppleEvent(eventStrucPtr);
      break;

    case keyDown:
      if((eventStrucPtr->modifiers & cmdKey) != 0)
      {
        menuChoice = MenuEvent(eventStrucPtr);
        menuID = HiWord(menuChoice);
        menuItem = LoWord(menuChoice);
        if(menuID == mFile && menuItem  == iQuit)
          gDone = true;
      }
      break;

    case mouseDown:
      doMouseDown(eventStrucPtr);
      break;

    case updateEvt:
      doUpdate(eventStrucPtr);
      break;

    case activateEvt:
      doActivate(eventStrucPtr);
      break;

    case osEvt:
      doOSEvent(eventStrucPtr);
      break;
  }
}

// ******************************************************************************* doMouseDown

void  doMouseDown(EventRecord *eventStrucPtr)
{
  WindowPartCode partCode, zoomPart;
  WindowRef      windowRef;
  Rect           constraintRect, mainScreenRect, portRect;
  BitMap         screenBits;
  Point          standardStateHeightAndWidth;

  partCode = FindWindow(eventStrucPtr->where,&windowRef);
  
  switch(partCode)
  {
    case inMenuBar:
      doMenuChoice(MenuSelect(eventStrucPtr->where));
      break;

    case inContent:
      if(windowRef != FrontWindow())
        SelectWindow(windowRef);
      else
        doInContent(eventStrucPtr,windowRef);
      break;

    case inDrag:
      DragWindow(windowRef,eventStrucPtr->where,NULL);
      break;

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

    case inGrow:
      constraintRect.top   = 341; 
      constraintRect.left = 287;
      constraintRect.bottom = constraintRect.right = 32767;
      ResizeWindow(windowRef,eventStrucPtr->where,&constraintRect,NULL);
      doAdjustScrollBars(windowRef);
      doDrawMessage(windowRef,true);
      break;

    case inZoomIn:
    case inZoomOut:
      mainScreenRect = GetQDGlobalsScreenBits(&screenBits)->bounds;
      standardStateHeightAndWidth.v = mainScreenRect.bottom - 75;
      standardStateHeightAndWidth.h = 600;

      if(IsWindowInStandardState(windowRef,&standardStateHeightAndWidth,NULL))
        zoomPart = inZoomIn;
      else
        zoomPart = inZoomOut;

      if(TrackBox(windowRef,eventStrucPtr->where,partCode))
      {
        GetWindowPortBounds(windowRef,&portRect);
        EraseRect(&portRect);
        ZoomWindowIdeal(windowRef,zoomPart,&standardStateHeightAndWidth);
        doAdjustScrollBars(windowRef);
      }
      break;
  }
}

// ****************************************************************************** doMenuChoice

void  doMenuChoice(SInt32 menuChoice)
{
  MenuID         menuID;
  MenuItemIndex  menuItem;
  MenuRef        menuRef;
  WindowRef      windowRef;
  docStrucHandle docStrucHdl;

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

  if(menuID == 0)
    return;

  switch(menuID)
  {
    case mAppleApplication:
      if(menuItem == iAbout)
        SysBeep(10);
      break;

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

    case mDemonstration:
      menuRef = GetMenuRef(mDemonstration);
      windowRef = FrontWindow();
      docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));

      if(menuItem == iColour)
      {
        if(IsControlVisible((*docStrucHdl)->groupboxColourRef))
        {
          HideControl((*docStrucHdl)->groupboxColourRef);
          SetMenuItemText(menuRef,iColour,"\pShow Colour");
        }
        else
        {
          ShowControl((*docStrucHdl)->groupboxColourRef);
          SetMenuItemText(menuRef,iColour,"\pHide Colour");
        }
      }
      else if(menuItem == iGrids)
      {
        if(IsControlActive((*docStrucHdl)->groupboxGridsRef))
        {
          DeactivateControl((*docStrucHdl)->groupboxGridsRef);
          SetMenuItemText(menuRef,iGrids,"\pActivate Grids");
        }
        else
        {
          ActivateControl((*docStrucHdl)->groupboxGridsRef);
          SetMenuItemText(menuRef,iGrids,"\pDeactivate Grids");
        }
      }
      break;
  }

  HiliteMenu(0);
}

// ********************************************************************************** doUpdate

void  doUpdate(EventRecord *eventStrucPtr)
{
  WindowRef windowRef;
  RgnHandle regionHdl;
  
  windowRef = (WindowRef) eventStrucPtr->message;

  BeginUpdate(windowRef);

  SetPortWindowPort(windowRef);
  doDrawMessage(windowRef,!gInBackground);
  
  if(regionHdl = NewRgn())
  {
    GetPortVisibleRegion(GetWindowPort(windowRef),regionHdl);
    UpdateControls(windowRef,regionHdl);
    DisposeRgn(regionHdl);
  }
  
  EndUpdate(windowRef);
}

// ******************************************************************************** doActivate

void  doActivate(EventRecord *eventStrucPtr)
{
  WindowRef windowRef;
  Boolean   becomingActive;

  windowRef = (WindowRef) eventStrucPtr->message;
  becomingActive = ((eventStrucPtr->modifiers & activeFlag) == activeFlag);
  doActivateWindow(windowRef,becomingActive);
}

// ************************************************************************** doActivateWindow

void  doActivateWindow(WindowRef windowRef,Boolean becomingActive)
{
  ControlRef controlRef;

  GetRootControl(windowRef,&controlRef);

  if(becomingActive)
  {
    ActivateControl(controlRef);
    doDrawMessage(windowRef,becomingActive);
  }
  else
  {
    DeactivateControl(controlRef);
    doDrawMessage(windowRef,becomingActive);
  }
}

// ********************************************************************************* doOSEvent

void  doOSEvent(EventRecord *eventStrucPtr)
{
  switch((eventStrucPtr->message >> 24) & 0x000000FF)
  {
    case suspendResumeMessage:
      if((eventStrucPtr->message & resumeFlag) == 1)
      {
        SetThemeCursor(kThemeArrowCursor);
        gInBackground = false;
      }
      else
        gInBackground = true;
      break;
  }
}

// ******************************************************************************* doInContent

void  doInContent(EventRecord *eventStrucPtr,WindowRef windowRef)
{
  docStrucHandle docStrucHdl;
  ControlRef     controlRef;
  SInt16         controlValue, controlPartCode;

  docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));

  SetPortWindowPort(windowRef);
  GlobalToLocal(&eventStrucPtr->where);

  if(controlPartCode = FindControl(eventStrucPtr->where,windowRef,&controlRef))
  {
    if(controlRef == (*docStrucHdl)->popupFixedRef || 
       controlRef == (*docStrucHdl)->popupVariableRef ||
       controlRef == (*docStrucHdl)->popupWinFontRef)
    {
      TrackControl(controlRef,eventStrucPtr->where,(ControlActionUPP) -1);
      controlValue = GetControlValue(controlRef);
      doPopupMenuChoice(windowRef,controlRef,controlValue);
    }
    else if(controlRef == (*docStrucHdl)->scrollbarVertRef) 
    {
      doVertScrollbar(controlPartCode,windowRef,controlRef,eventStrucPtr->where);
    }
    else if(controlRef == (*docStrucHdl)->scrollbarHorizRef)
    {
      TrackControl(controlRef,eventStrucPtr->where,gActionFunctionHorizUPP);
    }
    else
    {
      if(TrackControl(controlRef,eventStrucPtr->where,NULL))
      {
        if(controlRef == (*docStrucHdl)->radiobuttonRedRef ||
           controlRef == (*docStrucHdl)->radiobuttonWhiteRef ||
           controlRef == (*docStrucHdl)->radiobuttonBlueRef)
        {
          doRadioButtons(controlRef,windowRef);
        }
        if(controlRef == (*docStrucHdl)->checkboxGridRef ||
           controlRef == (*docStrucHdl)->checkboxRulersRef ||
           controlRef == (*docStrucHdl)->checkboxGridSnapRef)
        {
          doCheckboxes(controlRef);
        }
        if(controlRef == (*docStrucHdl)->buttonRef ||
           controlRef == (*docStrucHdl)->buttonDefaultRef ||
           controlRef == (*docStrucHdl)->buttonLeftIconRef ||
           controlRef == (*docStrucHdl)->buttonRightIconRef)
        {
          doPushButtons(controlRef,windowRef);
        }
      }
    }
  }  
}

// ************************************************************************* doPopupMenuChoice

void  doPopupMenuChoice(WindowRef windowRef,ControlRef controlRef,SInt16 controlValue)
{
  MenuRef menuRef;
  Size    actualSize;

  GetControlData(controlRef,kControlEntireControl,kControlPopupButtonMenuHandleTag,
                 sizeof(menuRef),&menuRef,&actualSize);
  GetMenuItemText(menuRef,controlValue,gCurrentString);    
  doDrawMessage(windowRef,true);
}

// *************************************************************************** doVertScrollbar

void  doVertScrollbar(ControlPartCode controlPartCode,WindowRef windowRef,
                      ControlRef controlRef,Point mouseXY)
{
  Str255 valueString;

  doCopyPString("\pScroll Bar Control Value: ",gCurrentString);

  switch(controlPartCode)
  {
    case kControlIndicatorPart:
      if(TrackControl(controlRef,mouseXY,NULL))
      {
        NumToString((SInt32) GetControlValue(controlRef),valueString);
        doConcatPStrings(gCurrentString,valueString);
        doDrawMessage(windowRef,true);
      }
      break;

    case kControlUpButtonPart:
    case kControlDownButtonPart:
    case kControlPageUpPart:
    case kControlPageDownPart:
      TrackControl(controlRef,mouseXY,gActionFunctionVertUPP);
      break;
  }
}

// ************************************************************************ actionFunctionVert

void  actionFunctionVert(ControlRef controlRef,ControlPartCode controlPartCode)
{
  SInt16    scrollDistance, controlValue;
  Str255    valueString;
  WindowRef windowRef;

  doCopyPString("\pScroll Bar Control Value: ",gCurrentString);

  if(controlPartCode)
  {
    switch(controlPartCode)
    {
      case kControlUpButtonPart:
      case kControlDownButtonPart:
        scrollDistance = 2;
        break;

      case kControlPageUpPart:
      case kControlPageDownPart:
        scrollDistance = 55;
        break;
    }

    if((controlPartCode == kControlDownButtonPart) || 
       (controlPartCode == kControlPageDownPart))
      scrollDistance = -scrollDistance;

    controlValue = GetControlValue(controlRef);
    if(((controlValue == GetControlMaximum(controlRef)) && scrollDistance < 0) || 
       ((controlValue == GetControlMinimum(controlRef)) && scrollDistance > 0))
      return;

    doMoveScrollBox(controlRef,scrollDistance);

    NumToString((SInt32) GetControlValue(controlRef),valueString);
    doConcatPStrings(gCurrentString,valueString);
    windowRef = GetControlOwner(controlRef);
    doDrawMessage(windowRef,true);
  }
}

// *********************************************************************** actionFunctionHoriz

void  actionFunctionHoriz(ControlRef controlRef,ControlPartCode controlPartCode)
{
  SInt16    scrollDistance, controlValue;
  Str255    valueString;
  WindowRef windowRef;

  doCopyPString("\pScroll Bar Control Value: ",gCurrentString);

  if(partCode != kControlNoPart)
  {
    if(controlPartCode != kControlIndicatorPart)
    {      
      switch(controlPartCode)
      {
        case kControlUpButtonPart:
        case kControlDownButtonPart:
          scrollDistance = 2;
          break;

        case kControlPageUpPart:
        case kControlPageDownPart:
          scrollDistance = 55;
          break;
      }
    
      if((controlPartCode == kControlDownButtonPart) || 
         (controlPartCode == kControlPageDownPart))
        scrollDistance = -scrollDistance;

      controlValue = GetControlValue(controlRef);
      if(((controlValue == GetControlMaximum(controlRef)) && scrollDistance < 0) || 
         ((controlValue == GetControlMinimum(controlRef)) && scrollDistance > 0))
        return;

      doMoveScrollBox(controlRef,scrollDistance);
    }

    NumToString((SInt32) GetControlValue(controlRef),valueString);
    doConcatPStrings(gCurrentString,valueString);
    windowRef = GetControlOwner(controlRef);
    doDrawMessage(windowRef,true);
  }
}

// *************************************************************************** doMoveScrollBox

void doMoveScrollBox(ControlRef controlRef,SInt16 scrollDistance)
{
  SInt16 oldControlValue, controlValue, controlMax;

  oldControlValue = GetControlValue(controlRef);
  controlMax = GetControlMaximum(controlRef);

  controlValue = oldControlValue - scrollDistance;
  
  if(controlValue < 0)
    controlValue = 0;
  else if(controlValue > controlMax)
    controlValue = controlMax;

  SetControlValue(controlRef,controlValue);
}

// **************************************************************************** doRadioButtons

void  doRadioButtons(ControlRef controlRef,WindowRef windowRef)
{  
  docStrucHandle docStrucHdl;

  docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));

  SetControlValue((*docStrucHdl)->radiobuttonRedRef,kControlRadioButtonUncheckedValue);
  SetControlValue((*docStrucHdl)->radiobuttonWhiteRef,kControlRadioButtonUncheckedValue);
  SetControlValue((*docStrucHdl)->radiobuttonBlueRef,kControlRadioButtonUncheckedValue);
  SetControlValue(controlRef,kControlRadioButtonCheckedValue);
}

// ****************************************************************************** doCheckboxes

void  doCheckboxes(ControlRef controlRef)
{
  SetControlValue(controlRef,!GetControlValue(controlRef));
}

// ***************************************************************************** doPushButtons

void  doPushButtons(ControlRef controlRef,WindowRef windowRef)
{
  docStrucHandle docStrucHdl;

  docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));

  if(controlRef == (*docStrucHdl)->buttonRef)
  {
    doCopyPString("\pButton",gCurrentString);
    doDrawMessage(windowRef,true);
  }
  else if(controlRef == (*docStrucHdl)->buttonDefaultRef)
  {
    doCopyPString("\pDefault Button",gCurrentString);
    doDrawMessage(windowRef,true);
  }
  else if(controlRef == (*docStrucHdl)->buttonLeftIconRef)
  {
    doCopyPString("\pLeft Icon Button",gCurrentString);
    doDrawMessage(windowRef,true);
  }
  else if(controlRef == (*docStrucHdl)->buttonRightIconRef)
  {
    doCopyPString("\pRight Icon Button",gCurrentString);
    doDrawMessage(windowRef,true);
  }
}

// ************************************************************************ doAdjustScrollBars

void  doAdjustScrollBars(WindowRef windowRef)
{
  Rect           portRect;
  docStrucHandle docStrucHdl;
      
  docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));

  GetWindowPortBounds(windowRef,&portRect);

  HideControl((*docStrucHdl)->scrollbarVertRef);
  HideControl((*docStrucHdl)->scrollbarHorizRef);

  MoveControl((*docStrucHdl)->scrollbarVertRef,portRect.right - kScrollBarWidth,
              portRect.top + 25);
  MoveControl((*docStrucHdl)->scrollbarHorizRef,portRect.left -1,
              portRect.bottom - kScrollBarWidth);

  SizeControl((*docStrucHdl)->scrollbarVertRef,16, portRect.bottom - 39);
  SizeControl((*docStrucHdl)->scrollbarHorizRef, portRect.right - 13,16);

  ShowControl((*docStrucHdl)->scrollbarVertRef);
  ShowControl((*docStrucHdl)->scrollbarHorizRef);
  
  SetControlMaximum((*docStrucHdl)->scrollbarVertRef,portRect.bottom - portRect.top - 25
                    - kScrollBarWidth);
  SetControlMaximum((*docStrucHdl)->scrollbarHorizRef,portRect.right - portRect.left 
                    - kScrollBarWidth);
}

// ***************************************************************************** doDrawMessage

void  doDrawMessage(WindowRef windowRef,Boolean inState)
{
  Rect        portRect, headerRect;
  CFStringRef stringRef;
  Rect        textBoxRect;

  if(windowRef == gWindowRef)
  {
    SetPortWindowPort(windowRef);

    GetWindowPortBounds(windowRef,&portRect);

    SetRect(&headerRect,portRect.left - 1,portRect.top - 1,portRect.right + 1,
          portRect.top + 26);
    DrawThemeWindowHeader(&headerRect,inState);

    if(inState == kThemeStateActive)
      TextMode(srcOr);
    else
      TextMode(grayishTextOr);

    stringRef = CFStringCreateWithPascalString(NULL,gCurrentString,
                                                 kCFStringEncodingMacRoman);
    SetRect(&textBoxRect,portRect.left,6,portRect.right,21);
    DrawThemeTextBox(stringRef,kThemeSmallSystemFont,0,true,&textBoxRect,teJustCenter,NULL);
    if(stringRef != NULL)
      CFRelease(stringRef);

    TextMode(srcOr);
  }
}

// ************************************************************************** doConcatPStrings

void  doConcatPStrings(Str255 targetString,Str255 appendString)
{
  SInt16 appendLength;

  appendLength = MIN(appendString[0],255 - targetString[0]);

  if(appendLength > 0)
  {
    BlockMoveData(appendString+1,targetString+targetString[0]+1,(SInt32) appendLength);
    targetString[0] += appendLength;
  }
}

// ***************************************************************************** doCopyPString

void  doCopyPString(Str255 sourceString,Str255 destinationString)
{
  SInt16 stringLength;

  stringLength = sourceString[0];
  BlockMove(sourceString + 1,destinationString + 1,stringLength);
  destinationString[0] = stringLength;
}

// ********************************************************************************** helpTags

void  helpTags(WindowRef windowRef)
{
  docStrucHandle   docStrucHdl;
  HMHelpContentRec helpContent;

  memset(&helpContent,0,sizeof(helpContent));

  docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));

  HMSetTagDelay(500);
  HMSetHelpTagsDisplayed(true);
  helpContent.version = kMacHelpVersion;
  helpContent.tagSide = kHMOutsideTopCenterAligned;

  helpContent.content[kHMMinimumContentIndex].contentType = kHMStringResContent;
  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmResID = 128;

  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 1;
  HMSetControlHelpContent((*docStrucHdl)->popupFixedRef,&helpContent);

  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 2;
  HMSetControlHelpContent((*docStrucHdl)->popupVariableRef,&helpContent);

  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 3;
  HMSetControlHelpContent((*docStrucHdl)->popupWinFontRef,&helpContent);

  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 4;
  HMSetControlHelpContent((*docStrucHdl)->groupboxColourRef,&helpContent);

  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 5;
  HMSetControlHelpContent((*docStrucHdl)->groupboxGridsRef,&helpContent);

  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 6;
  HMSetControlHelpContent((*docStrucHdl)->buttonRef,&helpContent);

  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 7;
  HMSetControlHelpContent((*docStrucHdl)->buttonDefaultRef,&helpContent);

  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 8;
  HMSetControlHelpContent((*docStrucHdl)->buttonLeftIconRef,&helpContent);

  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 9;
  HMSetControlHelpContent((*docStrucHdl)->buttonRightIconRef,&helpContent);

  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 10;
  HMSetControlHelpContent((*docStrucHdl)->scrollbarVertRef,&helpContent);

  helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 11;
  HMSetControlHelpContent((*docStrucHdl)->scrollbarHorizRef,&helpContent);
}

// *******************************************************************************************

Demonstration Program Controls1 Comments

When this program is run, the user should:

o On Mac OS 8/9, choose Show Balloons from the Help menu and peruse the help balloons which are
  invoked when the mouse cursor is moved over the various controls.

o On Mac OS X, peruse the Help tags which are invoked when the mouse cursor is held over the
  various controls.

o Choose items from each of the pop-up menu buttons, noting that the chosen item is displayed
  in the window header.

o Click on the radio buttons, checkboxes, and push buttons, noting particularly that the radio
  button settings are mutually exclusive and that checkbox settings are not.

o Click in the scroll bar arrows and gray areas/tracks of the scroll bars, noting the control
  value changes displayed in the window header.

o Drag the scroll box/scroller of the vertical scroll bar (which uses the non-live-feedback
  CDEF variant), noting that only a ghosted outline is dragged and that the control value does
  not change until the mouse button is released.

o Drag the scroll box/scroller of the horizontal scroll bar (which uses the live-feedback CDEF
  variant), noting that the scroll box proper is dragged and that the control value is
  continually updated during the drag.

o Resize and zoom the window, noting (1) that the scroll bars are moved and resized in response
  to those actions and (2) the change in the maximum value of the scroll bars.

o Send the program to the background and bring it to the foreground, noting the changes to the
  appearance of the controls.  (The program activates and deactivates the root control only;
  however, because all controls are embedded in the root control, all controls are activated 
  and deactivated along with the root control.)

o Alternately hide and show the Colour primary group box by choosing the associated item in the
  Demonstration menu.  (The program hides and shows the primary group box only; however, 
  because the radio buttons are embedded in the primary group box, those controls hidden and 
  shown along with the primary group box.)

o Alternately activate and deactivate the Grids primary group box by choosing the associated
  item in the Demonstration menu.  (The program activates and deactivates the primary group box
  only; however, because the checkboxes are embedded in the primary group box, those controls 
  are activated and deactivated along with the primary group box.)  Also note the latency of 
  the Show Rulers checkbox.  It is deactivated at program launch, and retains that status when 
  the primary group box is deactivated and then re-activated.
In this program (and all others that use Help tags), the header file string.h is included and
the library MSL C.PPC.Lib has been added to the project because of the use of memset in the
function helpTags.

If you wish to display the Help tags, rather than Balloon help, on Mac OS 8/9:

o Comment out the line "if(gRunningOnX)" before the line "helpTags(gWindowRef);"

o In the function helpTags, change "kHMOutsideTopCenterAligned" to
  "kHMOutsideLeftCenterAligned".  (At the time of writing, use of any but the first thirteen
  positioning constants in MacHelp.h defeated the display of Help tags on Mac OS 8/9.)

Help tag creation is addressed at Chapter 25.

defines

Constants are established for 'CNTL' resources for those controls not created programmatically.

typedef

The data type docStruc is a structure comprising fields in which the references to the control
objects for the various controls will be stored.  A handle to this structure will be stored in
the window's window object.

Global Variables

actionFunctionVertUPP and actionFunctionHorizUPP will be assigned universal procedure pointers
to action functions for the scroll bars.

main

Universal procedure pointers are created for the action functions for the two scroll bars.

The call to the function copyPString causes the string in the first parameter to be copied to
the global variable gCurrentString.  The string in gCurrentString, which will be changed at
various points in the code, will be drawn in the window header frame.

The next block opens a window, makes the window's graphics port the current port, and sets the
graphics port's font to the small system font.  This latter is because one of the pop-up menus
will use the window font.  SetThemeWindowBackground is called to set the background
colour/pattern for the window.  The window's background will be similar to that applying to Mac
OS 8/9 dialogs, which is appropriate for a window containing nothing but controls.

The call to NewHandle gets a relocatable block the size of one docStruc structure.  The handle
to the block is stored in the window's window object by the call to SetWRefCon.

In the next block, doGetControls creates and draws the controls, doAdjustScrollBars resizes and
locates the scroll bars, and sets their maximum value, according to the dimensions of the
window's port rectangle, and ShowWindow makes the window visible.

Note that error handling here and in other areas of this demonstration program is somewhat
rudimentary.  In the unlikely event that certain calls fail, ExitToShell is called to terminate
the program.

doGetControls

The function doGetControls creates the controls.  

At the first line, if the program is running on Mac OS 8/9, CreateRootControl is called to
create a root control for the window.  On Mac OS 8/9, the first control created must be always
be the root control (which is implemented as a user pane).  This call is not necessary on Mac
OS X because, on Mac OS X, root controls are created automatically for windows which have at
least one control.

A handle to the structure in which the references to the control objects will be stored is then
retrieved.

The controls are then created, some from 'CNTL' resources using GetNewControl and some
programmatically.  All of these calls create a control object for the relevant control and
insert the object into the control list for the specified window.  GetNewControl draws the
controls created from 'CNTL' resources.  In the case of controls created programmatically,
ShowControl must me called to cause the control to be drawn.  The reference to each control
object is assigned to the appropriate field of the window's "document" structure.

Because of the sequence in which the controls are created and initially drawn, the group boxes
would ordinarily over-draw the radio buttons and checkboxes.  However, the calls to
AutoEmbedControl embed these latter controls in their respective group boxes, ensuring that
they will be drawn after (or "on top of") the group boxes.  (AutoEmbedControl, rather than
EmbedControl, is used in this instance because the radio button rectangles are visually
contained by their respective group box rectangles.)

The call to SetControlData, with kControlPushButtonDefaultTag passed in the third parameter
causes the default outline to be drawn around the specified push button.

In the next block, the title fonts of the left colour icon variant and right colour	icon
variant push buttons are changed.  Firstly, the flags and font fields of a control font style
structure are assigned constants so that the following call to SetControlFontStyle will set the
title font of the left colour icon variant push button to the small system font.  The font
field is then changed so that the second call to SetControlFontStyle will set the title font of
the right colour icon variant push button to the small emphasized system font.

Lastly, the checkbox titled Rulers is disabled.  This is for the purpose of the latency aspect
of the demonstration.

doMouseDown

doMouseDown switches according to the window part in which a mouseDown event occurs.

At the inContent case, if the window in which the mouse-down occurred is the front window, and
since all of the controls are located in the window's content region, a call to the function
doInContent is made.

The inGrow case is of particular significance to the scroll bars.  Following the call to
ResizeWindow, the function doAdjustScrollBars is called to erase, move, resize, and redraw the
scroll bars and reset the control's maximum value according to the new size of the window. 
(The call to doDrawMessage is incidental to the demonstration.  It simply redraws the window
header frame and text in the window.)

The inZoomIn/InZoomOut case is also of significance to the scroll bars.  If the call to
TrackBox returns a non-zero value, the window's port rectangle is erased before ZoomWindowIdeal
zooms the window.  Following the call to ZoomWindowIdeal the function doAdjustScrollBars is
called to hide, move, resize, and redraw the scroll bars.

doMenuChoice

doMenuChoice handles user choices from the pull-down menus.

The mDemonstration case handles the Demonstration menu.  Firstly, reference to that menu and a
handle to the window's "document" structure are obtained.  

If the menu item is the Colour item, IsControlVisible is called to determine the current
visibility status of the Colour group box.  If it is visible, the call to HideControl hides the
group box and its embedded radio buttons; also, the menu item text is changed to "Show Colour". 
If it is not visible, ShowControl is called and the menu item text is changed to "Hide Colour".

If the menu item is the Grids item, the same general sequence takes place in respect of the
Grids group box.  This time, however, IsControlActive is used to determine whether the control
is active or inactive, and ActivateControl and DeactivateControl are called, and the menu item
toggled, as appropriate.  Note that, because of latency, the application does not have to
"remember" that one of the embedded checkboxes was deactivated at program start.  The Control
Manager does the remembering.

doUpdate

doUpdate is called whenever the application receives an update event for its window.  Between
the usual calls to BeginUpdate and EndUpdate (ignored on Mac OS X), the window's graphics port
is set as the current port for drawing, and UpdateControls is called to draw those controls
intersecting the current visible region (which, between the BeginUpdate and EndUpdate calls,
equates to the Mac OS 8/9 update region).  The line preceding the if block is incidental to the
demonstration.  It simply redraws the window header frame and text in the window.

doActivateWindow

doActivateWindow switches according to whether the specified window is becoming active or is
about to be made inactive.  (Actually, doActivateWindow will never be called by doActivate in
this program because the program only opens one window.  It will however, be called by the
function doOSEvent.)

At the first line, GetRootControl gets a reference to the window's root control.

If the window is becoming active, ActivateControl is called to activate the root control. 
Since all other controls are embedded in the root control, all controls will be activated by
this call.

If the window is about to become inactive, DeactivateControl is called to deactivate the root
control.  Since all other controls are embedded in the root control, all controls will be
deactivated by this call.

The calls to doDrawMessage are incidental to the demonstration.  They simply redraw the window
header frame and text in the window in the appropriate mode (inactive or active).

doOSEvent

doOSEvent handles operating system events.  If the event is a suspend or resume event, a global
variable is then set to indicate whether the program has come to the foreground or has been
sent to the background.  This global variable is used in doUpdate, and controls the colour in
which the text in the window header is drawn.

doInContent

doInContent further processes mouse-down events in the content region.  Since the content
region of the window contains nothing but controls, this function is really just the main
switching point for the further handling of those controls.

The first line gets the handle to the "document" structure containing the references to the
various control objects.  The call to SetPortWindowPort is a necessary precursor to the call to
GlobalToLocal, which converts the mouse coordinates in the event structure's where field from
global coordinates to the local coordinates required in the following call to FindControl. 
(FindControl is used here rather than the newer function FindControlUnderMouse because there is
no requirement to get a reference to a control even if no part was hit and no requirement to
determine whether a mouse-down event has occurred in a deactivated control.)

If there is a control at the cursor location at which the mouse button is released, the control
reference returned by the FindControl call is first compared with the references to the pop-up
menu controls stored in the window's "document" structure.  If a match is found, TrackControl
is called with (ControlActionUPP) -1 passed in the actionProc parameter so as to cause an
action function within the control's CDEF to be repeatedly invoked while the mouse button
remains down.  When TrackControl returns, the control value is obtained by a call to
GetControlValue and a function is called to perform further handling

Note that TrackControl, rather than the newer function HandleControlClick, is used in this
program because none of the controls require modifier keys to be passed in.  (Of course,
HandleControlClick would work just as well (with 0 passed in the inModifiers parameter).)

If the control reference returned by FindControl does not match the pop-up controls'
references, it is then tested against the references to the vertical and horizontal scroll bar
controls.  If it matches the reference to the vertical scroll bar (which uses the
non-live-feedback CDEF variant), the function doVertScrollbar is called to perform further
handling.  If it matches the reference to the horizontal scroll bar (which uses the
live-feedback CDEF variant), TrackControl is called (ControlActionUPP) Đ1 passed in the
actionProc parameter.  This latter is because the UPP to the control action function has
already been set.  (Recall that it was passed in a parameter of the CreateScrollBarControl call
which created the control.)  The net effect of is that the application-defined action function
to which the UPP relates will be repeatedly called while the mouse button remains down.

If the reference returned by FindControl does not match the references to any of the pop-up
menu buttons or scroll bars, it must be a reference to one of the other controls.  In this
case, TrackControl is called, with the procPtr field set to that required for push buttons,
radio buttons, and checkboxes (that is, NULL).  If the cursor is still within the control when
the mouse button is released, the reference is compared to, in sequence, the references to the
radio buttons, the checkboxes, and the push buttons.  If a match is found, the appropriate
function is called to perform further handling.

doPopupMenuChoice

doPopupMenuChoice further handles mouse-downs in the pop-up menu buttons.  In this
demonstration, the further handling that would normally take place here is replaced by simply
drawing the menu item text in the window.

The call to GetControlData gets a reference to the control's menu, allowing GetMenuItemText to 
get the item text into a global variable.  This allows the text to be drawn in the window
header frame.

doVertScrollbar

doVertScrollbar is called from doInContent in the case of a mouse-down in the vertical scroll
bar (which uses the non-live-feedback variant of the CDEF).

The call to copyPString is incidental to the demonstration.  It simply copies some appropriate
text to the global variable gCurrentString.

At the next line, the function switches on the control part code.  If the control part code was
the scroll box/scroller (that is, the "indicator"), TrackControl is called with the procPtr
parameter set to that required for the scroll box of non-live-feedback scroll bars (that is,
NULL).  If the user did not move the cursor outside the control before releasing it, the if
block executes, retrieving the new control value, converting it to a string, appending that
string to the string currently in gCurrentString, and drawing gCurrentString in the window
header.  (In a real application, calculation of the distance and direction to scroll, and the
scrolling itself, would take place inside this if block.)

If the mouse down was in the gray area/track or one of the scroll arrows, TrackControl is
called with a Universal Procedure Pointer (UPP) passed in the actionProc parameter.  The effect
of this is that the application-defined action function to which the UPP relates will be
repeatedly called while the mouse button remains down.

                                      ACTION FUNCTIONS

An action function is an example of a "callback function" (sometimes called a "hook function"). 
A callback function is an application-defined function that is called by a Toolbox function
during the Toolbox function's execution, thus extending the features of the Toolbox function.

actionFunctionVert

actionFunctionVert is the action function called by TrackControl at the bottom of
doVertScrollbar.  Because it is repeatedly called by TrackControl while the mouse button
remains down, the scrolling such a function would perform in a real application continues
repeatedly until the mouse button is released (provided the cursor remains within the scroll
arrow or gray area/track).

The call to copyPString is incidental to the demonstration.  It simply copies some appropriate
text to the global variable gCurrentString.

The if(controlPartCode) line ensures that, if the cursor is not still inside the scroll arrow
or gray area/track, the action function exits and all scrolling ceases until the user brings
the cursor back within the scroll arrow or gray area/track, causing a non-zero control part
code to be again received.  The following occurs only when the cursor is within the control.

The function switches on the control part code.  If the mouse-down is in a scroll arrow, the
variable scrollDistance is set to 2.  If it is in a gray area, scrollDistance is set to 55. 
(In this simple demonstration, these are just arbitrary values.  In a real application, you
would assign an appropriate value in the case of the scroll arrows, and assign a calculated
value (based primarily on current window height) in the case of the gray areas/tracks.)

The next block convert the value in scrollDistance to the required negative value if the user
is scrolling down rather than up.

The next block defeats any further scrolling action if, firstly, the down scroll arrow is being
used and the "document" is at the maximum scrolled position or, secondly, the up scroll arrow
is being used and the "document" is at the minimum scrolled position.

The distance to scroll having been set, the call to the function doMoveScrollBox moves the
scroll box/scroller the appropriate distance in the appropriate direction and updates the
control's value accordingly.  This means, of course, that the scroll box/scroller is being
continually moved, and the control's value continually updated, while the mouse button remains
down.

In this demonstration, the remaining action is to retrieve the current value of the control,
convert it to a string, append it to the string currently in gCurrentString, and draw
gCurrentString in the window header frame.  (In a real application, the actual scrolling of the
window's contents would be effected here.)

actionFunctionHoriz

actionFunctionHoriz is the action function passed in the actionProc parameter of the
TrackControl call in doInContent arising from a mouse-down in the horizontal scroll bar.  This
action function differs from that for the vertical scroll bar because the horizontal scroll bar
uses the live-feedback variant of the CDEF.

The principal differences are that action functions for live-feedback scroll bars must
continually scroll the window's contents, not only while the mouse button remains down in the
scroll arrows and gray areas, but also while the scroll box/scroller is being dragged. 
Accordingly, this function, unlike the action function for the vertical scroll bar, is also
called while the mouse button remains down in the scroll box/scroller.

The call to copyPString is incidental to the demonstration.  It simply copies some appropriate
text to the global variable gCurrentString.

If the mouse-down occurred in the scroll box/scroller, the code which sets up the scroll
distance, adjusts the sign of the scroll distance according to whether the scroll is left or
right, prevents scrolling beyond the minimum and maximum scroll values, and calls
doMoveScrollBox to move the scroll box/scroller and update the control's value, is bypassed. 
The call to doMoveScrollBox is bypassed because, in live-feedback, the CDEF moves the scroll
box/scroller and updates the control's value when the mouse-down is in the scroll box/scroller.

In this demonstration, the action taken after the main block of code has been bypassed
(mouse-down in the scroll box/scroller) or executed (mouse-down in the scroll arrows or gray
area/track) is to retrieve the current value of the control, convert it to a string, append it
to the string currently in gCurrentString, and draw gCurrentString in the window header frame. 
(In a real application, the actual scrolling of the window's contents would be effected here.)

doMoveScrollBox

doMoveScrollBox is called from within the action functions to reset the control's current value
to reflect the scrolled distance, and to reposition the scroll box/scroller accordingly.

The first two lines retrieve the control's current value and maximum value.  The next line
calculates the new control value by subtracting the distance to scroll received from the
calling action function from the current control value.  The next four lines prevent the
control's value from being set lower or higher than the control's minimum and maximum values
respectively.  The call to SetControlValue sets the new control value and repositions the
scroll box/scroller.

doRadioButtons

doRadioButtons is called when the mouse-down is within a radio button.  The first three calls
to SetControlValue set all radio buttons to the off state.  The final call sets the radio
button under the mouse to the on state.

doCheckboxes

doCheckboxes is called when the mouse-down is within a checkbox.  The single line simply
toggles the current value of the control.

doPushButtons

doPushButtons is called when the mouse-down is within a push button.  In this demonstration,
the only action taken is to draw the identity of the push button in the window header frame.

doAdjustScrollBars

doAdjustScrollBars is called if the user resizes or zooms the window.

At the first line, a handle to the window's "document" structure is retrieved from the window
object.

At the next line, the coordinates representing the window's current content region are assigned
to a Rect variable which will be used in calls to MoveControl and SizeControl.

Amongst other things, MoveControl and SizeControl both redraw the specified scroll bar.  Since
SizeControl will be called immediately after MoveControl, this will causes a very slight
flickering of the scroll bars.  To prevent this, the scroll bars will be hidden while these two
functions are executing.

The calls to HideControl hide the scroll bars.  The calls to MoveControl erase the scroll bars,
offset the contrlRect fields of their control structures, and redraw the scroll bars within the
offset rectangle.  SizeControl hides the scroll bars (in this program they are already hidden),
adjusts the contrlRect fields of their control structures, and redraws the scroll bars within
their new rectangles.  The calls to ShowControl then show the scroll bars.

In this demonstration, the remaining lines set the new maximum values for the scroll bars
according to the new height and width of the window.  No attempt is made to calculate the
required new control value to ensure that the (non-existent) document remains in the same
scrolled position after the zoom or resize.  In a real application, this, plus the calculation
of the maximum value according to, for example, the line height of text content as well as the
new window height, are matters that would need to be attended to in this function.

Demonstration Program Controls2

// *******************************************************************************************
// Controls2.c                                                             CLASSIC EVENT MODEL
// *******************************************************************************************
// 
// This program:
//
// o  Opens a kWindowDocumentProc window with a two horizontal scroll bars, each of which
//    relates to the picture displayed immediately above it.
//
// o  Allows the user to horizontally scroll the pictures within the window using the scroll
//    box/scroller, the scroll arrows and the gray area/track of each scroll bar.  
//
// The top scroll bar uses the non-live-feedback variant of the scroll bar CDEF.  The bottom
// scroll bar uses the live-feedback variant.    
//
// With regard to the scroll bars, the principal differences between this program and 
// Controls1 are that, in this program:
//
// o  The scroll bar scroll boxes are made proportional.
//
// o  The action functions are set using the function SetControlAction.
//
// o  References to the scroll bar controls are not stored in, and retrieved from, a document
//    structure associated with the window.  Instead,  each control is assigned a controlID 
//    using SetControlID, allowing the ID of the control to be retrieved using GetControlID
//    and a reference to the control to be obtained using GetControlByID. 
//
// The program utilises the following resources:
//
// o  A 'plst' resource.
//
// o  An 'MBAR' resource, and 'MENU' resources for Apple, File and Edit (preload, non-
//    purgeable).
//
// o  A 'WIND' resource (purgeable) (initially visible).
//
// o  Two 'CNTL' resource for the horizontal scroll bars (purgeable).
//
// o  'PICT' resources containing the pictures to be scrolled (non-purgeable).
//
// o  A 'SIZE' resource with the acceptSuspendResumeEvents, canBackground, 
//    doesActivateOnFGSwitch, and isHighLevelEventAware flags set.
//
// *******************************************************************************************

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

#include <Carbon.h>

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

#define rMenubar            128
#define rNewWindow          128
#define rPictureNonLive     128
#define rPictureLive        129
#define mAppleApplication   128
#define  iAbout             1
#define mFile               129
#define  iQuit              12
#define cScrollbarNonLive   128
#define cScrollbarLive      129
#define kScrollbarNonLiveID 1
#define kScrollbarLiveID    2
#define MAX_UINT32          0xFFFFFFFF

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

ControlActionUPP gActionFuncNonLiveUPP;
ControlActionUPP gActionFuncLiveUPP;
Boolean          gDone;
Rect             gPictRectNonLive, gPictRectLive;
PicHandle        gPictHandleNonLive, gPictHandleLive ;

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

void  main                (void);
void  doPreliminaries     (void);
OSErr quitAppEventHandler (AppleEvent *,AppleEvent *,SInt32);
void  doEvents            (EventRecord *);
void  doMouseDown         (EventRecord *);
void  doUpdate            (EventRecord *);
void  doActivate          (EventRecord *);
void  doActivateWindow    (WindowRef,Boolean);
void  doOSEvent           (EventRecord *);
void  doMenuChoice        (SInt32);
void  doInContent         (EventRecord *,WindowRef);
void  doNonLiveScrollBars (ControlPartCode,WindowRef,ControlRef,Point);
void  actionFuncNonLive   (ControlRef,ControlPartCode);
void  actionFuncLive      (ControlRef,ControlPartCode);
void  doMoveScrollBox     (ControlRef,SInt16);

// ************************************************************************************** main

void  main(void)
{
  MenuBarHandle menubarHdl;
  SInt32        response;
  MenuRef       menuRef;
  WindowRef     windowRef;
  ControlRef    controlRefScrollbarNonLive, controlRefScrollbarLive;
  ControlID     controlID;
  Rect          portRect;
  EventRecord   eventStructure;

  // ........................................................................ do preliminaries

  doPreliminaries();

  // ..................................................... create universal procedure pointers

  gActionFuncNonLiveUPP = NewControlActionUPP((ControlActionProcPtr) actionFuncNonLive);
  gActionFuncLiveUPP    = NewControlActionUPP((ControlActionProcPtr) actionFuncLive);

  // ............................................................... set up menu bar and menus

  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    ExitToShell();
  SetMenuBar(menubarHdl);
  DrawMenuBar();

  Gestalt(gestaltMenuMgrAttr,&response);
  if(response & gestaltMenuMgrAquaLayoutMask)
  {
    menuRef = GetMenuRef(mFile);
    if(menuRef != NULL)
    {
      DeleteMenuItem(menuRef,iQuit);
      DeleteMenuItem(menuRef,iQuit - 1);
      DisableMenuItem(menuRef,0);
    }
  }

  // ........................................................................... open a window

  if(!(windowRef = GetNewCWindow(rNewWindow,NULL,(WindowRef)-1)))
    ExitToShell();

  SetPortWindowPort(windowRef);

  // .................................... get controls and set ID and control action functions

  controlRefScrollbarNonLive  = GetNewControl(cScrollbarNonLive,windowRef);
  controlID.signature = 'kjB ';
  controlID.id = kScrollbarNonLiveID;
  SetControlID(controlRefScrollbarNonLive,&controlID);

  SetControlAction(controlRefScrollbarNonLive,gActionFuncNonLiveUPP);
  
  controlRefScrollbarLive = GetNewControl(cScrollbarLive,windowRef);
  controlID.id = kScrollbarLiveID;
  SetControlID(controlRefScrollbarLive,&controlID);

  SetControlAction(controlRefScrollbarLive,gActionFuncLiveUPP);

  // ............................................................................. get picture

  if(!(gPictHandleNonLive = GetPicture(rPictureNonLive)))
    ExitToShell();
  gPictRectNonLive = (*gPictHandleNonLive)->picFrame;

  if(!(gPictHandleLive = GetPicture(rPictureLive)))
    ExitToShell();
  gPictRectLive = (*gPictHandleLive)->picFrame;
  OffsetRect(&gPictRectLive,0,191);

  // .................................................... set up for proportional scroll boxes

  GetWindowPortBounds(windowRef,&portRect);
  SetControlViewSize(controlRefScrollbarNonLive,portRect.right);
  SetControlViewSize(controlRefScrollbarLive,portRect.right);

  // ......................................................................... enter eventLoop

  gDone = false;

  while(!gDone)
  {
    if(WaitNextEvent(everyEvent,&eventStructure,MAX_UINT32,NULL))
      doEvents(&eventStructure);
  }
}

// *************************************************************************** doPreliminaries

void  doPreliminaries(void)
{
  OSErr osError;

  MoreMasterPointers(32);
  InitCursor();
  FlushEvents(everyEvent,0);

  osError = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
                            NewAEEventHandlerUPP((AEEventHandlerProcPtr) quitAppEventHandler),
                            0L,false);
  if(osError != noErr)
    ExitToShell();
}

// **************************************************************************** doQuitAppEvent

OSErr  quitAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
  OSErr    osError;
  DescType returnedType;
  Size     actualSize;

  osError = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,NULL,0,
                              &actualSize);

  if(osError == errAEDescNotFound)
  {
    gDone = true;
    osError = noErr;
  } 
  else if(osError == noErr)
    osError = errAEParamMissed;

  return osError;
}

// ********************************************************************************** doEvents

void  doEvents(EventRecord *eventStrucPtr)
{
  switch(eventStrucPtr->what)
  {
    case kHighLevelEvent:
      AEProcessAppleEvent(eventStrucPtr);
      break;

    case mouseDown:
      doMouseDown(eventStrucPtr);
      break;

    case updateEvt:
      doUpdate(eventStrucPtr);
      break;

    case activateEvt:
      doActivate(eventStrucPtr);
      break;

    case osEvt:
      doOSEvent(eventStrucPtr);
      break;
  }
}

// ******************************************************************************* doMouseDown

void  doMouseDown(EventRecord *eventStrucPtr)
{
  WindowRef      windowRef;
  WindowPartCode partCode;
  
  partCode = FindWindow(eventStrucPtr->where,&windowRef);
  
  switch(partCode)
  {
    case inMenuBar:
      doMenuChoice(MenuSelect(eventStrucPtr->where));
      break;

    case inContent:
      if(windowRef != FrontWindow())
        SelectWindow(windowRef);
      else
        doInContent(eventStrucPtr,windowRef);
      break;

    case inDrag:
      DragWindow(windowRef,eventStrucPtr->where,NULL);
      break;
  }
}

// ********************************************************************************** doUpdate

void  doUpdate(EventRecord *eventStrucPtr)
{
  WindowRef  windowRef;
  RgnHandle  regionHdl;
  ControlID  controlID;
  ControlRef controlRef;

  windowRef = (WindowRef) eventStrucPtr->message;

  BeginUpdate(windowRef);

  SetPortWindowPort(windowRef);

  regionHdl = NewRgn();
  if(regionHdl)
  {
    GetPortVisibleRegion(GetWindowPort(windowRef),regionHdl);
    UpdateControls(windowRef,regionHdl);
    DisposeRgn(regionHdl);
  }

  controlID.signature = 'kjB ';
  controlID.id = kScrollbarNonLiveID;
  GetControlByID(windowRef,&controlID,&controlRef);
  SetOrigin(GetControlValue(controlRef),0);
  DrawPicture(gPictHandleNonLive,&gPictRectNonLive);
  SetOrigin(0,0);

  controlID.id = kScrollbarLiveID;
  GetControlByID(windowRef,&controlID,&controlRef);
  SetOrigin(GetControlValue(controlRef),0);
  DrawPicture(gPictHandleLive,&gPictRectLive);
  SetOrigin(0,0);

  EndUpdate(windowRef);
}

// ******************************************************************************** doActivate

void  doActivate(EventRecord *eventStrucPtr)
{
  WindowRef windowRef;
  Boolean   becomingActive;

  windowRef = (WindowRef) eventStrucPtr->message;
  becomingActive = ((eventStrucPtr->modifiers & activeFlag) == activeFlag);
  doActivateWindow(windowRef,becomingActive);
}

// ************************************************************************** doActivateWindow

void  doActivateWindow(WindowRef windowRef,Boolean becomingActive)
{  
  ControlID  controlID;
  ControlRef controlRefScrollbarNonLive, controlRefScrollbarLive;

  controlID.signature = 'kjB ';
  controlID.id = kScrollbarNonLiveID;
  GetControlByID(windowRef,&controlID,&controlRefScrollbarNonLive);
  controlID.id = kScrollbarLiveID;
  GetControlByID(windowRef,&controlID,&controlRefScrollbarLive);

  if(becomingActive)
  {
    ActivateControl(controlRefScrollbarNonLive);
    ActivateControl(controlRefScrollbarLive);
  }
  else
  {
    DeactivateControl(controlRefScrollbarNonLive);
    DeactivateControl(controlRefScrollbarLive);
  }
}

// ********************************************************************************* doOSEvent

void  doOSEvent(EventRecord *eventStrucPtr)
{
  switch((eventStrucPtr->message >> 24) & 0x000000FF)
  {
    case suspendResumeMessage:
      if((eventStrucPtr->message & resumeFlag) == 1)
        SetThemeCursor(kThemeArrowCursor);
      break;
  }
}

// ****************************************************************************** doMenuChoice

void  doMenuChoice(SInt32 menuChoice)
{
  MenuID        menuID;
  MenuItemIndex menuItem;

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

  if(menuID == 0)
    return;

  switch(menuID)
  {
    case mAppleApplication:
      if(menuItem == iAbout)
        SysBeep(10);
      break;
      
    case mFile:
      if(menuItem == iQuit)
        gDone = true;
      break;
  }

  HiliteMenu(0);
}

// ******************************************************************************* doIncontent

void  doInContent(EventRecord *eventStrucPtr,WindowRef windowRef)
{
  ControlPartCode controlPartCode;
  ControlRef      controlRef;
  ControlID       controlID;

  SetPortWindowPort(windowRef);
  GlobalToLocal(&eventStrucPtr->where);

  if(controlPartCode = FindControl(eventStrucPtr->where,windowRef,&controlRef))
  {
    GetControlID(controlRef,&controlID);

    if(controlID.id == kScrollbarNonLiveID)
      doNonLiveScrollBars(controlPartCode,windowRef,controlRef,eventStrucPtr->where);
    else if(controlID.id == kScrollbarLiveID)
      TrackControl(controlRef,eventStrucPtr->where,(ControlActionUPP) -1);
  }
}

// *********************************************************************** doNonLiveScrollBars

void  doNonLiveScrollBars(ControlPartCode controlPartCode,WindowRef windowRef,
                           ControlRef controlRef,Point mouseXY)
{
  SInt16    oldControlValue;
  SInt16    scrollDistance;
  RgnHandle updateRgnHdl;

  switch(controlPartCode)
  {
    case kControlIndicatorPart:
      oldControlValue = GetControlValue(controlRef);
      if(TrackControl(controlRef,mouseXY,NULL))
      {
        scrollDistance = oldControlValue - GetControlValue(controlRef);
        if(scrollDistance != 0)
        {
          updateRgnHdl = NewRgn();
          ScrollRect(&gPictRectNonLive,scrollDistance,0,updateRgnHdl);
          InvalWindowRgn(windowRef,updateRgnHdl);
          DisposeRgn(updateRgnHdl);
        }
      }
      break;

    case kControlUpButtonPart:
    case kControlDownButtonPart:
    case kControlPageUpPart:
    case kControlPageDownPart:
      TrackControl(controlRef,mouseXY,(ControlActionUPP) -1);
      break;
  }
}

// ************************************************************************* actionFuncNonLive

void  actionFuncNonLive(ControlRef controlRef,ControlPartCode controlPartCode)
{
  WindowRef windowRef;
  SInt16    scrollDistance, controlValue;
  Rect      portRect;           
  RgnHandle updateRgnHdl;

  if(controlPartCode)
  {
    windowRef = GetControlOwner(controlRef);

    switch(controlPartCode)
    {
      case kControlUpButtonPart:
      case kControlDownButtonPart:
        scrollDistance = 2;
        break;

      case kControlPageUpPart:
      case kControlPageDownPart:
        GetWindowPortBounds(windowRef,&portRect);
        scrollDistance = (portRect.right - portRect.left - 10);
        break;
    }

    if((controlPartCode == kControlDownButtonPart) || 
       (controlPartCode == kControlPageDownPart))
      scrollDistance = -scrollDistance;

    controlValue = GetControlValue(controlRef);
    if(((controlValue == GetControlMaximum(controlRef)) && scrollDistance < 0) || 
       ((controlValue == GetControlMinimum(controlRef)) && scrollDistance > 0))
      return;

    doMoveScrollBox(controlRef,scrollDistance);

    if(controlPartCode == kControlUpButtonPart || 
       controlPartCode == kControlDownButtonPart)
    {
      updateRgnHdl = NewRgn();
      ScrollRect(&gPictRectNonLive,scrollDistance,0,updateRgnHdl);
      InvalWindowRgn(windowRef,updateRgnHdl);
      DisposeRgn(updateRgnHdl);
      BeginUpdate(windowRef);
    }

    SetOrigin(GetControlValue(controlRef),0);
    DrawPicture(gPictHandleNonLive,&gPictRectNonLive);
    SetOrigin(0,0);

    if(controlPartCode == kControlUpButtonPart || controlPartCode == kControlDownButtonPart)
      EndUpdate(windowRef);
  }
}

// *************************************************************************** actionFuncLive

void  actionFuncLive(ControlRef controlRef,ControlPartCode partCode)
{
  WindowRef windowRef;
  SInt16    scrollDistance, controlValue;
  Rect      portRect;

  windowRef = GetControlOwner(controlRef);

  if(partCode != 0)
  {
    if(partCode != kControlIndicatorPart)
    {
      switch(partCode)
      {
        case kControlUpButtonPart:
        case kControlDownButtonPart:
          scrollDistance = 2;
          break;

        case kControlPageUpPart:
        case kControlPageDownPart:
          GetWindowPortBounds(windowRef,&portRect);
          scrollDistance = (portRect.right - portRect.left) - 10;
          break;
      }

      if((partCode == kControlDownButtonPart) || (partCode == kControlPageDownPart))
        scrollDistance = -scrollDistance;

      controlValue = GetControlValue(controlRef);
      if(((controlValue == GetControlMaximum(controlRef)) && scrollDistance < 0) || 
         ((controlValue == GetControlMinimum(controlRef)) && scrollDistance > 0))
        return;

      doMoveScrollBox(controlRef,scrollDistance);
    }

    SetOrigin(GetControlValue(controlRef),0);
    DrawPicture(gPictHandleLive,&gPictRectLive);
    SetOrigin(0,0);
  }
}

// *************************************************************************** doMoveScrollBox

void doMoveScrollBox(ControlRef controlRef,SInt16 scrollDistance)
{
  SInt16 oldControlValue, controlValue, controlMax;

  oldControlValue = GetControlValue(controlRef);
  controlMax = GetControlMaximum(controlRef);

  controlValue = oldControlValue - scrollDistance;
  
  if(controlValue < 0)
    controlValue = 0;
  else if(controlValue > controlMax)
    controlValue = controlMax;

  SetControlValue(controlRef,controlValue);
}

// *******************************************************************************************

Demonstration Program Controls2 Comments

This program is basically an extension of the scroll bars aspects of the demonstration program
Controls1 in that, unlike the scroll bars in Controls1, the scroll bars in this program
actually scroll the contents of the window.  Also, this program supports proportional scroll
boxes/scrollers.

When the program is run, the user should scroll the pictures by dragging the scroll
boxes/scrollers, clicking in the scroll bar gray areas/tracks, clicking in the scroll arrows
and holding the mouse button down while the cursor is in the gray areas/travks and scroll
arrows.  The user should note, when scrolling with the scroll boxes/scrollers, that the top
scroll bar uses the non-live-feedback variant of the scroll bar CDEF and the bottom scroll bar
uses the live-feedback variant, this latter to facilitate the program's live-scrolling of the
bottom picture.

The pictures scrolled in this demonstration are, respectively 1220 by 175 pixels and 915 by 175
pixels. "pane" for each picture and scroll bar is 400 pixels wide by 175 pixels high, the
'CNTL' resources set the control maximum values to 820 and 515 respectively, and the control
rectangles specified in the 'CNTL' resource locate the scroll bars in the correct position in
the non-resizable, non-zoomable window.

As an incidental aspect of the demonstration, two different methods are used to scroll the
pictures when the scroll arrows are being used.  In the top picture, at each pass through the
action function, the pixels are scrolled using ScrollRect, the "vacated" area is invalidated,
and only this vacated area is redrawn.  In the bottom picture, at each pass through the action
function, the whole visible part of the picture is redrawn.  The user should note that the
first method results in some flickering in the "vacated" area when the picture is scrolled, and
that the second method eliminates this flickering at the cost of some horizontal "tearing" of
the picture caused by the way in which the image is drawn by the monitor on its screen.

The following comments are limited to those areas which are significantly different from the
same areas in the demonstration program Controls1.

defines

kScrollbarNonLiveID and kScrollbarLiveID will be assigned as the IDs of, respectively, the top
(non-live) scroll bar and the bottom (live) scroll bar.

main

Two calls to GetNewControl allocate memory for the control objects, insert the control objects
into the window's control list and draw the controls.

Following each call to GetNewControl:

o The id field of a variable of type ControlID is assigned the appropriate ID value for the
  specific control and the ID is then assigned to the control by a call to SetControlID.

o SetControlAction is called with a UPP passed in the actionProc parameter.  The effect of this
  is that the application-defined action function to which the UPP relates will be repeatedly
  called while the mouse button remains down.  As a consequence of using SetControlAction,
  (ControlActionUPP) -1 will be passed in TrackControl's actionProc parameter.

Note that no root control is created in this program; accordingly, the two controls will be
activated and deactivated individually.

In the next block, two 'PICT' resources are loaded, the associated handles being assigned to
two global variables.  In each case, the picture structure's picFrame field (a Rect) is copied
to a global variable.  In the case of the second picture, this rectangle is then offset
downwards by 191 pixels.  (Note that the two 'PICT' resources were created so that the top and
left fields of the picFrame Rect are both zero.)

In the next block, the width of the port rectangle is passed in the newViewSize parameter of
calls to SetControlViewSize.  (In this case, the view width is the same as the width of the
port rectangle.  This value is in the same units of measurement as are used for the scroll bar
minimum, maximum, and current values.)  This makes the scroll boxes proportional (on Mac OS
8/9, provided that the user has selected Smart Scrolling on in the Options tab of the
Appearance control panel).

doUpdate

In the two blocks which draw the pictures, the first call to SetOrigin sets the window origin
to the current scroll position, that is, to the position represented by the control's current
value, thus ensuring that the correct part of the picture will be drawn by the call to
DrawPicture.  The second call to SetOrigin resets the window's origin to (0,0).

Note that GetControlByID is used to retrieve references to the two controls.

doActivateWindow

Note that GetControlByID is used to retrieve references to the two controls.

doInContent

doInContent establishes whether a mouse-down event was in one of the scroll bars and, if so,
branches accordingly.

The call to GlobalToLocal converts the global coordinates of the mouse-down, stored in the
where field of the event structure, to the local coordinates required by FindControl.  If the
call to FindControl returns a non-zero result, the mouse-down was in a scroll bar.

If the mouse-down was in a scroll bar, GetControlID is called to get the ID of the control. 
Then, as in the demonstration program Controls1:

o If the mouse-down was in the non-live-feedback scroll bar, one of the applicationŐs functions
  is called to further handle the mouse-down event.

o If the mouse-down was in the live-feedback scroll bar, TrackControl is called with
  (ControlActionUPP) - 1 passed in the actionProc parameter.  This means that the
  application-defined (callback) function associated with the UPP previously set by
  SetControlAction will be continually called while the mouse button remains down.

doNonLiveScrollBars

doNonLiveScrollBars is similar to its sister function in Controls1 except that it actually
scrolls the window's contents.

At the first line, the function switches on the control part code:

o If the mouse-down was in the scroll box/scroller (that is, the "indicator"), the control's
  value at the time of the mouse-down is retrieved.  Control is then handed over to 
  TrackControl, which tracks user actions while the mouse button remains down.  If the user 
  releases the mouse button with the cursor inside the scroll box/scroller, the scroll distance 
  (in pixels) is calculated by subtracting the control's value prior to the scroll from its 
  current value.  If the user moved the scroll box/scroller, the picture's pixels are scrolled
  by the specified scroll distance in the appropriate direction, and the "vacated" area of the 
  window following the scroll is added to the (currently empty) window update region (Mac OS 
  8/9).  This means that an update event will be generated for the window and that the re-draw 
  of the picture will be attended to in the doUpdate function.

o If the mouse-down was in a scroll arrow or gray area/track, more specifically in one of the
  non-live-feedback's scroll bar's scroll arrows or gray areas/tracks, TrackControl takes 
  control until the user releases the mouse button.  The third parameter in the TrackControl 
  call means that the application-defined (callback) function associated with the UPP set by
  SetControlAction will be continually called while the mouse button remains down.

actionFunctionNonLive

actionFunctionNonLive is the action function for the non-live-feedback scroll bar.  Because it
is repeatedly called by TrackControl while the mouse button remains down, the scrolling it
performs continues repeatedly until the mouse button is released.

Firstly, if the cursor is not still inside the scroll arrow or gray area/track, the action
function exits.  The following occurs only when the cursor is within the control.

A reference to the window which "owns" this control is retrieved from the control object.

If the control part being used by the user to perform the scrolling is one of the scroll
arrows, the distance to scroll (in pixels) is set to 2.  If the control part being used is one
of the gray areas/track, the distance to scroll is set to the width of the window's content
region minus 10 pixels.  (Subtracting 10 pixels ensures that a small part of the pre-scroll
display will appear at right or left (depending on the direction of scroll) of the post-scroll
display.)

The first block following the switch converts the distance to scroll to the required negative
value if the user is scrolling towards the right.  The second block defeats any further
scrolling action if, firstly, the left scroll arrow is being used, the mouse button is still
down and the document is at the minimum (left) scrolled position or, secondly, the right scroll
arrow is being used, the mouse button is still down and the document is at the maximum (right)
scrolled position.

With the scroll distance determined, the call to the function doMoveScrollBox adds/subtracts
the distance to scroll to/from the control's current value and repositions the scroll
box/scroller accordingly.

At this stage, the picture scrolling takes place.  If scrolling is being effected using the
scroll arrows, ScrollRect scrolls the picture's pixels by the specified amount, and in the
specified direction, as represented by the distance-to-scroll value.  The "vacated" area is
then added to the window's update region (previously empty) by the call to InvalWindowRgn, and
BeginUpdate is called to ensure that, on Mac OS 8/9, (1) only the "vacated" area will be
redrawn and (2) the update region is cleared.

Regardless of whether the picture is being scrolled using the scroll arrows or the gray areas,
SetOrigin is then called to reset the window origin so that that part of the picture
represented by the current scroll position is drawn.  After the correct part of the picture is
drawn, the window origin is reset to (0,0).  

Finally, if BeginUpdate was called prior to the draw (that is, scrolling is being effected
using the scroll arrows), EndUpdate is called.

actionFunctionLive

actionFunctionLive is the action function for the live-feedback scroll bar.

The principal differences between this action function and the previous one are that action
functions for live-feedback scroll bars must continually scroll the window's contents, not only
while the mouse button remains down in the scroll arrows and gray areas/track, but also while
the scroll box/scroller is being dragged.  Accordingly, this action function, unlike the action
function for the non-live-feedback scroll bar, is also called while the mouse button remains
down in the scroll box/scroller.

If the mouse-down occurred in the scroll box/scroller, the code which sets up the scroll
distance, adjusts the sign of the scroll distance according to whether the scroll is left or
right, prevents scrolling beyond the minimum and maximum scroll values, and calls
doMoveScrollBox to move the scroll box/scroller and update the control's value, is bypassed. 
The call to doMoveScrollBox is bypassed because, the live-feedback variant of the CDEF moves
the scroll box/scroller and updates the control's value when the mouse-down is in the scroll
box/scroller.

After the if block has been bypassed (mouse-down in the scroll box/scroller) or executed
(mouse-down in the scroll arrows or gray area/track), the window contents are scrolled. 
Regardless of whether the picture is being scrolled using the scroll box/scroller, the scroll
arrows, or the gray areas/track, SetOrigin is called to reset the window origin so that that
part of the picture represented by the current scroll position is drawn by the call to
DrawPicture.  After the correct part of the picture is drawn, the window origin is reset to
(0,0).

Note that this alternative approach to re-drawing the picture when scrolling is being effected
using the scroll arrows has not been dictated by the fact that this is a live-feedback action
function.  Either of these two approaches will work in both live-feedback and non-live-feedback
action functions.

doMoveScrollBox

doMoveScrollBox is called from within the action function to reset the control's current value
to reflect the scrolled distance, and to reposition the scroll box accordingly.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

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

Price Scanner via MacPrices.net

Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobile’s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Apple’s one-year warranty is... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16″ and 14″ MacBook Pros along with 13″ and 15″ MacBook... Read more
Every model of Apple’s 13-inch M3 MacBook Air...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices are the lowest currently available for new 13″ M3 MacBook Airs among... Read more
Sunday Sale: Apple iPad Magic Keyboards for 1...
Walmart has Apple Magic Keyboards for 12.9″ iPad Pros, in Black, on sale for $150 off MSRP on their online store. Sale price for online orders only, in-store price may vary. Order online and choose... Read more
Apple Watch Ultra 2 now available at Apple fo...
Apple has, for the first time, begun offering Certified Refurbished Apple Watch Ultra 2 models in their online store for $679, or $120 off MSRP. Each Watch includes Apple’s standard one-year warranty... Read more
AT&T has the iPhone 14 on sale for only $...
AT&T has the 128GB Apple iPhone 14 available for only $5.99 per month for new and existing customers when you activate unlimited service and use AT&T’s 36 month installment plan. The fine... Read more
Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
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

Jobs Board

Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
IT Systems Engineer ( *Apple* Platforms) - S...
IT Systems Engineer ( Apple Platforms) at SpaceX Hawthorne, CA SpaceX was founded under the belief that a future where humanity is out exploring the stars is 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
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.