Demonstration Program Controls1
// *******************************************************************************************
// Controls1.c CLASSIC EVENT MODEL
// *******************************************************************************************
//
// This program opens a kWindowFullZoomGrowDocumentProc window containing:
//
// o Three pop-up menu buttons (fixed width, variable width and use window font variants).
//
// o Three non-auto-toggling radio buttons auto-embedded in a primary group box (text title
// variant).
//
// o Three non-auto-toggling checkboxes auto-embedded in a primary group box (text title
// variant).
//
// o Four push buttons (two basic, one left colour icon variant, and one right colour icon
// variant).
//
// o A vertical scroll bar (non live-feedback variant) and a horizontal scroll bar (live-
// feedback variant).
//
// Some controls are created using 'CNTL' resources. Others are created programmatically.
//
// The window also contains a window header frame in which is displayed:
//
// o The menu items chosen from the pop-up menus.
//
// o The identity of a push button when that push button is clicked.
//
// o Scroll bar control values when the scroll arrows or gray areas/tracks of the scroll bars
// are clicked and when the scroll box/scroller is dragged.
//
// The scroll bars are moved and resized when the user resizes or zooms the window; however,
// the scroll bars do not scroll the window contents.
//
// A Demonstration menu allows the user to deactivate the group boxes in which the radio
// buttons and checkboxes are embedded.
//
// The program utilises the following resources:
//
// o A 'plst' resource.
//
// o An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit, and Demonstration menus,
// and the pop-up menus (preload, non-purgeable).
//
// o A 'WIND' resource (purgeable) (initially not visible).
//
// o 'CNTL' resources for those controls not created programmatically.
//
// o Two 'cicn' resources (purgeable) for the colour icon variant buttons.
//
// o An 'hrct' resource and an 'hwin' resource (both purgeable), which provide help balloons
// describing the various controls.
//
// o A 'SIZE' resource with the acceptSuspendResumeEvents, canBackground,
// doesActivateOnFGSwitch, and isHighLevelEventAware flags set.
//
// *******************************************************************************************
// .................................................................................. includes
#include <Carbon.h>
#include <string.h>
// ................................................................................... defines
#define rMenubar 128
#define rWindow 128
#define mAppleApplication 128
#define iAbout 1
#define mFile 129
#define iQuit 12
#define mDemonstration 131
#define iColour 1
#define iGrids 2
#define cPopupFixed 128
#define cPopupWinFont 129
#define cRadiobuttonRed 130
#define cRadiobuttonBlue 131
#define cCheckboxGrid 132
#define cCheckboxGridsnap 133
#define cGroupBoxColour 134
#define cButton 135
#define cButtonLeftIcon 136
#define cScrollbarVert 137
#define kScrollBarWidth 15
#define MAX_UINT32 0xFFFFFFFF
#define MIN(a,b) ((a) < (b) ? (a) : (b))
// .................................................................................. typedefs
typedef struct
{
ControlRef popupFixedRef;
ControlRef popupVariableRef;
ControlRef popupWinFontRef;
ControlRef groupboxColourRef;
ControlRef groupboxGridsRef;
ControlRef buttonRef;
ControlRef buttonDefaultRef;
ControlRef buttonLeftIconRef;
ControlRef buttonRightIconRef;
ControlRef radiobuttonRedRef;
ControlRef radiobuttonWhiteRef;
ControlRef radiobuttonBlueRef;
ControlRef checkboxGridRef;
ControlRef checkboxRulersRef;
ControlRef checkboxGridSnapRef;
ControlRef scrollbarVertRef;
ControlRef scrollbarHorizRef;
} docStruc;
typedef docStruc **docStrucHandle;
// .......................................................................... global variables
ControlActionUPP gActionFunctionVertUPP;
ControlActionUPP gActionFunctionHorizUPP;
Boolean gRunningOnX = false;
WindowRef gWindowRef;
Boolean gDone;
Boolean gInBackground = false;
Str255 gCurrentString;
// ....................................................................... function prototypes
void main (void);
void doPreliminaries (void);
OSErr quitAppEventHandler (AppleEvent *,AppleEvent *,SInt32);
void doGetControls (WindowRef);
void doEvents (EventRecord *);
void doMouseDown (EventRecord *);
void doMenuChoice (SInt32);
void doUpdate (EventRecord *);
void doActivate (EventRecord *);
void doActivateWindow (WindowRef,Boolean);
void doOSEvent (EventRecord *);
void doInContent (EventRecord *,WindowRef);
void doPopupMenuChoice (WindowRef,ControlRef,SInt16);
void doVertScrollbar (ControlPartCode,WindowRef,ControlRef,Point);
void actionFunctionVert (ControlRef,ControlPartCode);
void actionFunctionHoriz (ControlRef,ControlPartCode);
void doMoveScrollBox (ControlRef,SInt16);
void doRadioButtons (ControlRef,WindowRef);
void doCheckboxes (ControlRef);
void doPushButtons (ControlRef,WindowRef);
void doAdjustScrollBars (WindowRef);
void doDrawMessage (WindowRef,Boolean);
void doConcatPStrings (Str255,Str255);
void doCopyPString (Str255,Str255);
void helpTags (WindowRef);
// ************************************************************************************** main
void main(void)
{
MenuBarHandle menubarHdl;
SInt32 response;
MenuRef menuRef;
docStrucHandle docStrucHdl;
EventRecord EventStructure;
// ........................................................................ do preliminaries
doPreliminaries();
// ..................................................... create universal procedure pointers
gActionFunctionVertUPP = NewControlActionUPP((ControlActionProcPtr) actionFunctionVert);
gActionFunctionHorizUPP = NewControlActionUPP((ControlActionProcPtr) actionFunctionHoriz);
// ............................................................... set up menu bar and menus
menubarHdl = GetNewMBar(rMenubar);
if(menubarHdl == NULL)
ExitToShell();
SetMenuBar(menubarHdl);
DrawMenuBar();
Gestalt(gestaltMenuMgrAttr,&response);
if(response & gestaltMenuMgrAquaLayoutMask)
{
menuRef = GetMenuRef(mFile);
if(menuRef != NULL)
{
DeleteMenuItem(menuRef,iQuit);
DeleteMenuItem(menuRef,iQuit - 1);
DisableMenuItem(menuRef,0);
}
gRunningOnX = true;
}
// ................................................. initial advisory text for window header
doCopyPString("\pBalloon and Help tag help is available",gCurrentString);
// ........ open a window, set font size, set Appearance-compliant colour/pattern for window
if(!(gWindowRef = GetNewCWindow(rWindow,NULL,(WindowRef) -1)))
ExitToShell();
SetPortWindowPort(gWindowRef);
UseThemeFont(kThemeSmallSystemFont,smSystemScript);
SetThemeWindowBackground(gWindowRef,kThemeBrushDialogBackgroundActive,false);
// ........... get block for document structure, assign handle to window record refCon field
if(!(docStrucHdl = (docStrucHandle) NewHandle(sizeof(docStruc))))
ExitToShell();
SetWRefCon(gWindowRef,(SInt32) docStrucHdl);
// ........................ get controls, adjust scroll bars, get help tags, and show window
doGetControls(gWindowRef);
doAdjustScrollBars(gWindowRef);
if(gRunningOnX)
helpTags(gWindowRef);
ShowWindow(gWindowRef);
// ......................................................................... enter eventLoop
gDone = false;
while(!gDone)
{
if(WaitNextEvent(everyEvent,&EventStructure,MAX_UINT32,NULL))
doEvents(&EventStructure);
}
}
// *************************************************************************** doPreliminaries
void doPreliminaries(void)
{
OSErr osError;
MoreMasterPointers(128);
InitCursor();
FlushEvents(everyEvent,0);
osError = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
NewAEEventHandlerUPP((AEEventHandlerProcPtr) quitAppEventHandler),
0L,false);
if(osError != noErr)
ExitToShell();
}
// **************************************************************************** doQuitAppEvent
OSErr quitAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
OSErr osError;
DescType returnedType;
Size actualSize;
osError = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,NULL,0,
&actualSize);
if(osError == errAEDescNotFound)
{
gDone = true;
osError = noErr;
}
else if(osError == noErr)
osError = errAEParamMissed;
return osError;
}
// ***************************************************************************** doGetControls
void doGetControls(WindowRef windowRef)
{
ControlRef controlRef;
docStrucHandle docStrucHdl;
OSStatus osError;
Rect popupVariableRect = { 73, 25, 93,245 };
Rect radioButtonWhiteRect = { 183, 35,201, 92 };
Rect checkboxRulersRect = { 183,138,201,242 };
Rect groupboxGridsRect = { 136,123,236,252 };
Rect buttonDefaultRect = { 252,141,272,210 };
Rect buttonRightIconRect = { 285,141,305,220 };
Rect scrollbarVertRect = { 0, 0, 16,100 };
ControlButtonContentInfo buttonContentInfo;
Boolean booleanData = true;
ControlFontStyleRec controlFontStyleStruc;
if(!gRunningOnX)
CreateRootControl(windowRef,&controlRef);
docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));
// ...................................................................... popup menu buttons
if(!((*docStrucHdl)->popupFixedRef = GetNewControl(cPopupFixed,windowRef)))
ExitToShell();
if((osError = CreatePopupButtonControl(windowRef,&popupVariableRect,CFSTR("Time Zone:"),
132,true,76,popupTitleLeftJust,popupTitleNoStyle,
&(*docStrucHdl)->popupVariableRef)) == noErr)
ShowControl((*docStrucHdl)->popupVariableRef);
else
ExitToShell();
if(!((*docStrucHdl)->popupWinFontRef = GetNewControl(cPopupWinFont,windowRef)))
ExitToShell();
// ........................................................................... radio buttons
if(!((*docStrucHdl)->radiobuttonRedRef = GetNewControl(cRadiobuttonRed,windowRef)))
ExitToShell();
if((osError = CreateRadioButtonControl(windowRef,&radioButtonWhiteRect,CFSTR("White"),0,
false,&(*docStrucHdl)->radiobuttonWhiteRef)) == noErr)
ShowControl((*docStrucHdl)->radiobuttonWhiteRef);
else
ExitToShell();
if(!((*docStrucHdl)->radiobuttonBlueRef = GetNewControl(cRadiobuttonBlue,windowRef)))
ExitToShell();
// .............................................................................. checkboxes
if(!((*docStrucHdl)->checkboxGridRef = GetNewControl(cCheckboxGrid,windowRef)))
ExitToShell();
if((osError = CreateCheckBoxControl(windowRef,&checkboxRulersRect,CFSTR("Rulers"),0,false,
&(*docStrucHdl)->checkboxRulersRef)) == noErr)
ShowControl((*docStrucHdl)->checkboxRulersRef);
else
ExitToShell();
if(!((*docStrucHdl)->checkboxGridSnapRef = GetNewControl(cCheckboxGridsnap,windowRef)))
ExitToShell();
// ............................................................................. group boxes
if(!((*docStrucHdl)->groupboxColourRef = GetNewControl(cGroupBoxColour,windowRef)))
ExitToShell();
if((osError = CreateGroupBoxControl(windowRef,&groupboxGridsRect,CFSTR("Grids"),true,
&(*docStrucHdl)->groupboxGridsRef)) == noErr)
ShowControl((*docStrucHdl)->groupboxGridsRef);
else
ExitToShell();
// ............................................................................ push buttons
if(!((*docStrucHdl)->buttonRef = GetNewControl(cButton,windowRef)))
ExitToShell();
if((osError = CreatePushButtonControl(windowRef,&buttonDefaultRect,CFSTR("OK"),
&(*docStrucHdl)->buttonDefaultRef)) == noErr)
ShowControl((*docStrucHdl)->buttonDefaultRef);
else
ExitToShell();
if(!((*docStrucHdl)->buttonLeftIconRef = GetNewControl(cButtonLeftIcon,windowRef)))
ExitToShell();
buttonContentInfo.contentType = kControlContentCIconRes;
buttonContentInfo.u.resID = 101;
if((osError = CreatePushButtonWithIconControl(windowRef,&buttonRightIconRect,
CFSTR("Button"),&buttonContentInfo,
kControlPushButtonIconOnRight,
&(*docStrucHdl)->buttonRightIconRef)) == noErr)
ShowControl((*docStrucHdl)->buttonRightIconRef);
else
ExitToShell();
// ............................................................................. scroll bars
if(!((*docStrucHdl)->scrollbarVertRef = GetNewControl(cScrollbarVert,windowRef)))
ExitToShell();
if((osError = CreateScrollBarControl(windowRef,&scrollbarVertRect,0,0,100,100,true,
gActionFunctionHorizUPP,
&(*docStrucHdl)->scrollbarHorizRef)) == noErr)
ShowControl((*docStrucHdl)->scrollbarHorizRef);
else
ExitToShell();
// .........................................................................................
AutoEmbedControl((*docStrucHdl)->radiobuttonRedRef,windowRef);
AutoEmbedControl((*docStrucHdl)->radiobuttonWhiteRef,windowRef);
AutoEmbedControl((*docStrucHdl)->radiobuttonBlueRef,windowRef);
AutoEmbedControl((*docStrucHdl)->checkboxGridRef,windowRef);
AutoEmbedControl((*docStrucHdl)->checkboxRulersRef,windowRef);
AutoEmbedControl((*docStrucHdl)->checkboxGridSnapRef,windowRef);
SetControlData((*docStrucHdl)->buttonDefaultRef,kControlEntireControl,
kControlPushButtonDefaultTag,sizeof(booleanData),&booleanData);
controlFontStyleStruc.flags = kControlUseFontMask;
controlFontStyleStruc.font = kControlFontSmallSystemFont;
SetControlFontStyle((*docStrucHdl)->buttonLeftIconRef,&controlFontStyleStruc);
controlFontStyleStruc.font = kControlFontSmallBoldSystemFont;
SetControlFontStyle((*docStrucHdl)->buttonRightIconRef,&controlFontStyleStruc);
DeactivateControl((*docStrucHdl)->checkboxRulersRef);
}
// ********************************************************************************** doEvents
void doEvents(EventRecord *eventStrucPtr)
{
SInt32 menuChoice;
MenuID menuID;
MenuItemIndex menuItem;
switch(eventStrucPtr->what)
{
case kHighLevelEvent:
AEProcessAppleEvent(eventStrucPtr);
break;
case keyDown:
if((eventStrucPtr->modifiers & cmdKey) != 0)
{
menuChoice = MenuEvent(eventStrucPtr);
menuID = HiWord(menuChoice);
menuItem = LoWord(menuChoice);
if(menuID == mFile && menuItem == iQuit)
gDone = true;
}
break;
case mouseDown:
doMouseDown(eventStrucPtr);
break;
case updateEvt:
doUpdate(eventStrucPtr);
break;
case activateEvt:
doActivate(eventStrucPtr);
break;
case osEvt:
doOSEvent(eventStrucPtr);
break;
}
}
// ******************************************************************************* doMouseDown
void doMouseDown(EventRecord *eventStrucPtr)
{
WindowPartCode partCode, zoomPart;
WindowRef windowRef;
Rect constraintRect, mainScreenRect, portRect;
BitMap screenBits;
Point standardStateHeightAndWidth;
partCode = FindWindow(eventStrucPtr->where,&windowRef);
switch(partCode)
{
case inMenuBar:
doMenuChoice(MenuSelect(eventStrucPtr->where));
break;
case inContent:
if(windowRef != FrontWindow())
SelectWindow(windowRef);
else
doInContent(eventStrucPtr,windowRef);
break;
case inDrag:
DragWindow(windowRef,eventStrucPtr->where,NULL);
break;
case inGoAway:
if(TrackGoAway(windowRef,eventStrucPtr->where) == true)
gDone = true;
break;
case inGrow:
constraintRect.top = 341;
constraintRect.left = 287;
constraintRect.bottom = constraintRect.right = 32767;
ResizeWindow(windowRef,eventStrucPtr->where,&constraintRect,NULL);
doAdjustScrollBars(windowRef);
doDrawMessage(windowRef,true);
break;
case inZoomIn:
case inZoomOut:
mainScreenRect = GetQDGlobalsScreenBits(&screenBits)->bounds;
standardStateHeightAndWidth.v = mainScreenRect.bottom - 75;
standardStateHeightAndWidth.h = 600;
if(IsWindowInStandardState(windowRef,&standardStateHeightAndWidth,NULL))
zoomPart = inZoomIn;
else
zoomPart = inZoomOut;
if(TrackBox(windowRef,eventStrucPtr->where,partCode))
{
GetWindowPortBounds(windowRef,&portRect);
EraseRect(&portRect);
ZoomWindowIdeal(windowRef,zoomPart,&standardStateHeightAndWidth);
doAdjustScrollBars(windowRef);
}
break;
}
}
// ****************************************************************************** doMenuChoice
void doMenuChoice(SInt32 menuChoice)
{
MenuID menuID;
MenuItemIndex menuItem;
MenuRef menuRef;
WindowRef windowRef;
docStrucHandle docStrucHdl;
menuID = HiWord(menuChoice);
menuItem = LoWord(menuChoice);
if(menuID == 0)
return;
switch(menuID)
{
case mAppleApplication:
if(menuItem == iAbout)
SysBeep(10);
break;
case mFile:
if(menuItem == iQuit)
gDone = true;
break;
case mDemonstration:
menuRef = GetMenuRef(mDemonstration);
windowRef = FrontWindow();
docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));
if(menuItem == iColour)
{
if(IsControlVisible((*docStrucHdl)->groupboxColourRef))
{
HideControl((*docStrucHdl)->groupboxColourRef);
SetMenuItemText(menuRef,iColour,"\pShow Colour");
}
else
{
ShowControl((*docStrucHdl)->groupboxColourRef);
SetMenuItemText(menuRef,iColour,"\pHide Colour");
}
}
else if(menuItem == iGrids)
{
if(IsControlActive((*docStrucHdl)->groupboxGridsRef))
{
DeactivateControl((*docStrucHdl)->groupboxGridsRef);
SetMenuItemText(menuRef,iGrids,"\pActivate Grids");
}
else
{
ActivateControl((*docStrucHdl)->groupboxGridsRef);
SetMenuItemText(menuRef,iGrids,"\pDeactivate Grids");
}
}
break;
}
HiliteMenu(0);
}
// ********************************************************************************** doUpdate
void doUpdate(EventRecord *eventStrucPtr)
{
WindowRef windowRef;
RgnHandle regionHdl;
windowRef = (WindowRef) eventStrucPtr->message;
BeginUpdate(windowRef);
SetPortWindowPort(windowRef);
doDrawMessage(windowRef,!gInBackground);
if(regionHdl = NewRgn())
{
GetPortVisibleRegion(GetWindowPort(windowRef),regionHdl);
UpdateControls(windowRef,regionHdl);
DisposeRgn(regionHdl);
}
EndUpdate(windowRef);
}
// ******************************************************************************** doActivate
void doActivate(EventRecord *eventStrucPtr)
{
WindowRef windowRef;
Boolean becomingActive;
windowRef = (WindowRef) eventStrucPtr->message;
becomingActive = ((eventStrucPtr->modifiers & activeFlag) == activeFlag);
doActivateWindow(windowRef,becomingActive);
}
// ************************************************************************** doActivateWindow
void doActivateWindow(WindowRef windowRef,Boolean becomingActive)
{
ControlRef controlRef;
GetRootControl(windowRef,&controlRef);
if(becomingActive)
{
ActivateControl(controlRef);
doDrawMessage(windowRef,becomingActive);
}
else
{
DeactivateControl(controlRef);
doDrawMessage(windowRef,becomingActive);
}
}
// ********************************************************************************* doOSEvent
void doOSEvent(EventRecord *eventStrucPtr)
{
switch((eventStrucPtr->message >> 24) & 0x000000FF)
{
case suspendResumeMessage:
if((eventStrucPtr->message & resumeFlag) == 1)
{
SetThemeCursor(kThemeArrowCursor);
gInBackground = false;
}
else
gInBackground = true;
break;
}
}
// ******************************************************************************* doInContent
void doInContent(EventRecord *eventStrucPtr,WindowRef windowRef)
{
docStrucHandle docStrucHdl;
ControlRef controlRef;
SInt16 controlValue, controlPartCode;
docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));
SetPortWindowPort(windowRef);
GlobalToLocal(&eventStrucPtr->where);
if(controlPartCode = FindControl(eventStrucPtr->where,windowRef,&controlRef))
{
if(controlRef == (*docStrucHdl)->popupFixedRef ||
controlRef == (*docStrucHdl)->popupVariableRef ||
controlRef == (*docStrucHdl)->popupWinFontRef)
{
TrackControl(controlRef,eventStrucPtr->where,(ControlActionUPP) -1);
controlValue = GetControlValue(controlRef);
doPopupMenuChoice(windowRef,controlRef,controlValue);
}
else if(controlRef == (*docStrucHdl)->scrollbarVertRef)
{
doVertScrollbar(controlPartCode,windowRef,controlRef,eventStrucPtr->where);
}
else if(controlRef == (*docStrucHdl)->scrollbarHorizRef)
{
TrackControl(controlRef,eventStrucPtr->where,gActionFunctionHorizUPP);
}
else
{
if(TrackControl(controlRef,eventStrucPtr->where,NULL))
{
if(controlRef == (*docStrucHdl)->radiobuttonRedRef ||
controlRef == (*docStrucHdl)->radiobuttonWhiteRef ||
controlRef == (*docStrucHdl)->radiobuttonBlueRef)
{
doRadioButtons(controlRef,windowRef);
}
if(controlRef == (*docStrucHdl)->checkboxGridRef ||
controlRef == (*docStrucHdl)->checkboxRulersRef ||
controlRef == (*docStrucHdl)->checkboxGridSnapRef)
{
doCheckboxes(controlRef);
}
if(controlRef == (*docStrucHdl)->buttonRef ||
controlRef == (*docStrucHdl)->buttonDefaultRef ||
controlRef == (*docStrucHdl)->buttonLeftIconRef ||
controlRef == (*docStrucHdl)->buttonRightIconRef)
{
doPushButtons(controlRef,windowRef);
}
}
}
}
}
// ************************************************************************* doPopupMenuChoice
void doPopupMenuChoice(WindowRef windowRef,ControlRef controlRef,SInt16 controlValue)
{
MenuRef menuRef;
Size actualSize;
GetControlData(controlRef,kControlEntireControl,kControlPopupButtonMenuHandleTag,
sizeof(menuRef),&menuRef,&actualSize);
GetMenuItemText(menuRef,controlValue,gCurrentString);
doDrawMessage(windowRef,true);
}
// *************************************************************************** doVertScrollbar
void doVertScrollbar(ControlPartCode controlPartCode,WindowRef windowRef,
ControlRef controlRef,Point mouseXY)
{
Str255 valueString;
doCopyPString("\pScroll Bar Control Value: ",gCurrentString);
switch(controlPartCode)
{
case kControlIndicatorPart:
if(TrackControl(controlRef,mouseXY,NULL))
{
NumToString((SInt32) GetControlValue(controlRef),valueString);
doConcatPStrings(gCurrentString,valueString);
doDrawMessage(windowRef,true);
}
break;
case kControlUpButtonPart:
case kControlDownButtonPart:
case kControlPageUpPart:
case kControlPageDownPart:
TrackControl(controlRef,mouseXY,gActionFunctionVertUPP);
break;
}
}
// ************************************************************************ actionFunctionVert
void actionFunctionVert(ControlRef controlRef,ControlPartCode controlPartCode)
{
SInt16 scrollDistance, controlValue;
Str255 valueString;
WindowRef windowRef;
doCopyPString("\pScroll Bar Control Value: ",gCurrentString);
if(controlPartCode)
{
switch(controlPartCode)
{
case kControlUpButtonPart:
case kControlDownButtonPart:
scrollDistance = 2;
break;
case kControlPageUpPart:
case kControlPageDownPart:
scrollDistance = 55;
break;
}
if((controlPartCode == kControlDownButtonPart) ||
(controlPartCode == kControlPageDownPart))
scrollDistance = -scrollDistance;
controlValue = GetControlValue(controlRef);
if(((controlValue == GetControlMaximum(controlRef)) && scrollDistance < 0) ||
((controlValue == GetControlMinimum(controlRef)) && scrollDistance > 0))
return;
doMoveScrollBox(controlRef,scrollDistance);
NumToString((SInt32) GetControlValue(controlRef),valueString);
doConcatPStrings(gCurrentString,valueString);
windowRef = GetControlOwner(controlRef);
doDrawMessage(windowRef,true);
}
}
// *********************************************************************** actionFunctionHoriz
void actionFunctionHoriz(ControlRef controlRef,ControlPartCode controlPartCode)
{
SInt16 scrollDistance, controlValue;
Str255 valueString;
WindowRef windowRef;
doCopyPString("\pScroll Bar Control Value: ",gCurrentString);
if(partCode != kControlNoPart)
{
if(controlPartCode != kControlIndicatorPart)
{
switch(controlPartCode)
{
case kControlUpButtonPart:
case kControlDownButtonPart:
scrollDistance = 2;
break;
case kControlPageUpPart:
case kControlPageDownPart:
scrollDistance = 55;
break;
}
if((controlPartCode == kControlDownButtonPart) ||
(controlPartCode == kControlPageDownPart))
scrollDistance = -scrollDistance;
controlValue = GetControlValue(controlRef);
if(((controlValue == GetControlMaximum(controlRef)) && scrollDistance < 0) ||
((controlValue == GetControlMinimum(controlRef)) && scrollDistance > 0))
return;
doMoveScrollBox(controlRef,scrollDistance);
}
NumToString((SInt32) GetControlValue(controlRef),valueString);
doConcatPStrings(gCurrentString,valueString);
windowRef = GetControlOwner(controlRef);
doDrawMessage(windowRef,true);
}
}
// *************************************************************************** doMoveScrollBox
void doMoveScrollBox(ControlRef controlRef,SInt16 scrollDistance)
{
SInt16 oldControlValue, controlValue, controlMax;
oldControlValue = GetControlValue(controlRef);
controlMax = GetControlMaximum(controlRef);
controlValue = oldControlValue - scrollDistance;
if(controlValue < 0)
controlValue = 0;
else if(controlValue > controlMax)
controlValue = controlMax;
SetControlValue(controlRef,controlValue);
}
// **************************************************************************** doRadioButtons
void doRadioButtons(ControlRef controlRef,WindowRef windowRef)
{
docStrucHandle docStrucHdl;
docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));
SetControlValue((*docStrucHdl)->radiobuttonRedRef,kControlRadioButtonUncheckedValue);
SetControlValue((*docStrucHdl)->radiobuttonWhiteRef,kControlRadioButtonUncheckedValue);
SetControlValue((*docStrucHdl)->radiobuttonBlueRef,kControlRadioButtonUncheckedValue);
SetControlValue(controlRef,kControlRadioButtonCheckedValue);
}
// ****************************************************************************** doCheckboxes
void doCheckboxes(ControlRef controlRef)
{
SetControlValue(controlRef,!GetControlValue(controlRef));
}
// ***************************************************************************** doPushButtons
void doPushButtons(ControlRef controlRef,WindowRef windowRef)
{
docStrucHandle docStrucHdl;
docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));
if(controlRef == (*docStrucHdl)->buttonRef)
{
doCopyPString("\pButton",gCurrentString);
doDrawMessage(windowRef,true);
}
else if(controlRef == (*docStrucHdl)->buttonDefaultRef)
{
doCopyPString("\pDefault Button",gCurrentString);
doDrawMessage(windowRef,true);
}
else if(controlRef == (*docStrucHdl)->buttonLeftIconRef)
{
doCopyPString("\pLeft Icon Button",gCurrentString);
doDrawMessage(windowRef,true);
}
else if(controlRef == (*docStrucHdl)->buttonRightIconRef)
{
doCopyPString("\pRight Icon Button",gCurrentString);
doDrawMessage(windowRef,true);
}
}
// ************************************************************************ doAdjustScrollBars
void doAdjustScrollBars(WindowRef windowRef)
{
Rect portRect;
docStrucHandle docStrucHdl;
docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));
GetWindowPortBounds(windowRef,&portRect);
HideControl((*docStrucHdl)->scrollbarVertRef);
HideControl((*docStrucHdl)->scrollbarHorizRef);
MoveControl((*docStrucHdl)->scrollbarVertRef,portRect.right - kScrollBarWidth,
portRect.top + 25);
MoveControl((*docStrucHdl)->scrollbarHorizRef,portRect.left -1,
portRect.bottom - kScrollBarWidth);
SizeControl((*docStrucHdl)->scrollbarVertRef,16, portRect.bottom - 39);
SizeControl((*docStrucHdl)->scrollbarHorizRef, portRect.right - 13,16);
ShowControl((*docStrucHdl)->scrollbarVertRef);
ShowControl((*docStrucHdl)->scrollbarHorizRef);
SetControlMaximum((*docStrucHdl)->scrollbarVertRef,portRect.bottom - portRect.top - 25
- kScrollBarWidth);
SetControlMaximum((*docStrucHdl)->scrollbarHorizRef,portRect.right - portRect.left
- kScrollBarWidth);
}
// ***************************************************************************** doDrawMessage
void doDrawMessage(WindowRef windowRef,Boolean inState)
{
Rect portRect, headerRect;
CFStringRef stringRef;
Rect textBoxRect;
if(windowRef == gWindowRef)
{
SetPortWindowPort(windowRef);
GetWindowPortBounds(windowRef,&portRect);
SetRect(&headerRect,portRect.left - 1,portRect.top - 1,portRect.right + 1,
portRect.top + 26);
DrawThemeWindowHeader(&headerRect,inState);
if(inState == kThemeStateActive)
TextMode(srcOr);
else
TextMode(grayishTextOr);
stringRef = CFStringCreateWithPascalString(NULL,gCurrentString,
kCFStringEncodingMacRoman);
SetRect(&textBoxRect,portRect.left,6,portRect.right,21);
DrawThemeTextBox(stringRef,kThemeSmallSystemFont,0,true,&textBoxRect,teJustCenter,NULL);
if(stringRef != NULL)
CFRelease(stringRef);
TextMode(srcOr);
}
}
// ************************************************************************** doConcatPStrings
void doConcatPStrings(Str255 targetString,Str255 appendString)
{
SInt16 appendLength;
appendLength = MIN(appendString[0],255 - targetString[0]);
if(appendLength > 0)
{
BlockMoveData(appendString+1,targetString+targetString[0]+1,(SInt32) appendLength);
targetString[0] += appendLength;
}
}
// ***************************************************************************** doCopyPString
void doCopyPString(Str255 sourceString,Str255 destinationString)
{
SInt16 stringLength;
stringLength = sourceString[0];
BlockMove(sourceString + 1,destinationString + 1,stringLength);
destinationString[0] = stringLength;
}
// ********************************************************************************** helpTags
void helpTags(WindowRef windowRef)
{
docStrucHandle docStrucHdl;
HMHelpContentRec helpContent;
memset(&helpContent,0,sizeof(helpContent));
docStrucHdl = (docStrucHandle) (GetWRefCon(windowRef));
HMSetTagDelay(500);
HMSetHelpTagsDisplayed(true);
helpContent.version = kMacHelpVersion;
helpContent.tagSide = kHMOutsideTopCenterAligned;
helpContent.content[kHMMinimumContentIndex].contentType = kHMStringResContent;
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmResID = 128;
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 1;
HMSetControlHelpContent((*docStrucHdl)->popupFixedRef,&helpContent);
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 2;
HMSetControlHelpContent((*docStrucHdl)->popupVariableRef,&helpContent);
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 3;
HMSetControlHelpContent((*docStrucHdl)->popupWinFontRef,&helpContent);
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 4;
HMSetControlHelpContent((*docStrucHdl)->groupboxColourRef,&helpContent);
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 5;
HMSetControlHelpContent((*docStrucHdl)->groupboxGridsRef,&helpContent);
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 6;
HMSetControlHelpContent((*docStrucHdl)->buttonRef,&helpContent);
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 7;
HMSetControlHelpContent((*docStrucHdl)->buttonDefaultRef,&helpContent);
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 8;
HMSetControlHelpContent((*docStrucHdl)->buttonLeftIconRef,&helpContent);
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 9;
HMSetControlHelpContent((*docStrucHdl)->buttonRightIconRef,&helpContent);
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 10;
HMSetControlHelpContent((*docStrucHdl)->scrollbarVertRef,&helpContent);
helpContent.content[kHMMinimumContentIndex].u.tagStringRes.hmmIndex = 11;
HMSetControlHelpContent((*docStrucHdl)->scrollbarHorizRef,&helpContent);
}
// *******************************************************************************************
Demonstration Program Controls1 Comments
When this program is run, the user should:
o On Mac OS 8/9, choose Show Balloons from the Help menu and peruse the help balloons which are
invoked when the mouse cursor is moved over the various controls.
o On Mac OS X, peruse the Help tags which are invoked when the mouse cursor is held over the
various controls.
o Choose items from each of the pop-up menu buttons, noting that the chosen item is displayed
in the window header.
o Click on the radio buttons, checkboxes, and push buttons, noting particularly that the radio
button settings are mutually exclusive and that checkbox settings are not.
o Click in the scroll bar arrows and gray areas/tracks of the scroll bars, noting the control
value changes displayed in the window header.
o Drag the scroll box/scroller of the vertical scroll bar (which uses the non-live-feedback
CDEF variant), noting that only a ghosted outline is dragged and that the control value does
not change until the mouse button is released.
o Drag the scroll box/scroller of the horizontal scroll bar (which uses the live-feedback CDEF
variant), noting that the scroll box proper is dragged and that the control value is
continually updated during the drag.
o Resize and zoom the window, noting (1) that the scroll bars are moved and resized in response
to those actions and (2) the change in the maximum value of the scroll bars.
o Send the program to the background and bring it to the foreground, noting the changes to the
appearance of the controls. (The program activates and deactivates the root control only;
however, because all controls are embedded in the root control, all controls are activated
and deactivated along with the root control.)
o Alternately hide and show the Colour primary group box by choosing the associated item in the
Demonstration menu. (The program hides and shows the primary group box only; however,
because the radio buttons are embedded in the primary group box, those controls hidden and
shown along with the primary group box.)
o Alternately activate and deactivate the Grids primary group box by choosing the associated
item in the Demonstration menu. (The program activates and deactivates the primary group box
only; however, because the checkboxes are embedded in the primary group box, those controls
are activated and deactivated along with the primary group box.) Also note the latency of
the Show Rulers checkbox. It is deactivated at program launch, and retains that status when
the primary group box is deactivated and then re-activated.
In this program (and all others that use Help tags), the header file string.h is included and
the library MSL C.PPC.Lib has been added to the project because of the use of memset in the
function helpTags.
If you wish to display the Help tags, rather than Balloon help, on Mac OS 8/9:
o Comment out the line "if(gRunningOnX)" before the line "helpTags(gWindowRef);"
o In the function helpTags, change "kHMOutsideTopCenterAligned" to
"kHMOutsideLeftCenterAligned". (At the time of writing, use of any but the first thirteen
positioning constants in MacHelp.h defeated the display of Help tags on Mac OS 8/9.)
Help tag creation is addressed at Chapter 25.
defines
Constants are established for 'CNTL' resources for those controls not created programmatically.
typedef
The data type docStruc is a structure comprising fields in which the references to the control
objects for the various controls will be stored. A handle to this structure will be stored in
the window's window object.
Global Variables
actionFunctionVertUPP and actionFunctionHorizUPP will be assigned universal procedure pointers
to action functions for the scroll bars.
main
Universal procedure pointers are created for the action functions for the two scroll bars.
The call to the function copyPString causes the string in the first parameter to be copied to
the global variable gCurrentString. The string in gCurrentString, which will be changed at
various points in the code, will be drawn in the window header frame.
The next block opens a window, makes the window's graphics port the current port, and sets the
graphics port's font to the small system font. This latter is because one of the pop-up menus
will use the window font. SetThemeWindowBackground is called to set the background
colour/pattern for the window. The window's background will be similar to that applying to Mac
OS 8/9 dialogs, which is appropriate for a window containing nothing but controls.
The call to NewHandle gets a relocatable block the size of one docStruc structure. The handle
to the block is stored in the window's window object by the call to SetWRefCon.
In the next block, doGetControls creates and draws the controls, doAdjustScrollBars resizes and
locates the scroll bars, and sets their maximum value, according to the dimensions of the
window's port rectangle, and ShowWindow makes the window visible.
Note that error handling here and in other areas of this demonstration program is somewhat
rudimentary. In the unlikely event that certain calls fail, ExitToShell is called to terminate
the program.
doGetControls
The function doGetControls creates the controls.
At the first line, if the program is running on Mac OS 8/9, CreateRootControl is called to
create a root control for the window. On Mac OS 8/9, the first control created must be always
be the root control (which is implemented as a user pane). This call is not necessary on Mac
OS X because, on Mac OS X, root controls are created automatically for windows which have at
least one control.
A handle to the structure in which the references to the control objects will be stored is then
retrieved.
The controls are then created, some from 'CNTL' resources using GetNewControl and some
programmatically. All of these calls create a control object for the relevant control and
insert the object into the control list for the specified window. GetNewControl draws the
controls created from 'CNTL' resources. In the case of controls created programmatically,
ShowControl must me called to cause the control to be drawn. The reference to each control
object is assigned to the appropriate field of the window's "document" structure.
Because of the sequence in which the controls are created and initially drawn, the group boxes
would ordinarily over-draw the radio buttons and checkboxes. However, the calls to
AutoEmbedControl embed these latter controls in their respective group boxes, ensuring that
they will be drawn after (or "on top of") the group boxes. (AutoEmbedControl, rather than
EmbedControl, is used in this instance because the radio button rectangles are visually
contained by their respective group box rectangles.)
The call to SetControlData, with kControlPushButtonDefaultTag passed in the third parameter
causes the default outline to be drawn around the specified push button.
In the next block, the title fonts of the left colour icon variant and right colour icon
variant push buttons are changed. Firstly, the flags and font fields of a control font style
structure are assigned constants so that the following call to SetControlFontStyle will set the
title font of the left colour icon variant push button to the small system font. The font
field is then changed so that the second call to SetControlFontStyle will set the title font of
the right colour icon variant push button to the small emphasized system font.
Lastly, the checkbox titled Rulers is disabled. This is for the purpose of the latency aspect
of the demonstration.
doMouseDown
doMouseDown switches according to the window part in which a mouseDown event occurs.
At the inContent case, if the window in which the mouse-down occurred is the front window, and
since all of the controls are located in the window's content region, a call to the function
doInContent is made.
The inGrow case is of particular significance to the scroll bars. Following the call to
ResizeWindow, the function doAdjustScrollBars is called to erase, move, resize, and redraw the
scroll bars and reset the control's maximum value according to the new size of the window.
(The call to doDrawMessage is incidental to the demonstration. It simply redraws the window
header frame and text in the window.)
The inZoomIn/InZoomOut case is also of significance to the scroll bars. If the call to
TrackBox returns a non-zero value, the window's port rectangle is erased before ZoomWindowIdeal
zooms the window. Following the call to ZoomWindowIdeal the function doAdjustScrollBars is
called to hide, move, resize, and redraw the scroll bars.
doMenuChoice
doMenuChoice handles user choices from the pull-down menus.
The mDemonstration case handles the Demonstration menu. Firstly, reference to that menu and a
handle to the window's "document" structure are obtained.
If the menu item is the Colour item, IsControlVisible is called to determine the current
visibility status of the Colour group box. If it is visible, the call to HideControl hides the
group box and its embedded radio buttons; also, the menu item text is changed to "Show Colour".
If it is not visible, ShowControl is called and the menu item text is changed to "Hide Colour".
If the menu item is the Grids item, the same general sequence takes place in respect of the
Grids group box. This time, however, IsControlActive is used to determine whether the control
is active or inactive, and ActivateControl and DeactivateControl are called, and the menu item
toggled, as appropriate. Note that, because of latency, the application does not have to
"remember" that one of the embedded checkboxes was deactivated at program start. The Control
Manager does the remembering.
doUpdate
doUpdate is called whenever the application receives an update event for its window. Between
the usual calls to BeginUpdate and EndUpdate (ignored on Mac OS X), the window's graphics port
is set as the current port for drawing, and UpdateControls is called to draw those controls
intersecting the current visible region (which, between the BeginUpdate and EndUpdate calls,
equates to the Mac OS 8/9 update region). The line preceding the if block is incidental to the
demonstration. It simply redraws the window header frame and text in the window.
doActivateWindow
doActivateWindow switches according to whether the specified window is becoming active or is
about to be made inactive. (Actually, doActivateWindow will never be called by doActivate in
this program because the program only opens one window. It will however, be called by the
function doOSEvent.)
At the first line, GetRootControl gets a reference to the window's root control.
If the window is becoming active, ActivateControl is called to activate the root control.
Since all other controls are embedded in the root control, all controls will be activated by
this call.
If the window is about to become inactive, DeactivateControl is called to deactivate the root
control. Since all other controls are embedded in the root control, all controls will be
deactivated by this call.
The calls to doDrawMessage are incidental to the demonstration. They simply redraw the window
header frame and text in the window in the appropriate mode (inactive or active).
doOSEvent
doOSEvent handles operating system events. If the event is a suspend or resume event, a global
variable is then set to indicate whether the program has come to the foreground or has been
sent to the background. This global variable is used in doUpdate, and controls the colour in
which the text in the window header is drawn.
doInContent
doInContent further processes mouse-down events in the content region. Since the content
region of the window contains nothing but controls, this function is really just the main
switching point for the further handling of those controls.
The first line gets the handle to the "document" structure containing the references to the
various control objects. The call to SetPortWindowPort is a necessary precursor to the call to
GlobalToLocal, which converts the mouse coordinates in the event structure's where field from
global coordinates to the local coordinates required in the following call to FindControl.
(FindControl is used here rather than the newer function FindControlUnderMouse because there is
no requirement to get a reference to a control even if no part was hit and no requirement to
determine whether a mouse-down event has occurred in a deactivated control.)
If there is a control at the cursor location at which the mouse button is released, the control
reference returned by the FindControl call is first compared with the references to the pop-up
menu controls stored in the window's "document" structure. If a match is found, TrackControl
is called with (ControlActionUPP) -1 passed in the actionProc parameter so as to cause an
action function within the control's CDEF to be repeatedly invoked while the mouse button
remains down. When TrackControl returns, the control value is obtained by a call to
GetControlValue and a function is called to perform further handling
Note that TrackControl, rather than the newer function HandleControlClick, is used in this
program because none of the controls require modifier keys to be passed in. (Of course,
HandleControlClick would work just as well (with 0 passed in the inModifiers parameter).)
If the control reference returned by FindControl does not match the pop-up controls'
references, it is then tested against the references to the vertical and horizontal scroll bar
controls. If it matches the reference to the vertical scroll bar (which uses the
non-live-feedback CDEF variant), the function doVertScrollbar is called to perform further
handling. If it matches the reference to the horizontal scroll bar (which uses the
live-feedback CDEF variant), TrackControl is called (ControlActionUPP) Ð1 passed in the
actionProc parameter. This latter is because the UPP to the control action function has
already been set. (Recall that it was passed in a parameter of the CreateScrollBarControl call
which created the control.) The net effect of is that the application-defined action function
to which the UPP relates will be repeatedly called while the mouse button remains down.
If the reference returned by FindControl does not match the references to any of the pop-up
menu buttons or scroll bars, it must be a reference to one of the other controls. In this
case, TrackControl is called, with the procPtr field set to that required for push buttons,
radio buttons, and checkboxes (that is, NULL). If the cursor is still within the control when
the mouse button is released, the reference is compared to, in sequence, the references to the
radio buttons, the checkboxes, and the push buttons. If a match is found, the appropriate
function is called to perform further handling.
doPopupMenuChoice
doPopupMenuChoice further handles mouse-downs in the pop-up menu buttons. In this
demonstration, the further handling that would normally take place here is replaced by simply
drawing the menu item text in the window.
The call to GetControlData gets a reference to the control's menu, allowing GetMenuItemText to
get the item text into a global variable. This allows the text to be drawn in the window
header frame.
doVertScrollbar
doVertScrollbar is called from doInContent in the case of a mouse-down in the vertical scroll
bar (which uses the non-live-feedback variant of the CDEF).
The call to copyPString is incidental to the demonstration. It simply copies some appropriate
text to the global variable gCurrentString.
At the next line, the function switches on the control part code. If the control part code was
the scroll box/scroller (that is, the "indicator"), TrackControl is called with the procPtr
parameter set to that required for the scroll box of non-live-feedback scroll bars (that is,
NULL). If the user did not move the cursor outside the control before releasing it, the if
block executes, retrieving the new control value, converting it to a string, appending that
string to the string currently in gCurrentString, and drawing gCurrentString in the window
header. (In a real application, calculation of the distance and direction to scroll, and the
scrolling itself, would take place inside this if block.)
If the mouse down was in the gray area/track or one of the scroll arrows, TrackControl is
called with a Universal Procedure Pointer (UPP) passed in the actionProc parameter. The effect
of this is that the application-defined action function to which the UPP relates will be
repeatedly called while the mouse button remains down.
ACTION FUNCTIONS
An action function is an example of a "callback function" (sometimes called a "hook function").
A callback function is an application-defined function that is called by a Toolbox function
during the Toolbox function's execution, thus extending the features of the Toolbox function.
actionFunctionVert
actionFunctionVert is the action function called by TrackControl at the bottom of
doVertScrollbar. Because it is repeatedly called by TrackControl while the mouse button
remains down, the scrolling such a function would perform in a real application continues
repeatedly until the mouse button is released (provided the cursor remains within the scroll
arrow or gray area/track).
The call to copyPString is incidental to the demonstration. It simply copies some appropriate
text to the global variable gCurrentString.
The if(controlPartCode) line ensures that, if the cursor is not still inside the scroll arrow
or gray area/track, the action function exits and all scrolling ceases until the user brings
the cursor back within the scroll arrow or gray area/track, causing a non-zero control part
code to be again received. The following occurs only when the cursor is within the control.
The function switches on the control part code. If the mouse-down is in a scroll arrow, the
variable scrollDistance is set to 2. If it is in a gray area, scrollDistance is set to 55.
(In this simple demonstration, these are just arbitrary values. In a real application, you
would assign an appropriate value in the case of the scroll arrows, and assign a calculated
value (based primarily on current window height) in the case of the gray areas/tracks.)
The next block convert the value in scrollDistance to the required negative value if the user
is scrolling down rather than up.
The next block defeats any further scrolling action if, firstly, the down scroll arrow is being
used and the "document" is at the maximum scrolled position or, secondly, the up scroll arrow
is being used and the "document" is at the minimum scrolled position.
The distance to scroll having been set, the call to the function doMoveScrollBox moves the
scroll box/scroller the appropriate distance in the appropriate direction and updates the
control's value accordingly. This means, of course, that the scroll box/scroller is being
continually moved, and the control's value continually updated, while the mouse button remains
down.
In this demonstration, the remaining action is to retrieve the current value of the control,
convert it to a string, append it to the string currently in gCurrentString, and draw
gCurrentString in the window header frame. (In a real application, the actual scrolling of the
window's contents would be effected here.)
actionFunctionHoriz
actionFunctionHoriz is the action function passed in the actionProc parameter of the
TrackControl call in doInContent arising from a mouse-down in the horizontal scroll bar. This
action function differs from that for the vertical scroll bar because the horizontal scroll bar
uses the live-feedback variant of the CDEF.
The principal differences are that action functions for live-feedback scroll bars must
continually scroll the window's contents, not only while the mouse button remains down in the
scroll arrows and gray areas, but also while the scroll box/scroller is being dragged.
Accordingly, this function, unlike the action function for the vertical scroll bar, is also
called while the mouse button remains down in the scroll box/scroller.
The call to copyPString is incidental to the demonstration. It simply copies some appropriate
text to the global variable gCurrentString.
If the mouse-down occurred in the scroll box/scroller, the code which sets up the scroll
distance, adjusts the sign of the scroll distance according to whether the scroll is left or
right, prevents scrolling beyond the minimum and maximum scroll values, and calls
doMoveScrollBox to move the scroll box/scroller and update the control's value, is bypassed.
The call to doMoveScrollBox is bypassed because, in live-feedback, the CDEF moves the scroll
box/scroller and updates the control's value when the mouse-down is in the scroll box/scroller.
In this demonstration, the action taken after the main block of code has been bypassed
(mouse-down in the scroll box/scroller) or executed (mouse-down in the scroll arrows or gray
area/track) is to retrieve the current value of the control, convert it to a string, append it
to the string currently in gCurrentString, and draw gCurrentString in the window header frame.
(In a real application, the actual scrolling of the window's contents would be effected here.)
doMoveScrollBox
doMoveScrollBox is called from within the action functions to reset the control's current value
to reflect the scrolled distance, and to reposition the scroll box/scroller accordingly.
The first two lines retrieve the control's current value and maximum value. The next line
calculates the new control value by subtracting the distance to scroll received from the
calling action function from the current control value. The next four lines prevent the
control's value from being set lower or higher than the control's minimum and maximum values
respectively. The call to SetControlValue sets the new control value and repositions the
scroll box/scroller.
doRadioButtons
doRadioButtons is called when the mouse-down is within a radio button. The first three calls
to SetControlValue set all radio buttons to the off state. The final call sets the radio
button under the mouse to the on state.
doCheckboxes
doCheckboxes is called when the mouse-down is within a checkbox. The single line simply
toggles the current value of the control.
doPushButtons
doPushButtons is called when the mouse-down is within a push button. In this demonstration,
the only action taken is to draw the identity of the push button in the window header frame.
doAdjustScrollBars
doAdjustScrollBars is called if the user resizes or zooms the window.
At the first line, a handle to the window's "document" structure is retrieved from the window
object.
At the next line, the coordinates representing the window's current content region are assigned
to a Rect variable which will be used in calls to MoveControl and SizeControl.
Amongst other things, MoveControl and SizeControl both redraw the specified scroll bar. Since
SizeControl will be called immediately after MoveControl, this will causes a very slight
flickering of the scroll bars. To prevent this, the scroll bars will be hidden while these two
functions are executing.
The calls to HideControl hide the scroll bars. The calls to MoveControl erase the scroll bars,
offset the contrlRect fields of their control structures, and redraw the scroll bars within the
offset rectangle. SizeControl hides the scroll bars (in this program they are already hidden),
adjusts the contrlRect fields of their control structures, and redraws the scroll bars within
their new rectangles. The calls to ShowControl then show the scroll bars.
In this demonstration, the remaining lines set the new maximum values for the scroll bars
according to the new height and width of the window. No attempt is made to calculate the
required new control value to ensure that the (non-existent) document remains in the same
scrolled position after the zoom or resize. In a real application, this, plus the calculation
of the maximum value according to, for example, the line height of text content as well as the
new window height, are matters that would need to be attended to in this function.
Demonstration Program Controls2
// *******************************************************************************************
// Controls2.c CLASSIC EVENT MODEL
// *******************************************************************************************
//
// This program:
//
// o Opens a kWindowDocumentProc window with a two horizontal scroll bars, each of which
// relates to the picture displayed immediately above it.
//
// o Allows the user to horizontally scroll the pictures within the window using the scroll
// box/scroller, the scroll arrows and the gray area/track of each scroll bar.
//
// The top scroll bar uses the non-live-feedback variant of the scroll bar CDEF. The bottom
// scroll bar uses the live-feedback variant.
//
// With regard to the scroll bars, the principal differences between this program and
// Controls1 are that, in this program:
//
// o The scroll bar scroll boxes are made proportional.
//
// o The action functions are set using the function SetControlAction.
//
// o References to the scroll bar controls are not stored in, and retrieved from, a document
// structure associated with the window. Instead, each control is assigned a controlID
// using SetControlID, allowing the ID of the control to be retrieved using GetControlID
// and a reference to the control to be obtained using GetControlByID.
//
// The program utilises the following resources:
//
// o A 'plst' resource.
//
// o An 'MBAR' resource, and 'MENU' resources for Apple, File and Edit (preload, non-
// purgeable).
//
// o A 'WIND' resource (purgeable) (initially visible).
//
// o Two 'CNTL' resource for the horizontal scroll bars (purgeable).
//
// o 'PICT' resources containing the pictures to be scrolled (non-purgeable).
//
// o A 'SIZE' resource with the acceptSuspendResumeEvents, canBackground,
// doesActivateOnFGSwitch, and isHighLevelEventAware flags set.
//
// *******************************************************************************************
// .................................................................................. includes
#include <Carbon.h>
// ................................................................................... defines
#define rMenubar 128
#define rNewWindow 128
#define rPictureNonLive 128
#define rPictureLive 129
#define mAppleApplication 128
#define iAbout 1
#define mFile 129
#define iQuit 12
#define cScrollbarNonLive 128
#define cScrollbarLive 129
#define kScrollbarNonLiveID 1
#define kScrollbarLiveID 2
#define MAX_UINT32 0xFFFFFFFF
// .......................................................................... global variables
ControlActionUPP gActionFuncNonLiveUPP;
ControlActionUPP gActionFuncLiveUPP;
Boolean gDone;
Rect gPictRectNonLive, gPictRectLive;
PicHandle gPictHandleNonLive, gPictHandleLive ;
// ....................................................................... function prototypes
void main (void);
void doPreliminaries (void);
OSErr quitAppEventHandler (AppleEvent *,AppleEvent *,SInt32);
void doEvents (EventRecord *);
void doMouseDown (EventRecord *);
void doUpdate (EventRecord *);
void doActivate (EventRecord *);
void doActivateWindow (WindowRef,Boolean);
void doOSEvent (EventRecord *);
void doMenuChoice (SInt32);
void doInContent (EventRecord *,WindowRef);
void doNonLiveScrollBars (ControlPartCode,WindowRef,ControlRef,Point);
void actionFuncNonLive (ControlRef,ControlPartCode);
void actionFuncLive (ControlRef,ControlPartCode);
void doMoveScrollBox (ControlRef,SInt16);
// ************************************************************************************** main
void main(void)
{
MenuBarHandle menubarHdl;
SInt32 response;
MenuRef menuRef;
WindowRef windowRef;
ControlRef controlRefScrollbarNonLive, controlRefScrollbarLive;
ControlID controlID;
Rect portRect;
EventRecord eventStructure;
// ........................................................................ do preliminaries
doPreliminaries();
// ..................................................... create universal procedure pointers
gActionFuncNonLiveUPP = NewControlActionUPP((ControlActionProcPtr) actionFuncNonLive);
gActionFuncLiveUPP = NewControlActionUPP((ControlActionProcPtr) actionFuncLive);
// ............................................................... set up menu bar and menus
menubarHdl = GetNewMBar(rMenubar);
if(menubarHdl == NULL)
ExitToShell();
SetMenuBar(menubarHdl);
DrawMenuBar();
Gestalt(gestaltMenuMgrAttr,&response);
if(response & gestaltMenuMgrAquaLayoutMask)
{
menuRef = GetMenuRef(mFile);
if(menuRef != NULL)
{
DeleteMenuItem(menuRef,iQuit);
DeleteMenuItem(menuRef,iQuit - 1);
DisableMenuItem(menuRef,0);
}
}
// ........................................................................... open a window
if(!(windowRef = GetNewCWindow(rNewWindow,NULL,(WindowRef)-1)))
ExitToShell();
SetPortWindowPort(windowRef);
// .................................... get controls and set ID and control action functions
controlRefScrollbarNonLive = GetNewControl(cScrollbarNonLive,windowRef);
controlID.signature = 'kjB ';
controlID.id = kScrollbarNonLiveID;
SetControlID(controlRefScrollbarNonLive,&controlID);
SetControlAction(controlRefScrollbarNonLive,gActionFuncNonLiveUPP);
controlRefScrollbarLive = GetNewControl(cScrollbarLive,windowRef);
controlID.id = kScrollbarLiveID;
SetControlID(controlRefScrollbarLive,&controlID);
SetControlAction(controlRefScrollbarLive,gActionFuncLiveUPP);
// ............................................................................. get picture
if(!(gPictHandleNonLive = GetPicture(rPictureNonLive)))
ExitToShell();
gPictRectNonLive = (*gPictHandleNonLive)->picFrame;
if(!(gPictHandleLive = GetPicture(rPictureLive)))
ExitToShell();
gPictRectLive = (*gPictHandleLive)->picFrame;
OffsetRect(&gPictRectLive,0,191);
// .................................................... set up for proportional scroll boxes
GetWindowPortBounds(windowRef,&portRect);
SetControlViewSize(controlRefScrollbarNonLive,portRect.right);
SetControlViewSize(controlRefScrollbarLive,portRect.right);
// ......................................................................... enter eventLoop
gDone = false;
while(!gDone)
{
if(WaitNextEvent(everyEvent,&eventStructure,MAX_UINT32,NULL))
doEvents(&eventStructure);
}
}
// *************************************************************************** doPreliminaries
void doPreliminaries(void)
{
OSErr osError;
MoreMasterPointers(32);
InitCursor();
FlushEvents(everyEvent,0);
osError = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
NewAEEventHandlerUPP((AEEventHandlerProcPtr) quitAppEventHandler),
0L,false);
if(osError != noErr)
ExitToShell();
}
// **************************************************************************** doQuitAppEvent
OSErr quitAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
OSErr osError;
DescType returnedType;
Size actualSize;
osError = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,NULL,0,
&actualSize);
if(osError == errAEDescNotFound)
{
gDone = true;
osError = noErr;
}
else if(osError == noErr)
osError = errAEParamMissed;
return osError;
}
// ********************************************************************************** doEvents
void doEvents(EventRecord *eventStrucPtr)
{
switch(eventStrucPtr->what)
{
case kHighLevelEvent:
AEProcessAppleEvent(eventStrucPtr);
break;
case mouseDown:
doMouseDown(eventStrucPtr);
break;
case updateEvt:
doUpdate(eventStrucPtr);
break;
case activateEvt:
doActivate(eventStrucPtr);
break;
case osEvt:
doOSEvent(eventStrucPtr);
break;
}
}
// ******************************************************************************* doMouseDown
void doMouseDown(EventRecord *eventStrucPtr)
{
WindowRef windowRef;
WindowPartCode partCode;
partCode = FindWindow(eventStrucPtr->where,&windowRef);
switch(partCode)
{
case inMenuBar:
doMenuChoice(MenuSelect(eventStrucPtr->where));
break;
case inContent:
if(windowRef != FrontWindow())
SelectWindow(windowRef);
else
doInContent(eventStrucPtr,windowRef);
break;
case inDrag:
DragWindow(windowRef,eventStrucPtr->where,NULL);
break;
}
}
// ********************************************************************************** doUpdate
void doUpdate(EventRecord *eventStrucPtr)
{
WindowRef windowRef;
RgnHandle regionHdl;
ControlID controlID;
ControlRef controlRef;
windowRef = (WindowRef) eventStrucPtr->message;
BeginUpdate(windowRef);
SetPortWindowPort(windowRef);
regionHdl = NewRgn();
if(regionHdl)
{
GetPortVisibleRegion(GetWindowPort(windowRef),regionHdl);
UpdateControls(windowRef,regionHdl);
DisposeRgn(regionHdl);
}
controlID.signature = 'kjB ';
controlID.id = kScrollbarNonLiveID;
GetControlByID(windowRef,&controlID,&controlRef);
SetOrigin(GetControlValue(controlRef),0);
DrawPicture(gPictHandleNonLive,&gPictRectNonLive);
SetOrigin(0,0);
controlID.id = kScrollbarLiveID;
GetControlByID(windowRef,&controlID,&controlRef);
SetOrigin(GetControlValue(controlRef),0);
DrawPicture(gPictHandleLive,&gPictRectLive);
SetOrigin(0,0);
EndUpdate(windowRef);
}
// ******************************************************************************** doActivate
void doActivate(EventRecord *eventStrucPtr)
{
WindowRef windowRef;
Boolean becomingActive;
windowRef = (WindowRef) eventStrucPtr->message;
becomingActive = ((eventStrucPtr->modifiers & activeFlag) == activeFlag);
doActivateWindow(windowRef,becomingActive);
}
// ************************************************************************** doActivateWindow
void doActivateWindow(WindowRef windowRef,Boolean becomingActive)
{
ControlID controlID;
ControlRef controlRefScrollbarNonLive, controlRefScrollbarLive;
controlID.signature = 'kjB ';
controlID.id = kScrollbarNonLiveID;
GetControlByID(windowRef,&controlID,&controlRefScrollbarNonLive);
controlID.id = kScrollbarLiveID;
GetControlByID(windowRef,&controlID,&controlRefScrollbarLive);
if(becomingActive)
{
ActivateControl(controlRefScrollbarNonLive);
ActivateControl(controlRefScrollbarLive);
}
else
{
DeactivateControl(controlRefScrollbarNonLive);
DeactivateControl(controlRefScrollbarLive);
}
}
// ********************************************************************************* doOSEvent
void doOSEvent(EventRecord *eventStrucPtr)
{
switch((eventStrucPtr->message >> 24) & 0x000000FF)
{
case suspendResumeMessage:
if((eventStrucPtr->message & resumeFlag) == 1)
SetThemeCursor(kThemeArrowCursor);
break;
}
}
// ****************************************************************************** doMenuChoice
void doMenuChoice(SInt32 menuChoice)
{
MenuID menuID;
MenuItemIndex menuItem;
menuID = HiWord(menuChoice);
menuItem = LoWord(menuChoice);
if(menuID == 0)
return;
switch(menuID)
{
case mAppleApplication:
if(menuItem == iAbout)
SysBeep(10);
break;
case mFile:
if(menuItem == iQuit)
gDone = true;
break;
}
HiliteMenu(0);
}
// ******************************************************************************* doIncontent
void doInContent(EventRecord *eventStrucPtr,WindowRef windowRef)
{
ControlPartCode controlPartCode;
ControlRef controlRef;
ControlID controlID;
SetPortWindowPort(windowRef);
GlobalToLocal(&eventStrucPtr->where);
if(controlPartCode = FindControl(eventStrucPtr->where,windowRef,&controlRef))
{
GetControlID(controlRef,&controlID);
if(controlID.id == kScrollbarNonLiveID)
doNonLiveScrollBars(controlPartCode,windowRef,controlRef,eventStrucPtr->where);
else if(controlID.id == kScrollbarLiveID)
TrackControl(controlRef,eventStrucPtr->where,(ControlActionUPP) -1);
}
}
// *********************************************************************** doNonLiveScrollBars
void doNonLiveScrollBars(ControlPartCode controlPartCode,WindowRef windowRef,
ControlRef controlRef,Point mouseXY)
{
SInt16 oldControlValue;
SInt16 scrollDistance;
RgnHandle updateRgnHdl;
switch(controlPartCode)
{
case kControlIndicatorPart:
oldControlValue = GetControlValue(controlRef);
if(TrackControl(controlRef,mouseXY,NULL))
{
scrollDistance = oldControlValue - GetControlValue(controlRef);
if(scrollDistance != 0)
{
updateRgnHdl = NewRgn();
ScrollRect(&gPictRectNonLive,scrollDistance,0,updateRgnHdl);
InvalWindowRgn(windowRef,updateRgnHdl);
DisposeRgn(updateRgnHdl);
}
}
break;
case kControlUpButtonPart:
case kControlDownButtonPart:
case kControlPageUpPart:
case kControlPageDownPart:
TrackControl(controlRef,mouseXY,(ControlActionUPP) -1);
break;
}
}
// ************************************************************************* actionFuncNonLive
void actionFuncNonLive(ControlRef controlRef,ControlPartCode controlPartCode)
{
WindowRef windowRef;
SInt16 scrollDistance, controlValue;
Rect portRect;
RgnHandle updateRgnHdl;
if(controlPartCode)
{
windowRef = GetControlOwner(controlRef);
switch(controlPartCode)
{
case kControlUpButtonPart:
case kControlDownButtonPart:
scrollDistance = 2;
break;
case kControlPageUpPart:
case kControlPageDownPart:
GetWindowPortBounds(windowRef,&portRect);
scrollDistance = (portRect.right - portRect.left - 10);
break;
}
if((controlPartCode == kControlDownButtonPart) ||
(controlPartCode == kControlPageDownPart))
scrollDistance = -scrollDistance;
controlValue = GetControlValue(controlRef);
if(((controlValue == GetControlMaximum(controlRef)) && scrollDistance < 0) ||
((controlValue == GetControlMinimum(controlRef)) && scrollDistance > 0))
return;
doMoveScrollBox(controlRef,scrollDistance);
if(controlPartCode == kControlUpButtonPart ||
controlPartCode == kControlDownButtonPart)
{
updateRgnHdl = NewRgn();
ScrollRect(&gPictRectNonLive,scrollDistance,0,updateRgnHdl);
InvalWindowRgn(windowRef,updateRgnHdl);
DisposeRgn(updateRgnHdl);
BeginUpdate(windowRef);
}
SetOrigin(GetControlValue(controlRef),0);
DrawPicture(gPictHandleNonLive,&gPictRectNonLive);
SetOrigin(0,0);
if(controlPartCode == kControlUpButtonPart || controlPartCode == kControlDownButtonPart)
EndUpdate(windowRef);
}
}
// *************************************************************************** actionFuncLive
void actionFuncLive(ControlRef controlRef,ControlPartCode partCode)
{
WindowRef windowRef;
SInt16 scrollDistance, controlValue;
Rect portRect;
windowRef = GetControlOwner(controlRef);
if(partCode != 0)
{
if(partCode != kControlIndicatorPart)
{
switch(partCode)
{
case kControlUpButtonPart:
case kControlDownButtonPart:
scrollDistance = 2;
break;
case kControlPageUpPart:
case kControlPageDownPart:
GetWindowPortBounds(windowRef,&portRect);
scrollDistance = (portRect.right - portRect.left) - 10;
break;
}
if((partCode == kControlDownButtonPart) || (partCode == kControlPageDownPart))
scrollDistance = -scrollDistance;
controlValue = GetControlValue(controlRef);
if(((controlValue == GetControlMaximum(controlRef)) && scrollDistance < 0) ||
((controlValue == GetControlMinimum(controlRef)) && scrollDistance > 0))
return;
doMoveScrollBox(controlRef,scrollDistance);
}
SetOrigin(GetControlValue(controlRef),0);
DrawPicture(gPictHandleLive,&gPictRectLive);
SetOrigin(0,0);
}
}
// *************************************************************************** doMoveScrollBox
void doMoveScrollBox(ControlRef controlRef,SInt16 scrollDistance)
{
SInt16 oldControlValue, controlValue, controlMax;
oldControlValue = GetControlValue(controlRef);
controlMax = GetControlMaximum(controlRef);
controlValue = oldControlValue - scrollDistance;
if(controlValue < 0)
controlValue = 0;
else if(controlValue > controlMax)
controlValue = controlMax;
SetControlValue(controlRef,controlValue);
}
// *******************************************************************************************
Demonstration Program Controls2 Comments
This program is basically an extension of the scroll bars aspects of the demonstration program
Controls1 in that, unlike the scroll bars in Controls1, the scroll bars in this program
actually scroll the contents of the window. Also, this program supports proportional scroll
boxes/scrollers.
When the program is run, the user should scroll the pictures by dragging the scroll
boxes/scrollers, clicking in the scroll bar gray areas/tracks, clicking in the scroll arrows
and holding the mouse button down while the cursor is in the gray areas/travks and scroll
arrows. The user should note, when scrolling with the scroll boxes/scrollers, that the top
scroll bar uses the non-live-feedback variant of the scroll bar CDEF and the bottom scroll bar
uses the live-feedback variant, this latter to facilitate the program's live-scrolling of the
bottom picture.
The pictures scrolled in this demonstration are, respectively 1220 by 175 pixels and 915 by 175
pixels. "pane" for each picture and scroll bar is 400 pixels wide by 175 pixels high, the
'CNTL' resources set the control maximum values to 820 and 515 respectively, and the control
rectangles specified in the 'CNTL' resource locate the scroll bars in the correct position in
the non-resizable, non-zoomable window.
As an incidental aspect of the demonstration, two different methods are used to scroll the
pictures when the scroll arrows are being used. In the top picture, at each pass through the
action function, the pixels are scrolled using ScrollRect, the "vacated" area is invalidated,
and only this vacated area is redrawn. In the bottom picture, at each pass through the action
function, the whole visible part of the picture is redrawn. The user should note that the
first method results in some flickering in the "vacated" area when the picture is scrolled, and
that the second method eliminates this flickering at the cost of some horizontal "tearing" of
the picture caused by the way in which the image is drawn by the monitor on its screen.
The following comments are limited to those areas which are significantly different from the
same areas in the demonstration program Controls1.
defines
kScrollbarNonLiveID and kScrollbarLiveID will be assigned as the IDs of, respectively, the top
(non-live) scroll bar and the bottom (live) scroll bar.
main
Two calls to GetNewControl allocate memory for the control objects, insert the control objects
into the window's control list and draw the controls.
Following each call to GetNewControl:
o The id field of a variable of type ControlID is assigned the appropriate ID value for the
specific control and the ID is then assigned to the control by a call to SetControlID.
o SetControlAction is called with a UPP passed in the actionProc parameter. The effect of this
is that the application-defined action function to which the UPP relates will be repeatedly
called while the mouse button remains down. As a consequence of using SetControlAction,
(ControlActionUPP) -1 will be passed in TrackControl's actionProc parameter.
Note that no root control is created in this program; accordingly, the two controls will be
activated and deactivated individually.
In the next block, two 'PICT' resources are loaded, the associated handles being assigned to
two global variables. In each case, the picture structure's picFrame field (a Rect) is copied
to a global variable. In the case of the second picture, this rectangle is then offset
downwards by 191 pixels. (Note that the two 'PICT' resources were created so that the top and
left fields of the picFrame Rect are both zero.)
In the next block, the width of the port rectangle is passed in the newViewSize parameter of
calls to SetControlViewSize. (In this case, the view width is the same as the width of the
port rectangle. This value is in the same units of measurement as are used for the scroll bar
minimum, maximum, and current values.) This makes the scroll boxes proportional (on Mac OS
8/9, provided that the user has selected Smart Scrolling on in the Options tab of the
Appearance control panel).
doUpdate
In the two blocks which draw the pictures, the first call to SetOrigin sets the window origin
to the current scroll position, that is, to the position represented by the control's current
value, thus ensuring that the correct part of the picture will be drawn by the call to
DrawPicture. The second call to SetOrigin resets the window's origin to (0,0).
Note that GetControlByID is used to retrieve references to the two controls.
doActivateWindow
Note that GetControlByID is used to retrieve references to the two controls.
doInContent
doInContent establishes whether a mouse-down event was in one of the scroll bars and, if so,
branches accordingly.
The call to GlobalToLocal converts the global coordinates of the mouse-down, stored in the
where field of the event structure, to the local coordinates required by FindControl. If the
call to FindControl returns a non-zero result, the mouse-down was in a scroll bar.
If the mouse-down was in a scroll bar, GetControlID is called to get the ID of the control.
Then, as in the demonstration program Controls1:
o If the mouse-down was in the non-live-feedback scroll bar, one of the applicationÕs functions
is called to further handle the mouse-down event.
o If the mouse-down was in the live-feedback scroll bar, TrackControl is called with
(ControlActionUPP) - 1 passed in the actionProc parameter. This means that the
application-defined (callback) function associated with the UPP previously set by
SetControlAction will be continually called while the mouse button remains down.
doNonLiveScrollBars
doNonLiveScrollBars is similar to its sister function in Controls1 except that it actually
scrolls the window's contents.
At the first line, the function switches on the control part code:
o If the mouse-down was in the scroll box/scroller (that is, the "indicator"), the control's
value at the time of the mouse-down is retrieved. Control is then handed over to
TrackControl, which tracks user actions while the mouse button remains down. If the user
releases the mouse button with the cursor inside the scroll box/scroller, the scroll distance
(in pixels) is calculated by subtracting the control's value prior to the scroll from its
current value. If the user moved the scroll box/scroller, the picture's pixels are scrolled
by the specified scroll distance in the appropriate direction, and the "vacated" area of the
window following the scroll is added to the (currently empty) window update region (Mac OS
8/9). This means that an update event will be generated for the window and that the re-draw
of the picture will be attended to in the doUpdate function.
o If the mouse-down was in a scroll arrow or gray area/track, more specifically in one of the
non-live-feedback's scroll bar's scroll arrows or gray areas/tracks, TrackControl takes
control until the user releases the mouse button. The third parameter in the TrackControl
call means that the application-defined (callback) function associated with the UPP set by
SetControlAction will be continually called while the mouse button remains down.
actionFunctionNonLive
actionFunctionNonLive is the action function for the non-live-feedback scroll bar. Because it
is repeatedly called by TrackControl while the mouse button remains down, the scrolling it
performs continues repeatedly until the mouse button is released.
Firstly, if the cursor is not still inside the scroll arrow or gray area/track, the action
function exits. The following occurs only when the cursor is within the control.
A reference to the window which "owns" this control is retrieved from the control object.
If the control part being used by the user to perform the scrolling is one of the scroll
arrows, the distance to scroll (in pixels) is set to 2. If the control part being used is one
of the gray areas/track, the distance to scroll is set to the width of the window's content
region minus 10 pixels. (Subtracting 10 pixels ensures that a small part of the pre-scroll
display will appear at right or left (depending on the direction of scroll) of the post-scroll
display.)
The first block following the switch converts the distance to scroll to the required negative
value if the user is scrolling towards the right. The second block defeats any further
scrolling action if, firstly, the left scroll arrow is being used, the mouse button is still
down and the document is at the minimum (left) scrolled position or, secondly, the right scroll
arrow is being used, the mouse button is still down and the document is at the maximum (right)
scrolled position.
With the scroll distance determined, the call to the function doMoveScrollBox adds/subtracts
the distance to scroll to/from the control's current value and repositions the scroll
box/scroller accordingly.
At this stage, the picture scrolling takes place. If scrolling is being effected using the
scroll arrows, ScrollRect scrolls the picture's pixels by the specified amount, and in the
specified direction, as represented by the distance-to-scroll value. The "vacated" area is
then added to the window's update region (previously empty) by the call to InvalWindowRgn, and
BeginUpdate is called to ensure that, on Mac OS 8/9, (1) only the "vacated" area will be
redrawn and (2) the update region is cleared.
Regardless of whether the picture is being scrolled using the scroll arrows or the gray areas,
SetOrigin is then called to reset the window origin so that that part of the picture
represented by the current scroll position is drawn. After the correct part of the picture is
drawn, the window origin is reset to (0,0).
Finally, if BeginUpdate was called prior to the draw (that is, scrolling is being effected
using the scroll arrows), EndUpdate is called.
actionFunctionLive
actionFunctionLive is the action function for the live-feedback scroll bar.
The principal differences between this action function and the previous one are that action
functions for live-feedback scroll bars must continually scroll the window's contents, not only
while the mouse button remains down in the scroll arrows and gray areas/track, but also while
the scroll box/scroller is being dragged. Accordingly, this action function, unlike the action
function for the non-live-feedback scroll bar, is also called while the mouse button remains
down in the scroll box/scroller.
If the mouse-down occurred in the scroll box/scroller, the code which sets up the scroll
distance, adjusts the sign of the scroll distance according to whether the scroll is left or
right, prevents scrolling beyond the minimum and maximum scroll values, and calls
doMoveScrollBox to move the scroll box/scroller and update the control's value, is bypassed.
The call to doMoveScrollBox is bypassed because, the live-feedback variant of the CDEF moves
the scroll box/scroller and updates the control's value when the mouse-down is in the scroll
box/scroller.
After the if block has been bypassed (mouse-down in the scroll box/scroller) or executed
(mouse-down in the scroll arrows or gray area/track), the window contents are scrolled.
Regardless of whether the picture is being scrolled using the scroll box/scroller, the scroll
arrows, or the gray areas/track, SetOrigin is called to reset the window origin so that that
part of the picture represented by the current scroll position is drawn by the call to
DrawPicture. After the correct part of the picture is drawn, the window origin is reset to
(0,0).
Note that this alternative approach to re-drawing the picture when scrolling is being effected
using the scroll arrows has not been dictated by the fact that this is a live-feedback action
function. Either of these two approaches will work in both live-feedback and non-live-feedback
action functions.
doMoveScrollBox
doMoveScrollBox is called from within the action function to reset the control's current value
to reflect the scrolled distance, and to reposition the scroll box accordingly.
|