TweetFollow Us on Twitter

MACINTOSH C

Demonstration Program 1

Go to Contents
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// Controls1.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// 
// This program opens a kWindowFullZoomGrowDocumentProc window containing:
//
// „  Four pop-up menu buttons (fixed width, variable width, add resource, and use window
//    font variants).
//
// „  Three non-auto-toggling radio buttons auto-embedded in a primary group box (text
//    title variant).
//
// „  Three non-auto-toggling checkboxes auto-embedded in a primary group box (text title
//    variant).
//
// „  Four push buttons (two basic, one left colour icon variant, and one right colour
//    icon variant).
//
// „  A vertical scroll bar (non live-feedback variant) and a horizontal scroll bar 
//    (live-feedback variant). 
//
// The window also contains a window header frame in which is displayed:
//
// „  The menu items chosen from the pop-up menus.
//
// „  The identity of a push button when that push button is clicked.
//
// „  Scroll bar control values when the scroll arrows or gray areas of the scroll bars
//    are clicked and when the scroll box 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 content.
//
// 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:
//
// „  An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit, and Demonstration
//     menus, and the pop-up menus (preload, non-purgeable).  
//
// „  A 'WIND' resource (purgeable) (initially not visible).  
//
// „  'CNTL' resources for the pop-up menus, group boxes, radio buttons, checkboxes, 
//    buttons, and scroll bars (preload, purgeable) (initially visible).
//
// „  Two 'cicn' resources (purgeable) for the colour icon variant buttons.
//
// „  An 'hrct' resource and an 'hwin' resource (both purgeable), which provide help 
//    balloons describing  the various controls.  
//
// „  A 'SIZE' resource with the acceptSuspendResumeEvents, doesActivateOnFGSwitch,
//    and is32BitCompatible flags set.    
//
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

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

#include <Appearance.h>
#include <ControlDefinitions.h>
#include <Devices.h>
#include <Fonts.h>
#include <Menus.h>
#include <NumberFormatting.h>
#include <Processes.h>
#include <Resources.h>
#include <Sound.h>
#include <ToolUtils.h>
#include <LowMem.h>

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

#define rMenubar           128
#define rNewWindow         128
#define mApple             128
#define  iAbout            1
#define mFile              129
#define  iQuit             11
#define mDemonstration     131
#define  iColour           1
#define  iGrids            2
#define cPopupFixed        128
#define cPopupVariable     129
#define cPopupAddRes       130
#define cPopupWinFont      131
#define  iCanberra         1
#define  iLondon           2
#define  iAuckland         3
#define  iRome             4
#define cRadiobuttonRed    132
#define cRadiobuttonWhite  133
#define cRadiobuttonBlue   134
#define cCheckboxGrid      135
#define cCheckboxRulers    136
#define cCheckboxGridsnap  137
#define cGroupBoxColour    138
#define cGroupBoxGrids     139
#define cButton            140
#define cButtonDefault     141
#define cButtonLeftIcon    142
#define cButtonRightIcon   143
#define cScrollbarVert     144
#define cScrollbarHoriz    145

#define MAXLONG            0x7FFFFFFF
#define MIN(a,b)           ((a) < (b) ? (a) : (b))

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

typedef struct
{
  ControlHandle  popupFixedHdl;
  ControlHandle  popupVariableHdl;
  ControlHandle  popupAddResHdl;
  ControlHandle  popupWinFontHdl;
  ControlHandle  groupboxColourHdl;
  ControlHandle  groupboxGridsHdl;
  ControlHandle  buttonHdl;
  ControlHandle  buttonDefaultHdl;
  ControlHandle  buttonLeftIconHdl;
  ControlHandle  buttonRightIconHdl;
  ControlHandle  radiobuttonRedHdl;
  ControlHandle  radiobuttonWhiteHdl;
  ControlHandle  radiobuttonBlueHdl;
  ControlHandle  checkboxGridHdl;
  ControlHandle  checkboxRulersHdl;
  ControlHandle  checkboxGridSnapHdl;
  ControlHandle  scrollbarVertHdl;
  ControlHandle  scrollbarHorizHdl;
} DocStruc;

typedef DocStruc **DocStrucHandle;

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

ControlActionUPP   actionFunctionVertUPP;
ControlActionUPP   actionFunctionHorizUPP;
Boolean            gDone;
Boolean            gInBackground;
Boolean            gPixelDepth;
Boolean            gIsColourDevice;
Str255             gCurrentString;

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

void  main                 (void);
void  doInitManagers       (void);
void  doGetControls        (WindowPtr);
void  doEvents             (EventRecord *);
void  doMouseDown          (EventRecord *);
void  doMenuChoice         (SInt32);
void  doUpdate             (EventRecord *);
void  doActivate           (EventRecord *);
void  doActivateWindow     (WindowPtr,Boolean);
void  doOSEvent            (EventRecord *);
void  doInContent          (EventRecord *,WindowPtr);
void  doPopupMenuChoice    (WindowPtr,ControlHandle,SInt16);
void  doVertScrollbar      (ControlPartCode,WindowPtr,ControlHandle,Point);
void  doMoveScrollBox      (ControlHandle,SInt16);
void  doRadioButtons       (ControlHandle,WindowPtr);
void  doCheckboxes         (ControlHandle);
void  doPushButtons        (ControlHandle,WindowPtr);
void  doAdjustScrollBars   (WindowPtr);
void  doDrawMessage        (WindowPtr,Boolean);
void  doPlaySound          (Str255);
void  doConcatPStrings     (Str255,Str255);
void  doCopyPString        (Str255,Str255);
void  doGetDepthAndDevice  (void);

pascal void actionFunctionVert(ControlHandle controlHdl,ControlPartCode controlPartCode);
pascal void actionFunctionHoriz(ControlHandle controlHdl,ControlPartCode controlPartCode);

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

void  main(void)
{
  Handle           menubarHdl;
  MenuHandle       menuHdl;
  WindowPtr        windowPtr;
  DocStrucHandle   docStrucHdl;
  EventRecord      EventStructure;

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

  doInitManagers();

  // ......................................................... create routine descriptors
  
  actionFunctionVertUPP = NewControlActionProc((ProcPtr) actionFunctionVert);
  actionFunctionHorizUPP = NewControlActionProc((ProcPtr) actionFunctionHoriz);

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

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

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

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

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

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

  SetPort(windowPtr);
  TextSize(10);
  
  SetThemeWindowBackground(windowPtr,kThemeBrushDialogBackgroundActive,true);

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

  if(!(docStrucHdl = (DocStrucHandle) NewHandle(sizeof(DocStruc))))
    ExitToShell();
  
  SetWRefCon(windowPtr,(SInt32) docStrucHdl);

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

  doGetControls(windowPtr);
  doAdjustScrollBars(windowPtr);
  ShowWindow(windowPtr);

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

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

  gDone = false;

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

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

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

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

  InitCursor();  
  FlushEvents(everyEvent,0);

  RegisterAppearanceClient();
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doGetControls

void  doGetControls(WindowPtr windowPtr)
{
  ControlHandle        controlHdl;
  DocStrucHandle       docStrucHdl;
  Boolean              booleanData = true;
  ControlFontStyleRec  controlFontStyleStruc;

  CreateRootControl(windowPtr,&controlHdl);

  docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));

  if(!((*docStrucHdl)->popupFixedHdl        = GetNewControl(cPopupFixed,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->popupVariableHdl     = GetNewControl(cPopupVariable,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->popupAddResHdl       = GetNewControl(cPopupAddRes,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->popupWinFontHdl      = GetNewControl(cPopupWinFont,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->radiobuttonRedHdl    = GetNewControl(cRadiobuttonRed,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->radiobuttonWhiteHdl  = GetNewControl(cRadiobuttonWhite,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->radiobuttonBlueHdl   = GetNewControl(cRadiobuttonBlue,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->checkboxGridHdl      = GetNewControl(cCheckboxGrid,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->checkboxRulersHdl    = GetNewControl(cCheckboxRulers,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->checkboxGridSnapHdl  = GetNewControl(cCheckboxGridsnap,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->groupboxColourHdl    = GetNewControl(cGroupBoxColour,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->groupboxGridsHdl     = GetNewControl(cGroupBoxGrids,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->buttonHdl            = GetNewControl(cButton,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->buttonDefaultHdl     = GetNewControl(cButtonDefault,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->buttonLeftIconHdl    = GetNewControl(cButtonLeftIcon,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->buttonRightIconHdl   = GetNewControl(cButtonRightIcon,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->scrollbarVertHdl     = GetNewControl(cScrollbarVert,windowPtr)))
    ExitToShell();
  if(!((*docStrucHdl)->scrollbarHorizHdl    = GetNewControl(cScrollbarHoriz,windowPtr)))
    ExitToShell();

  AutoEmbedControl((*docStrucHdl)->radiobuttonRedHdl,windowPtr);
  AutoEmbedControl((*docStrucHdl)->radiobuttonWhiteHdl,windowPtr);
  AutoEmbedControl((*docStrucHdl)->radiobuttonBlueHdl,windowPtr);
  AutoEmbedControl((*docStrucHdl)->checkboxGridHdl,windowPtr);
  AutoEmbedControl((*docStrucHdl)->checkboxRulersHdl,windowPtr);
  AutoEmbedControl((*docStrucHdl)->checkboxGridSnapHdl,windowPtr);

  SetControlData((*docStrucHdl)->buttonDefaultHdl,kControlNoPart,
                 kControlPushButtonDefaultTag,sizeof(booleanData),(Ptr) &booleanData);

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

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

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

void  doEvents(EventRecord *eventStrucPtr)
{
  SInt8   charCode;
  SInt32  menuChoice;
  SInt16  menuID, menuItem;

  switch(eventStrucPtr->what)
  {
    case keyDown:
    case autoKey:
      charCode = eventStrucPtr->message & charCodeMask;
      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);
      HiliteMenu(0);
      break;
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMouseDown

void  doMouseDown(EventRecord *eventStrucPtr)
{
  SInt16     partCode;
  WindowPtr  windowPtr;
  Rect       growRect;
  SInt32     newSize;

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

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

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

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

    case inGrow:
      growRect = qd.screenBits.bounds;
      growRect.top   = 336; 
      growRect.left = 260;
      newSize = GrowWindow(windowPtr,eventStrucPtr->where,&growRect);
      if(newSize != 0)
      {
        SizeWindow(windowPtr,LoWord(newSize),HiWord(newSize),true);
        doAdjustScrollBars(windowPtr);
        doDrawMessage(windowPtr,true);
      }
      break;

    case inZoomIn:
    case inZoomOut:
      if(TrackBox(windowPtr,eventStrucPtr->where,partCode))
      {
        SetPort(windowPtr);
        EraseRect(&windowPtr->portRect);
        ZoomWindow(windowPtr,partCode,false);
        doAdjustScrollBars(windowPtr);
      }
      break;
  }
}

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

void  doMenuChoice(SInt32 menuChoice)
{
  SInt16           menuID, menuItem;
  Str255           itemName;
  SInt16           daDriverRefNum;
  MenuHandle       menuHdl;
  WindowPtr        windowPtr;
  DocStrucHandle   docStrucHdl;
         
  menuID = HiWord(menuChoice);
  menuItem = LoWord(menuChoice);

  if(menuID == 0)
    return;

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

    case mFile:
      if(menuItem == iQuit)
        gDone = true;
      break;
      
    case mDemonstration:
      menuHdl = GetMenuHandle(mDemonstration);
      windowPtr = FrontWindow();
      docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));
  
      if(menuItem == iColour)
      {
        if(IsControlVisible((*docStrucHdl)->groupboxColourHdl))
        {
          HideControl((*docStrucHdl)->groupboxColourHdl);
          SetMenuItemText(menuHdl,iColour,"\pShow Colour");
        }
        else
        {
          ShowControl((*docStrucHdl)->groupboxColourHdl);
          SetMenuItemText(menuHdl,iColour,"\pHide Colour");
        }
      }
      else if(menuItem == iGrids)
      {
        if(IsControlActive((*docStrucHdl)->groupboxGridsHdl))
        {
          DeactivateControl((*docStrucHdl)->groupboxGridsHdl);
          SetMenuItemText(menuHdl,iGrids,"\pActivate Grids");
        }
        else
        {
          ActivateControl((*docStrucHdl)->groupboxGridsHdl);
          SetMenuItemText(menuHdl,iGrids,"\pDeactivate Grids");
        }
      }
      break;
  }

  HiliteMenu(0);
}

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

void  doUpdate(EventRecord *eventStrucPtr)
{
  WindowPtr  windowPtr;
  
  windowPtr = (WindowPtr) eventStrucPtr->message;

  BeginUpdate(windowPtr);

  SetPort(windowPtr);
  doDrawMessage(windowPtr,!gInBackground);
  UpdateControls(windowPtr,windowPtr->visRgn);

  EndUpdate(windowPtr);
}

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

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

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

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

void  doActivateWindow(WindowPtr windowPtr,Boolean becomingActive)
{  
  ControlHandle controlHdl;
    
  GetRootControl(windowPtr,&controlHdl);

  if(becomingActive)
  {
    ActivateControl(controlHdl);
    doDrawMessage(windowPtr,becomingActive);
  }
  else
  {
    DeactivateControl(controlHdl);
    doDrawMessage(windowPtr,becomingActive);
  }
}

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

void  doOSEvent(EventRecord *eventStrucPtr)
{
  switch((eventStrucPtr->message >> 24) & 0x000000FF)
  {
    case suspendResumeMessage:
      gInBackground = (eventStrucPtr->message & resumeFlag) == 0;
      doActivateWindow(FrontWindow(),!gInBackground);
      break;
        
    case mouseMovedMessage:
      break;
  }
}

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

void  doInContent(EventRecord *eventStrucPtr,WindowPtr windowPtr)
{
  DocStrucHandle   docStrucHdl;
  ControlHandle    controlHdl;
  SInt16           controlValue, controlPartCode;

  docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));

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

  if(controlPartCode = FindControl(eventStrucPtr->where,windowPtr,&controlHdl))
  {
    if(controlHdl == (*docStrucHdl)->popupFixedHdl || 
       controlHdl == (*docStrucHdl)->popupVariableHdl ||
       controlHdl == (*docStrucHdl)->popupAddResHdl || 
       controlHdl == (*docStrucHdl)->popupWinFontHdl)
    {
      TrackControl(controlHdl,eventStrucPtr->where,(ControlActionUPP) -1);
      controlValue = GetControlValue(controlHdl);
      doPopupMenuChoice(windowPtr,controlHdl,controlValue);
    }
    else if(controlHdl == (*docStrucHdl)->scrollbarVertHdl) 
    {
      doVertScrollbar(controlPartCode,windowPtr,controlHdl,eventStrucPtr->where);
    }
    else if(controlHdl == (*docStrucHdl)->scrollbarHorizHdl)
    {
      TrackControl(controlHdl,eventStrucPtr->where,actionFunctionHorizUPP);
    }
    else
    {
      if(TrackControl(controlHdl,eventStrucPtr->where,NULL))
      {
        if(controlHdl == (*docStrucHdl)->radiobuttonRedHdl ||
           controlHdl == (*docStrucHdl)->radiobuttonWhiteHdl ||
           controlHdl == (*docStrucHdl)->radiobuttonBlueHdl)
        {
          doRadioButtons(controlHdl,windowPtr);
        }
        if(controlHdl == (*docStrucHdl)->checkboxGridHdl ||
           controlHdl == (*docStrucHdl)->checkboxRulersHdl ||
           controlHdl == (*docStrucHdl)->checkboxGridSnapHdl)
        {
          doCheckboxes(controlHdl);
        }
        if(controlHdl == (*docStrucHdl)->buttonHdl ||
           controlHdl == (*docStrucHdl)->buttonDefaultHdl ||
           controlHdl == (*docStrucHdl)->buttonLeftIconHdl ||
           controlHdl == (*docStrucHdl)->buttonRightIconHdl)
        {
          doPushButtons(controlHdl,windowPtr);
        }
      }
    }
  }  
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doPopupMenuChoice

void  doPopupMenuChoice(WindowPtr windowPtr,ControlHandle controlHdl,SInt16 controlValue)
{
  DocStrucHandle  docStrucHdl;
  MenuHandle      menuHdl;
  Str255          itemName;

  docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));
    
  if(controlHdl == (*docStrucHdl)->popupAddResHdl)
  {
    GetControlData(controlHdl,kControlNoPart,kControlPopupButtonMenuHandleTag,
                   sizeof(menuHdl),(Ptr) &menuHdl,NULL);
    GetMenuItemText(menuHdl,controlValue,itemName);    
    doPlaySound(itemName);
    doCopyPString(itemName,gCurrentString);
    doDrawMessage(windowPtr,true);
  }
  else
  {
    switch(controlValue)
    {
      case iCanberra:
        doCopyPString("\pCanberra",gCurrentString);
        doDrawMessage(windowPtr,true);
        break;

      case iLondon:
        doCopyPString("\pLondon",gCurrentString);
        doDrawMessage(windowPtr,true);
        break;

      case iAuckland:
        doCopyPString("\pAuckland",gCurrentString);
        doDrawMessage(windowPtr,true);
        break;

      case iRome:
        doCopyPString("\pRome",gCurrentString);
        doDrawMessage(windowPtr,true);
        break;
    }
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doVertScrollbar

void  doVertScrollbar(ControlPartCode controlPartCode,WindowPtr windowPtr,
                      ControlHandle controlHdl,Point mouseXY)
{
  Str255  valueString;

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

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

    case kControlUpButtonPart:
    case kControlDownButtonPart:
    case kControlPageUpPart:
    case kControlPageDownPart:
      TrackControl(controlHdl,mouseXY,actionFunctionVertUPP);
      break;
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× actionFunctionVert

pascal void  actionFunctionVert(ControlHandle controlHdl,ControlPartCode controlPartCode)
{
  SInt16     scrollDistance, controlValue;
  Str255     valueString;
  WindowPtr  windowPtr;

  doCopyPString("\pVertical Scroll 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(controlHdl);
    if(((controlValue == GetControlMaximum(controlHdl)) && scrollDistance < 0) || 
       ((controlValue == GetControlMinimum(controlHdl)) && scrollDistance > 0))
      return;

    doMoveScrollBox(controlHdl,scrollDistance);

    NumToString((SInt32) GetControlValue(controlHdl),valueString);
    doConcatPStrings(gCurrentString,valueString);
    windowPtr = (*controlHdl)->contrlOwner;
    doDrawMessage(windowPtr,true);
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× actionFunctionHoriz

pascal void  actionFunctionHoriz(ControlHandle controlHdl,
                                ControlPartCode controlPartCode)
{
  SInt16     scrollDistance, controlValue;
  Str255     valueString;
  WindowPtr  windowPtr;

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

  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(controlHdl);
    if(((controlValue == GetControlMaximum(controlHdl)) && scrollDistance < 0) || 
       ((controlValue == GetControlMinimum(controlHdl)) && scrollDistance > 0))
      return;

    doMoveScrollBox(controlHdl,scrollDistance);
  }

  NumToString((SInt32) GetControlValue(controlHdl),valueString);
  doConcatPStrings(gCurrentString,valueString);
  windowPtr = (*controlHdl)->contrlOwner;
  doDrawMessage(windowPtr,true);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMoveScrollBox

void doMoveScrollBox(ControlHandle controlHdl,SInt16 scrollDistance)
{
  SInt16  oldControlValue, controlValue, controlMax;

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

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

  SetControlValue(controlHdl,controlValue);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doRadioButtons

void  doRadioButtons(ControlHandle controlHdl,WindowPtr windowPtr)
{  
  DocStrucHandle  docStrucHdl;

  docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));

  SetControlValue((*docStrucHdl)->radiobuttonRedHdl,kControlRadioButtonUncheckedValue);
  SetControlValue((*docStrucHdl)->radiobuttonWhiteHdl,kControlRadioButtonUncheckedValue);
  SetControlValue((*docStrucHdl)->radiobuttonBlueHdl,kControlRadioButtonUncheckedValue);
  SetControlValue(controlHdl,kControlRadioButtonCheckedValue);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doCheckboxes

void  doCheckboxes(ControlHandle controlHdl)
{
  SetControlValue(controlHdl,!GetControlValue(controlHdl));
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doPushButtons

void  doPushButtons(ControlHandle controlHdl,WindowPtr windowPtr)
{
  DocStrucHandle  docStrucHdl;

  docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));

  if(controlHdl == (*docStrucHdl)->buttonHdl)
  {
    doCopyPString("\pButton",gCurrentString);
    doDrawMessage(windowPtr,true);
  }
  else if(controlHdl == (*docStrucHdl)->buttonDefaultHdl)
  {
    doCopyPString("\pDefault Button",gCurrentString);
    doDrawMessage(windowPtr,true);
  }
  else if(controlHdl == (*docStrucHdl)->buttonLeftIconHdl)
  {
    doCopyPString("\pLeft Icon Button",gCurrentString);
    doDrawMessage(windowPtr,true);
  }
  else if(controlHdl == (*docStrucHdl)->buttonRightIconHdl)
  {
    doCopyPString("\pRight Icon Button",gCurrentString);
    doDrawMessage(windowPtr,true);
  }
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doAdjustScrollBars

void  doAdjustScrollBars(WindowPtr windowPtr)
{
  Rect            winRect;
  DocStrucHandle  docStrucHdl;
      
  docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));

  winRect = windowPtr->portRect;

  HideControl((*docStrucHdl)->scrollbarVertHdl);
  HideControl((*docStrucHdl)->scrollbarHorizHdl);

  MoveControl((*docStrucHdl)->scrollbarVertHdl,winRect.right - 15,winRect.top + 25);
  MoveControl((*docStrucHdl)->scrollbarHorizHdl,winRect.left -1,winRect.bottom -15);

  SizeControl((*docStrucHdl)->scrollbarVertHdl,16, winRect.bottom - 39);
  SizeControl((*docStrucHdl)->scrollbarHorizHdl, winRect.right - 13,16);

  ShowControl((*docStrucHdl)->scrollbarVertHdl);
  ShowControl((*docStrucHdl)->scrollbarHorizHdl);
  
  SetControlMaximum((*docStrucHdl)->scrollbarVertHdl,
                    windowPtr->portRect.bottom - windowPtr->portRect.top - 25 - 15);
  SetControlMaximum((*docStrucHdl)->scrollbarHorizHdl,
                    windowPtr->portRect.right - windowPtr->portRect.left - 15);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doDrawMessage

void  doDrawMessage(WindowPtr windowPtr,Boolean inState)
{
  Rect    headerRect;
  SInt16  windowWidth, stringWidth;

  SetRect(&headerRect,windowPtr->portRect.left - 1,windowPtr->portRect.top - 1,
          windowPtr->portRect.right + 1,windowPtr->portRect.top + 26);
  DrawThemeWindowHeader(&headerRect,inState);
  
  if(inState == kThemeStateActive)
    SetThemeTextColor(kThemeTextColorWindowHeaderActive,gPixelDepth,gIsColourDevice);
  else
    SetThemeTextColor(kThemeTextColorWindowHeaderInactive,gPixelDepth,gIsColourDevice);

  windowWidth = (windowPtr)->portRect.right - (windowPtr)->portRect.left;
  stringWidth = StringWidth(gCurrentString);
  MoveTo((windowWidth / 2) - (stringWidth / 2), 17);
  DrawString(gCurrentString);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doPlaySound

void  doPlaySound(Str255 sndResourceName)
{
  SndListHandle  soundHdl;
  SndChannelPtr  soundChanPtr = NULL;

  if(soundHdl = (SndListHandle) GetNamedResource('snd ',sndResourceName))
    SndPlay(soundChanPtr,soundHdl,1);
}

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× 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;
}

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

void  doGetDepthAndDevice(void)
{
  GDHandle  deviceHdl;

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

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


Demonstration Program 2

// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// Controls2.c
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
// 
// This program:
//
// „  Opens a kWindowDocumentProc window with a two horizontal scroll bars, each of
//     which relates to the picture displayed immediately above it.
//
// „  Allows the user to horizontally scroll the pictures within the window using the
//    scroll box, the scroll arrows and the gray area 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.
//
// If the target is the PowerPC target, and if Mac OS 8.5 or later is present, the scroll
// bar scroll boxes are made proportional.  
//
// In this program, the action functions are are set using the function SetControlAction.
// (In Controls1, the action functions were passed in TrackControl's actionProc 
// parameter.)
//
// The program utilises the following resources:
//
// „  An 'MBAR' resource, and 'MENU' resources for Apple, File and Edit (preload, non-
//    purgeable).
//
// „  A 'WIND' resource (purgeable) (initially visible).
//
// „  Two 'CNTL' resource for the horizontal scroll bars (purgeable).
//
// „  A 'PICT' resource containing the picture to be scrolled (non-purgeable).
//
// „  A 'SIZE' resource with the acceptSuspendResumeEvents, doesActivateOnFGSwitch,
//    and is32BitCompatible flags set.
//
// ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

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

#include <Appearance.h>
#include <ControlDefinitions.h>
#include <Devices.h>
#include <Fonts.h>
#include <Gestalt.h>
#include <Processes.h>
#include <Sound.h>
#include <ToolUtils.h>

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

#define rMenubar           128
#define rNewWindow         128
#define rPictureNonLive    128
#define rPictureLive       129
#define mApple             128
#define  iAbout            1
#define mFile              129
#define  iQuit             11
#define cScrollbarNonLive  128
#define cScrollbarLive     129

#define MAXLONG            0x7FFFFFFF

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

typedef struct
{
  ControlHandle scrollbarNonLiveHdl;
  ControlHandle scrollbarLiveHdl;
} DocStruc;

typedef DocStruc **DocStrucHandle;

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

ControlActionUPP   actionFuncNonLiveUPP;
ControlActionUPP   actionFuncLiveUPP;
Boolean            gDone;
Boolean            gInBackground;
Rect               gPictRectNonLive, gPictRectLive;
PicHandle          gPictHandleNonLive, gPictHandleLive ;

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

void  main                 (void);
void  doInitManagers       (void);
void  doEvents             (EventRecord *);
void  doMouseDown          (EventRecord *);
void  doUpdate             (EventRecord *);
void  doActivate           (EventRecord *);
void  doActivateWindow     (WindowPtr,Boolean);
void  doOSEvent            (EventRecord *);
void  doMenuChoice         (SInt32);
void  doInContent          (EventRecord *,WindowPtr);
void  doNonLiveScrollBars  (ControlPartCode,WindowPtr,ControlHandle,Point);
void  doMoveScrollBox      (ControlHandle,SInt16);

pascal void actionFuncNonLive  (ControlHandle,ControlPartCode);
pascal void actionFuncLive     (ControlHandle,ControlPartCode);

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

void  main(void)
{
  Handle           menubarHdl;
  MenuHandle       menuHdl;
  WindowPtr        windowPtr;
  DocStrucHandle   docStrucHdl;
  OSErr            osError;
  SInt32           response, viewSize;
  EventRecord      eventStructure;

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

  doInitManagers();

  // ......................................................... create routine descriptors
  
  actionFuncNonLiveUPP = NewControlActionProc((ProcPtr) actionFuncNonLive);
  actionFuncLiveUPP = NewControlActionProc((ProcPtr) actionFuncLive);

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

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

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

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

  SetPort(windowPtr);

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

  docStrucHdl = (DocStrucHandle) NewHandle(sizeof(DocStruc));
  SetWRefCon(windowPtr,(SInt32) docStrucHdl);

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

  (*docStrucHdl)->scrollbarNonLiveHdl = GetNewControl(cScrollbarNonLive,windowPtr);
  SetControlAction((*docStrucHdl)->scrollbarNonLiveHdl,actionFuncNonLiveUPP);
  
  (*docStrucHdl)->scrollbarLiveHdl    = GetNewControl(cScrollbarLive,windowPtr);
  SetControlAction((*docStrucHdl)->scrollbarLiveHdl,actionFuncLiveUPP);

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

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

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

  // .............. if Mac OS 8.5 or later present,  set up for proportional scroll boxes

#if TARGET_CPU_PPC
  osError = Gestalt(gestaltSystemVersion,&response);

  if(osError == noErr && response >= 0x00000850)
  {
    viewSize = (*gPictHandleNonLive)->picFrame.right - 
               (*gPictHandleNonLive)->picFrame.left;
    SetControlViewSize((*docStrucHdl)->scrollbarNonLiveHdl,200);
    viewSize = (*gPictHandleLive)->picFrame.right - 
               (*gPictHandleLive)->picFrame.left;
    SetControlViewSize((*docStrucHdl)->scrollbarLiveHdl,200);
  }
#endif
  
  // .................................................................... enter eventLoop

  gDone = false;

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

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

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

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

  InitCursor();  
  FlushEvents(everyEvent,0);

  RegisterAppearanceClient();
}

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

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

    case updateEvt:
      doUpdate(eventStrucPtr);
      break;

    case activateEvt:
      doActivate(eventStrucPtr);
      break;

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

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMouseDown

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

  controlPartCode = FindWindow(eventStrucPtr->where,&windowPtr);

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

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

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

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

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

void  doUpdate(EventRecord *eventStrucPtr)
{
  WindowPtr       windowPtr;
  DocStrucHandle  docStrucHdl;

  windowPtr = (WindowPtr)eventStrucPtr->message;
  docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));

  BeginUpdate(windowPtr);

  SetPort(windowPtr);

  UpdateControls(windowPtr,windowPtr->visRgn);

  SetOrigin(GetControlValue((*docStrucHdl)->scrollbarNonLiveHdl),0);
  DrawPicture(gPictHandleNonLive,&gPictRectNonLive);
  SetOrigin(0,0);

  SetOrigin(GetControlValue((*docStrucHdl)->scrollbarLiveHdl),0);
  DrawPicture(gPictHandleLive,&gPictRectLive);
  SetOrigin(0,0);

  EndUpdate(windowPtr);
}

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

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

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

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

void  doActivateWindow(WindowPtr windowPtr,Boolean becomingActive)
{  
  DocStrucHandle  docStrucHdl;

  docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));

  if(becomingActive)
  {
    ActivateControl((*docStrucHdl)->scrollbarNonLiveHdl);
    ActivateControl((*docStrucHdl)->scrollbarLiveHdl);
  }
  else
  {
    DeactivateControl((*docStrucHdl)->scrollbarNonLiveHdl);
    DeactivateControl((*docStrucHdl)->scrollbarLiveHdl);
  }
}

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

void  doOSEvent(EventRecord *eventStrucPtr)
{
  switch((eventStrucPtr->message >> 24) & 0x000000FF)
  {
    case suspendResumeMessage:
      gInBackground = (eventStrucPtr->message & resumeFlag) == 0;
      doActivateWindow(FrontWindow(),!gInBackground);
      break;

    case mouseMovedMessage:
      break;
  }
}

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

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

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

  if(menuID == 0)
    return;

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

  HiliteMenu(0);
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doIncontent

void  doInContent(EventRecord *eventStrucPtr,WindowPtr windowPtr)
{
  DocStrucHandle  docStrucHdl;
  ControlPartCode  controlPartCode;
  ControlHandle    controlHdl;

  docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));

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

  if(controlPartCode = FindControl(eventStrucPtr->where,windowPtr,&controlHdl))
  {
    if(controlHdl == (*docStrucHdl)->scrollbarNonLiveHdl)
      doNonLiveScrollBars(controlPartCode,windowPtr,controlHdl,eventStrucPtr->where);
    else if(controlHdl == (*docStrucHdl)->scrollbarLiveHdl)
      TrackControl(controlHdl,eventStrucPtr->where,(ControlActionUPP) -1);
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doNonLiveScrollBars

void  doNonLiveScrollBars(ControlPartCode controlPartCode,WindowPtr windowPtr,
                   ControlHandle controlHdl,Point mouseXY)
{
  DocStrucHandle  docStrucHdl;
  SInt16          oldControlValue;
  SInt16          scrollDistance;
  RgnHandle       updateRgnHdl;

  docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));

  switch(controlPartCode)
  {
    case kControlIndicatorPart:
      oldControlValue = GetControlValue(controlHdl);
      if(TrackControl(controlHdl,mouseXY,NULL))
      {
        scrollDistance = oldControlValue - GetControlValue(controlHdl);
        if(scrollDistance != 0)
        {
          if(controlHdl == (*docStrucHdl)->scrollbarNonLiveHdl)
          {
            updateRgnHdl = NewRgn();
            ScrollRect(&gPictRectNonLive,scrollDistance,0,updateRgnHdl);
            InvalRgn(updateRgnHdl);
            DisposeRgn(updateRgnHdl);
          }
        }
      }
      break;

    case kControlUpButtonPart:
    case kControlDownButtonPart:
    case kControlPageUpPart:
    case kControlPageDownPart:
      if(controlHdl == (*docStrucHdl)->scrollbarNonLiveHdl)
        TrackControl(controlHdl,mouseXY,(ControlActionUPP) -1);
      break;
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× actionFuncNonLive

pascal void  actionFuncNonLive(ControlHandle controlHdl,ControlPartCode controlPartCode)
{
  WindowPtr        windowPtr;
  DocStrucHandle   docStrucHdl;
  SInt16           scrollDistance;
  SInt16           controlValue;
  RgnHandle        updateRgnHdl;

  if(controlPartCode)
  {
    windowPtr = (*controlHdl)->contrlOwner;
    docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));

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

      case kControlPageUpPart:
      case kControlPageDownPart:
        scrollDistance = (windowPtr->portRect.right - windowPtr->portRect.left - 10);
        break;
    }

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

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

    doMoveScrollBox(controlHdl,scrollDistance);

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

    SetOrigin(GetControlValue((*docStrucHdl)->scrollbarNonLiveHdl),0);
    DrawPicture(gPictHandleNonLive,&gPictRectNonLive);
    SetOrigin(0,0);

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

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× actionFuncLive

pascal void  actionFuncLive(ControlHandle controlHdl,ControlPartCode partCode)
{
  WindowPtr        windowPtr;
  DocStrucHandle   docStrucHdl;
  SInt16           scrollDistance;
  SInt16           controlValue;

  windowPtr = (*controlHdl)->contrlOwner;
  docStrucHdl = (DocStrucHandle) (GetWRefCon(windowPtr));

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

        case kControlPageUpPart:
        case kControlPageDownPart:
          scrollDistance = (windowPtr->portRect.right - windowPtr->portRect.left) - 10;
          break;
      }
    
      if((partCode == kControlDownButtonPart) || (partCode == kControlPageDownPart))
        scrollDistance = -scrollDistance;

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

      doMoveScrollBox(controlHdl,scrollDistance);
    }

    SetOrigin(GetControlValue((*docStrucHdl)->scrollbarLiveHdl),0);
    DrawPicture(gPictHandleLive,&gPictRectLive);
    SetOrigin(0,0);
  }
}

// ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××× doMoveScrollBox

void doMoveScrollBox(ControlHandle controlHdl,SInt16 scrollDistance)
{
  SInt16  oldControlValue, controlValue, controlMax;

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

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

  SetControlValue(controlHdl,controlValue);
}

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

Demonstration Program 1 Comments

When this program is run, the user should:

*   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.

*   Choose items from each of the pop-up menu buttons, noting that the chosen item is
    displayed in the window header.  (Also, in the case of the pop-up menu button 
    titled Sound (which uses the add resource variant of the CDEF and adds the names 
    of the system's 'snd ' resources to its menu), the chosen sound plays.)

*   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.

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

*   Drag the scroll box 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.

*   Drag the scroll box 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.

*   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.

*   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.)

*   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 are activated and deactivated along with the primary group
    box.)

*   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.

#define

The first block of #defines establish constants representing a menu bar resource, a
window resource, menu resources, menu IDs, and items for the drop-down menus.  The
second block establishes constants for control resources and items in the pop-up menu
buttons (other than the add resource variant pop-up menu button).  The penultimate
line defines MAXLONG as the maximum possible long value.  (This value will be assigned
to WaitNextEvent's sleep parameter.)  The last line is a macro which will be utilised
in the function doConcatPStrings.

#typedef

The data type DocStruc is a structure comprising fields in which the handles to the
control structures for the various controls will be stored.  A handle to this
structure will be assigned to the window structure's refCon field

Global Variables

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

gDone is used to control termination of the program, which will occur when the user
selects Quit from the File menu or clicks in the window's close box.  gInBackground
relates to foreground/background switching.

gPixelDepth will be assigned the pixel depth of the main device. gIsColourDevice will
be assigned true if the graphics device is a colour device and false if it is a
monochrome device.  The values in these two variables are required by the Appearance
Manager function SetThemeTextColor.

main

After the system software managers are initialised, two calls to NewControlActionProc
create a routine descriptor for each of two action functions associated with the 
scroll bars.  (If this program was required to be compiled as 68K code only, these 
routine descriptors would not be required.)

The next block sets up the drop-down menus.

The call to the application-defined 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 size of the window font to 10.  This latter is because one of the pop-up
menus will use the window font and Geneva 10pt is the ideal size for small pop-up menu
text.  The call to SetThemeWindowBackground sets an Appearance-compliant background
colour/pattern for the window.  The window's background will be similar to that
applying to dialog boxes, 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 assigned to the window structure's refCon field 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.

The call to the application-defined function doGetDepthAndDevice determines the
current pixel depth of the graphics port, and whether the current graphics device is a
colour device, and assigns the results to the global variables gPixelDepth and
gIsColourDevice.

The main event loop is then entered, and continues until gDone is set to true.

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 from the various 'CNTL' resources.  

At the first line, the root control is created.  The first control created must be
always be the root control (which is implemented as a user pane).

A handle to the structure in which the handles to the control structures will be
stored is then retrieved.  The following calls to GetNewControl create a control
structure for each control, insert the structure into the control list for the
specified window and draw the control.  At the same time, the handle to each control
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.

doEvents

doEvents switches according to the event type reported.  In the keyDown/autoKey case,
the program will terminate if the Command-key equivalent for the Quit item in the File
menu is pressed.

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 application-defined function doInContent is made.

The inGrow case is of particular significance to the scroll bars.  GrowWindow follows
the mouse cursor while the mouse button remains down, returning the new height and
width of the window, or zero if no change was made.  If a change was made, SizeWindow
is called to draw the window in its new size, and an application-defined function 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 following line 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 content region is erased, ZoomWindow is
called to redraw the window in its new state, and an application-defined function is
called to hide, move, resize, and redraw the scroll bars.

doMenuChoice

doMenuChoice handles user choices from the drop-down menus.

The mDemonstration case handle the Demonstration menu.  Firstly, handles to that menu
and 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 is
changed to "Show Colour".  If it is not visible, ShowControl is called and the menu
item is changed to Hide Colour.

At the else if, 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, 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 update region).  The line preceding the UpdateControls
call is incidental to the demonstration.  It simply redraws the window header frame
and text in the window.

doActivate

doActivate is called whenever the application receives an activate event for its
window.  At the second line, a variable is set to indicate whether the window is
becoming active or is about to be made inactive.  This variable is then passed in the
call to an application-defined function doActivateWindow.
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 application-defined function doOSEvent.)

At the first line, GetRootControl gets a handle 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 variable is then set to indicate whether the program is coming to the foreground or
is about to be sent to the background.  This variable is passed in the call to
doActivateWindow.  (Recall that the doesActivateOnFGSwitch flag is set in the 'SIZE'
resource.)

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 handles to
the various control structures.  The second line 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 new function FindControlUnderMouse because there is no requirement to get a handle
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 handle returned by the FindControl call is first compared with the handles
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 an application-defined function is
called to perform further handling

Note that TrackControl, rather than the new 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 handle returned by FindControl does not match the pop-up controls'
handles, it is then tested against the handles to the vertical and horizontal scroll
bar control structures.  If it matches the handle to the vertical scroll bar (which
uses the non-live-feedback CDEF variant), the application-defined function
doVertScrollbar is called to perform further handling.  If it matches the handle to
the horizontal scroll bar (which uses the live-feedback CDEF variant), TrackControl is
called with a Universal Procedure Pointer (UPP) passed in the actionProc parameter. 
The effect of this is that the UPP will be assigned to the cntrlAction field of the
control structure and thus the application-defined action function to which the UPP
relates will be repeatedly called while the mouse button remains down.

ROUTINE DESCRIPTORS AND UNIVERSAL PROCEDURE POINTERS
This call to TrackControl, incidentally, is your first encounter with source code
which differs from that which would have applied prior to the introduction of the
Power Macintosh and its PowerPC microprocessor.

Prior to the introduction of the PowerPC and the associated introduction of the
Universal Headers, the prototype for TrackControl looked like this:

  short  TrackControl(ControlHandle theControl,short thePoint,ProcPtr actionProc);

Notice that the actionProc parameter is of type ProcPtr (procedure pointer).  The
third parameter is thus a pointer to a function, specifically, an action function. 
That being the case, the subject call to TrackControl would have looked look like 
this in the days when source code was intended only for compilation for 680x0
microprocessors:

  TrackControl(controlHdl,eventStrucPtr->where,&actionFunctionHoriz);

With the introduction of the Universal Headers, the prototype for TrackControl
changed to this:

  ControlPartCode  TrackControl(ControlHandle theControl, Point thePoint,
                                 ControlActionUPP actionProc);

Notice that the actionProc parameter is now of type ControlActionUPP.  (UPP 
stands for Universal Procedure Pointer).  

As a result of this change:

*   For source code intended for compilation as either PowerPC code or 680x0 code,
    a call to create each required routine descriptor needs to be made as shown 
    in the main function of this program and the the pointer (UPP) to that 
    routine descriptor needs to be passed in the actionProc parameter of the 
    subject TrackControl call.

*   For source code intended only for compilation as 680x0 code, the routine
    descriptor does not need to created and only a simple procedure pointer 
    (the address of the action function) may be passed in the actionProc 
    parameter, as in the example call above.
If the handle returned by FindControl does not match the handles to any of the pop-up
menu buttons or scroll bars, it must be a handle 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 handle is compared to, in
sequence, the handles to the radio buttons, the checkboxes, and the push buttons.  If
a match is found, the appropriate application-defined function is called to perform
further handling.

doPopupMenuChoice

doPopupMenuChoice further handles mouse-downs in the pop-up menu buttons.  The code
reflects the fact that the Sound pop-up menu button, which uses the add resource
variant of the CDEF, and whose 'CNTL' resource causes the names of the system's 'snd '
resources to be appended to its associated menu, needs to be handled differently from
the others.

If the control handle passed to this function matches the handle to the Sound pop-up
menu button's control structure, the handle to the control's associated 'MENU'
structure is retrieved and used, together with the control's value (that is, the menu
item), in the call to GetMenuItemText.  This call returns, in the last parameter, the
menu item text.  This string is then passed to an application-defined function which
plays the chosen 'snd ' resource.  The call to doDrawMessage draws the menu item text
in the window header frame.
 
Note that the menu handle is not obtained in the same way as would apply in the case
of a drop-down menu (that is, calling GetMenuHandle with the menu ID).  In this case,
GetControlData is called with the tag constant kControlPopupButtonMenuHandleTag passed
in the second parameter, the menu handle being returned in the fifth parameter.

This is a good example of why the new data access mechanism was introduced with 
Mac OS 8 and the Appearance Manager.  In the past, the only way to allow access
to control-specific information was to create a handle to hold such data, place
it in the contrlData field of the control structure, and publish the interface.
For example, pop-up menu buttons place a handle to a block of private information
(called the pop-up menu private data structure) in the controlData field.  This 
structure contains two fields: a handle to the pop-up menu button's menu 
structure (mHandle), and the menu ID (mID).  In the past, before the new data 
access mechanism was introduced, the menu handle would have to have been 
retrieved like this:

   PopupPrivateDataHandle	popupPrivateDataHdl;

   popupPrivateDataHdl = (PopupPrivateDataHandle) (*controlHdl)->contrlData;
   menuHdl = (*popupPrivateDataHdl)->mHandle;

The new data access mechanism means that controls can now allow the outside world
 to access their specialised data without exposing how it is stored.
If the control handle passed to this function does not match that for the Sound pop-up
menu button, the function switches on the control value.  In this case, the further
handling that would normally take place here is replaced in this demonstration by the
simple drawing of some text (which is identical with the menu item's text) 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 (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 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.

At this point, another brief digression is necessary.

ACTION FUNCTIONS
Action functions (sometimes called hook functions or call-back functions) refer to
theability of a Toolbox function to call an application-defined function during its
execution, thus extending the features of the function.

Toolbox calls use Pascal calling conventions, and C and Pascal are different in 
their conventions on the 680x0 processor.  An action function can be written in C;
however, in order to account for the difference in calling conventions, it must be
declared using the pascal keyword.  (Note the first line of the function 
actionFunctionVert.)

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).

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, the action function exits and all scrolling ceases until
the user brings the cursor back within the scroll arrow or gray area, 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.)

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 application-defined function
doMoveScrollBox moves the scroll box the appropriate distance in the appropriate
direction and update the control's value accordingly.  This means, of course, that the
scroll box 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 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.

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, 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 and update the control's value, is
bypassed.  The call to doMoveScrollBox is bypassed because, in live-feedback, the CDEF
moves the scroll box and updates the control's value when the mouse-down is in the
scroll box.

In this demonstration, the action taken after the main block of code has been bypassed
(mouse-down in the scroll box) or executed (mouse-down in the scroll arrows or gray
area) 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
accordingly.

The first two line 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.

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 structure's refCon field.

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.

doDrawMessage, doPlaySound, doConcatPStrings, copyPString, and doGetDepthAndDevice

doDrawMessage, doPlaySound, and doConcatPStrings, copyPString, and doGetDepthAndDevice
are incidental to the demonstration.

Demonstration Program 2 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.

When the program is run, the user should scroll the pictures by dragging the scroll
boxes, clicking in the scroll bar gray areas, clicking in the scroll arrows and
holding the mouse button down while the cursor is in the gray areas and scroll arrows. 
The user should note, when scrolling with the scroll boxes, 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 600 pixels wide and 185 pixels high,
the window "pane" for each picture and scroll bar is 200 pixels wide by 200 pixels
high, the 'CNTL' resources sets the control maximum values to 400, 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.  (This latter
should be recalled at Chapter 23 - Miscellany, where the subject of VBL tasks is
addressed.  See the end of the Demonstration Program Comments section.)  Note that
either method may be used for either scroll bar.

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

main

After the block for the "document" structure is created, two calls to GetNewControl
allocate memory for the control structures, insert the controls into the window's
control list and draw the controls.  

Following each call to GetNewControl, SetControlAction is called with a Universal
Procedure Pointer (UPP) passed in the actionProc parameter.  The effect of this is
that the UPP will be assigned to the cntrlAction field of the control structure and
thus 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.  (This approach differs from the alternative method used in Controls1,
where the UPP is 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 201 pixels.  (Note that the two 'PICT'
resources were created so that the top and left fields of the picFrame Rect are both
zero.)

The next block is applicable only to the PowerPC target, and the if block executes 
only if Mac OS 8.5 or later is present.  The width of each picture in pixels is 
determined and passed in the newViewSize parameter of calls to SetControlViewSize. 
(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 
provided that the user has selected Smart Scrolling on in the Option 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).

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.

As in Controls1:

*   If the mouse-down was in the non-live-feedback scroll bar, an application-defined
    function is called to further handle the mouse-down event.

*   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 function associated with the UPP assigned by SetControlAction 
    to the contrlAction field of the control structure 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:

*   If the mouse-down was in the scroll box (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 control box,
    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,
    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.  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.

*   If the mouse-down was in a scroll arrow or gray area, more specifically in one of
    the non-live-feedback's scroll bar's scroll arrows or gray areas, TrackControl 
    takes control until the user releases the mouse button.  The third parameter in 
    the TrackControl call means that the application-defined function associated 
    with the UPP assigned by SetControlAction to the contrlAction field of the 
    control structure 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, the action
function exits.  The following occurs only when the cursor is within the control.

A pointer to the window structure for the window which "owns" this control is
retrieved from the control structure's contrlOwner field, and the handle to the
document structure is retrieved from that window structure's refCon field.

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, 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 application-defined function
doMoveScrollBox adds/subtracts the distance to scroll to/from the control's current
value and repositions the scroll box 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 InvalRgn, and BeginUpdate is called to ensure that (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, but also while the scroll box 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.

If the mouse-down occurred in the scroll box, 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 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 and updates the control's value when the mouse-down
is in the scroll box.

After the if block has been bypassed (mouse-down in the scroll box) or executed
(mouse-down in the scroll arrows or gray area), the window contents are "scrolled". 
Regardless of whether the picture is being scrolled using the scroll box, the scroll
arrows, or the gray areas, 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

Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, donā€™t remind me I could currently buy him this month Iā€™m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly ā€œborrowedā€ many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »

Price Scanner via MacPrices.net

Apple Watch Ultra 2 now available at Apple fo...
Apple has, for the first time, begun offering Certified Refurbished Apple Watch Ultra 2 models in their online store for $679, or $120 off MSRP. Each Watch includes Appleā€™s standard one-year warranty... Read more
AT&T has the iPhone 14 on sale for only $...
AT&T has the 128GB Apple iPhone 14 available for only $5.99 per month for new and existing customers when you activate unlimited service and use AT&Tā€™s 36 month installment plan. The fine... Read more
Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Appleā€™s new 13ā€³ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13ā€³ M3 MacBook... Read more
You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14ā€³ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Appleā€™s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13ā€³ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13ā€³ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Appleā€™s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14ā€³ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Appleā€™s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: ā€“ Mac mini M2/256GB SSD: $499, save $100 ā€“ Mac mini M2/512GB SSD: $699, save $100 ā€“... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazonā€™s stock tends to come and go: ā€“ 14ā€³ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more

Jobs Board

Sublease Associate Optometrist- *Apple* Val...
Sublease Associate Optometrist- Apple Valley, CA- Target Optical Date: Apr 20, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92307 **Requisition Read more
*Apple* Systems Administrator - JAMF - Syste...
Title: Apple Systems Administrator - JAMF ALTA is supporting a direct hire opportunity. This position is 100% Onsite for initial 3-6 months and then remote 1-2 Read more
Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.