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

Netflix Games expands its catalogue with...
It is a good time to be a Netflix subscriber this month. I presume there's a good show or two, but we are, of course, talking about their gaming service that seems to be picking up steam lately. May is adding five new titles, and there are some... | Read more »
Seven Knights Idle Adventure drafts in a...
Seven Knights Idle Adventure is opening up more stages, passing the 15k mark, and players may find themselves in need of more help to clear these higher stages. Well, the cavalry has arrived with the introduction of the Legendary Hero Iris, as... | Read more »
AFK Arena celebrates five years of 100 m...
Lilith Games is quite the behemoth when it comes to mobile games, with Rise of Kingdom and Dislyte firmly planting them as a bit name. Also up there is AFK Arena, which is celebrating a double whammy of its 5th anniversary, as well as blazing past... | Read more »
Fallout Shelter pulls in ten times its u...
When the Fallout TV series was announced I, like I assume many others, assumed it was going to be an utter pile of garbage. Well, as we now know that couldn't be further from the truth. It was a smash hit, and this success has of course given the... | Read more »
Recruit two powerful-sounding students t...
I am a fan of anime, and I hear about a lot that comes through, but one that escaped my attention until now is A Certain Scientific Railgun T, and that name is very enticing. If it's new to you too, then players of Blue Archive can get a hands-on... | Read more »
Top Hat Studios unveils a new gameplay t...
There are a lot of big games coming that you might be excited about, but one of those I am most interested in is Athenian Rhapsody because it looks delightfully silly. The developers behind this project, the rather fancy-sounding Top Hat Studios,... | Read more »
Bound through time on the hunt for sneak...
Have you ever sat down and wondered what would happen if Dr Who and Sherlock Holmes went on an adventure? Well, besides probably being the best mash-up of English fiction, you'd get the Hidden Through Time series, and now Rogueside has announced... | Read more »
The secrets of Penacony might soon come...
Version 2.2 of Honkai: Star Rail is on the horizon and brings the culmination of the Penacony adventure after quite the escalation in the latest story quests. To help you through this new expansion is the introduction of two powerful new... | Read more »
The Legend of Heroes: Trails of Cold Ste...
I adore game series that have connecting lore and stories, which of course means the Legend of Heroes is very dear to me, Trails lore has been building for two decades. Excitedly, the next stage is upon us as Userjoy has announced the upcoming... | Read more »
Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasn’t sued them for copyright infringement, but... | Read more »

Price Scanner via MacPrices.net

Apple introduces the new M4-powered 11-inch a...
Today, Apple revealed the new 2024 M4 iPad Pro series, boasting a surprisingly thin and light design that pushes the boundaries of portability and performance. Offered in silver and space black... Read more
Apple introduces the new 2024 11-inch and 13-...
Apple has unveiled the revamped 11-inch and brand-new 13-inch iPad Air models, upgraded with the M2 chip. Marking the first time it’s offered in two sizes, the 11-inch iPad Air retains its super-... Read more
Apple discontinues 9th-gen iPad, drops prices...
With today’s introduction of the new 2024 iPad Airs and iPad Pros, Apple has (finally) discontinued the older 9th-generation iPad with a home button. In response, they also dropped prices on 10th-... Read more
Apple AirPods on sale for record-low prices t...
Best Buy has Apple AirPods on sale for record-low prices today starting at only $79. Buy online and choose free shipping or free local store pickup (if available). Sale price for online orders only,... Read more
13-inch M3 MacBook Airs on sale for $100 off...
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, along with Amazon’s, are the lowest currently available for new 13″... Read more
Amazon is offering a $100 discount on every 1...
Amazon has every configuration and color of Apple’s 13″ M3 MacBook Air on sale for $100 off MSRP, now starting at $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD): $999 $100 off... Read more
Sunday Sale: Take $150 off every 15-inch M3 M...
Amazon is now offering a $150 discount on every configuration and color of Apple’s M3-powered 15″ MacBook Airs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 15″ M3 MacBook... Read more
Apple’s 24-inch M3 iMacs are on sale for $150...
Amazon is offering a $150 discount on Apple’s new M3-powered 24″ iMacs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 24″ M3 iMac/8-core GPU/8GB/256GB: $1149.99, $150 off... Read more
Verizon has Apple AirPods on sale this weeken...
Verizon has Apple AirPods on sale for up to 31% off MSRP on their online store this weekend. Their prices are the lowest price available for AirPods from any Apple retailer. Verizon service is not... Read more
Apple has 15-inch M2 MacBook Airs available s...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs available starting at $1019 and ranging up to $300 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at Apple.... Read more

Jobs Board

IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: May 8, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Nurse Anesthetist - *Apple* Hill Surgery Ce...
Nurse Anesthetist - Apple Hill Surgery Center Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
LPN-Physician Office Nurse - Orthopedics- *Ap...
LPN-Physician Office Nurse - Orthopedics- Apple Hill Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Read more
Supervisor/Therapist Rehabilitation Medicine...
Supervisor/Therapist Rehabilitation Medicine - Apple Hill (Outpatient Clinic) - Day Location: York Hospital, York, PA Schedule: Full Time Sign-On Bonus Eligible Read more
BBW Sales Support- *Apple* Blossom Mall - Ba...
BBW Sales Support- APPLE BLOSSOM MALL Brand: Bath & Body Works Location: Winchester, VA, US Location Type: On-site Job ID: 04388 Job Area: Store: Sales and Support Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.