Demonstration Program 1
//
// Text1.c
//
//
// This program demonstrates:
//
// A "bare-bones" monostyled text editor.
//
// A Help dialog which features the integrated scrolling of multistyled text and
// pictures.
//
// In the monostyled text editor demonstration, a panel is displayed at the bottom of all
// opened windows. This panel displays the edit record length, number of lines, line
// height, destination rectangle (top), scroll bar value, and scroll bar maximum value.
//
// The bulk of the source code for the Help dialog is contained in the file HelpDialog.c.
// The dialog itself displays information intended to assist the user in adapting the
// Help dialog source code and resources to the requirements of his/her own application.
//
// The program utilises the following resources:
//
// An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit, and Help dialog
// pop-up menus (preload, non-purgeable).
//
// A 'WIND' resource (purgeable) (initially visible).
//
// 'CNTL' resources (purgeable) for the vertical scroll bars in the text editor window
// and Help dialog, and for the pop-up menu in the Help Dialog.
//
// A 'DLOG' resource (purgeable, initially invisible) and associated 'dctb' resource
// (purgeable) for the Help dialog.
//
// 'DITL' resources (purgeable) for the 'ALRT' and 'DLOG' resources.
//
// 'TEXT' and associated 'styl' resources (all purgeable) for the Help dialog.
//
// 'PICT' resources (purgeable) for the Help dialog.
//
// A 'STR ' resource (purgeable) containing error text strings.
//
// A 'SIZE' resource with the acceptSuspendResumeEvents, doesActivateOnFGSwitch, and
// is32BitCompatible flags set.
//
//
// ............................................................................. includes
#include <Appearance.h>
#include <Balloons.h>
#include <ControlDefinitions.h>
#include <Devices.h>
#include <Gestalt.h>
#include <LowMem.h>
#include <Scrap.h>
#include <Sound.h>
#include <StandardFile.h>
#include <ToolUtils.h>
// .............................................................................. defines
#define rMenubar 128
#define mApple 128
#define iAbout 1
#define mFile 129
#define iNew 1
#define iOpen 2
#define iClose 4
#define iSaveAs 6
#define iQuit 12
#define mEdit 130
#define iUndo 1
#define iCut 3
#define iCopy 4
#define iPaste 5
#define iClear 6
#define iSelectAll 7
#define rWindow 128
#define rVScrollbar 128
#define rErrorStrings 128
#define eMenuBar 1
#define eMenu 2
#define eWindow 3
#define eDocStructure 4
#define eEditRecord 5
#define eExceedChara 6
#define eNoSpaceCut 7
#define eNoSpacePaste 8
#define kMaxTELength 32767
#define kTab 0x09
#define kDel 0x7F
#define kReturn 0x0D
#define topLeft(r) (((Point *) &(r))[0])
#define botRight(r) (((Point *) &(r))[1])
// ............................................................................. typedefs
typedef struct
{
TEHandle editRecHdl;
ControlHandle vScrollbarHdl;
} docStructure, **docStructureHandle;
// ..................................................................... global variables
ControlActionUPP scrollActionFunctionUPP;
TEClickLoopUPP customClickLoopUPP;
Boolean gMacOS85Present = false;
Boolean gDone;
Boolean gInBackground;
RgnHandle gCursorRegion;
SInt16 gNumberOfWindows = 0;
SInt16 gOldControlValue;
// .................................................................. function prototypes
void main (void);
void doInitManagers (void);
void eventLoop (void);
void doIdle (void);
void doEvents (EventRecord *);
void doKeyEvent (SInt8);
pascal void scrollActionFunction (ControlHandle,SInt16);
void doInContent (EventRecord *);
void doUpdate (EventRecord *);
void doActivate (EventRecord *);
void doActivateDocWindow (WindowPtr,Boolean);
void doOSEvent (EventRecord *);
WindowPtr doNewDocWindow (void);
pascal Boolean customClickLoop (void);
void doSetScrollBarValue (ControlHandle,SInt16 *);
void doAdjustMenus (void);
void doMenuChoice (SInt32);
void doFileMenu (SInt16);
void doEditMenu (SInt16);
SInt16 doGetSelectLength (TEHandle);
void doAdjustScrollbar (WindowPtr);
void doAdjustCursor (WindowPtr);
void doCloseWindow (WindowPtr);
void doSaveAsFile (TEHandle);
void doOpenCommand (void);
void doOpenFile (FSSpec);
void doDrawDataPanel (WindowPtr);
void doErrorAlert (SInt16);
void doHelpMenu (SInt16);
extern void doHelp (void);
// main
void main(void)
{
OSErr osError;
SInt32 response;
Handle menubarHdl;
MenuHandle menuHdl;
OSErr osErr;
// .................................................................initialise managers
doInitManagers();
// ....................................... check whether Mac OS 8.5 or later is present
osError = Gestalt(gestaltSystemVersion,&response);
if(osError == noErr && response >= 0x00000850)
gMacOS85Present = true;
// ......................................................... create routine descriptors
scrollActionFunctionUPP = NewControlActionProc((ProcPtr) scrollActionFunction);
customClickLoopUPP = NewTEClickLoopProc((ProcPtr) customClickLoop);
// .......................................................... set up menu bar and menus
menubarHdl = GetNewMBar(rMenubar);
if(menubarHdl == NULL)
doErrorAlert(eMenuBar);
SetMenuBar(menubarHdl);
DrawMenuBar();
menuHdl = GetMenuHandle(mApple);
if(menuHdl == NULL)
doErrorAlert(eMenu);
else
AppendResMenu(menuHdl,'DRVR');
osErr = HMGetHelpMenuHandle(&menuHdl);
if(osErr == noErr)
AppendMenu(menuHdl,"\pText1 Help");
else
doErrorAlert(eMenu);
// ............................................................ open an untitled window
doNewDocWindow();
// .................................................................... enter eventLoop
eventLoop();
}
// doInitManagers
void doInitManagers(void)
{
MaxApplZone();
MoreMasters();
InitGraf(&qd.thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(NULL);
InitCursor();
FlushEvents(everyEvent,0);
RegisterAppearanceClient();
}
// eventLoop
void eventLoop(void)
{
EventRecord eventStructure;
Boolean gotEvent;
SInt32 sleepTime;
gDone = false;
gCursorRegion = NewRgn();
sleepTime = LMGetCaretTime();
while(!gDone)
{
gotEvent = WaitNextEvent(everyEvent,&eventStructure,sleepTime,gCursorRegion);
if(gotEvent)
doEvents(&eventStructure);
else
{
if(gNumberOfWindows > 0)
doIdle();
}
}
}
// doIdle
void doIdle(void)
{
docStructureHandle docStrucHdl;
WindowPtr windowPtr;
windowPtr = FrontWindow();
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
if(docStrucHdl != NULL)
TEIdle((*docStrucHdl)->editRecHdl);
}
// doEvents
void doEvents(EventRecord *eventStrucPtr)
{
WindowPtr windowPtr;
SInt16 partCode;
SInt8 charCode;
switch(eventStrucPtr->what)
{
case mouseDown:
partCode = FindWindow(eventStrucPtr->where,&windowPtr);
switch(partCode)
{
case inMenuBar:
doAdjustMenus();
doMenuChoice(MenuSelect(eventStrucPtr->where));
break;
case inContent:
if(windowPtr != FrontWindow())
SelectWindow(windowPtr);
else
doInContent(eventStrucPtr);
break;
case inDrag:
DragWindow(windowPtr,eventStrucPtr->where,&qd.screenBits.bounds);
doAdjustCursor(windowPtr);
break;
case inGoAway:
if(TrackGoAway(windowPtr,eventStrucPtr->where))
doCloseWindow(FrontWindow());
break;
}
break;
case keyDown:
case autoKey:
charCode = eventStrucPtr->message & charCodeMask;
if((eventStrucPtr->modifiers & cmdKey) != 0)
{
doAdjustMenus();
doMenuChoice(MenuEvent(eventStrucPtr));
}
else
doKeyEvent(charCode);
break;
case updateEvt:
doUpdate(eventStrucPtr);
break;
case activateEvt:
doActivate(eventStrucPtr);
break;
case osEvt:
doOSEvent(eventStrucPtr);
break;
}
}
// doKeyEvent
void doKeyEvent(SInt8 charCode)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
SInt16 selectionLength;
windowPtr = FrontWindow();
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
editRecHdl = (*docStrucHdl)->editRecHdl;
if(charCode == kTab)
{
// Do tab key handling here if required.
}
else if(charCode == kDel)
{
selectionLength = doGetSelectLength(editRecHdl);
if(selectionLength == 0)
(*editRecHdl)->selEnd += 1;
TEDelete(editRecHdl);
doAdjustScrollbar(windowPtr);
}
else
{
selectionLength = doGetSelectLength(editRecHdl);
if(((*editRecHdl)->teLength - selectionLength + 1) < kMaxTELength)
{
TEKey(charCode,editRecHdl);
doAdjustScrollbar(windowPtr);
}
else
doErrorAlert(eExceedChara);
}
doDrawDataPanel(windowPtr);
}
// scrollActionFunction
pascal void scrollActionFunction(ControlHandle controlHdl,SInt16 partCode)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
SInt16 linesToScroll;
SInt16 controlValue, controlMax;
windowPtr = (*controlHdl)->contrlOwner;
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
editRecHdl = (*docStrucHdl)->editRecHdl;
controlValue = GetControlValue(controlHdl);
controlMax = GetControlMaximum(controlHdl);
if(partCode)
{
if(partCode != kControlIndicatorPart)
{
switch(partCode)
{
case kControlUpButtonPart:
case kControlDownButtonPart:
linesToScroll = 1;
break;
case kControlPageUpPart:
case kControlPageDownPart:
linesToScroll = (((*editRecHdl)->viewRect.bottom - (*editRecHdl)->viewRect.top) /
(*editRecHdl)->lineHeight) - 1;
break;
}
if((partCode == kControlDownButtonPart) || (partCode == kControlPageDownPart))
linesToScroll = -linesToScroll;
linesToScroll = controlValue - linesToScroll;
if(linesToScroll < 0)
linesToScroll = 0;
else if(linesToScroll > controlMax)
linesToScroll = controlMax;
SetControlValue(controlHdl,linesToScroll);
linesToScroll = controlValue - linesToScroll;
}
else
{
linesToScroll = gOldControlValue - controlValue;
gOldControlValue = controlValue;
}
if(linesToScroll != 0)
{
TEScroll(0,linesToScroll * (*editRecHdl)->lineHeight,editRecHdl);
doDrawDataPanel(windowPtr);
}
}
}
// doInContent
void doInContent(EventRecord *eventStrucPtr)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
Point mouseXY;
ControlHandle controlHdl;
SInt16 partCode;
Boolean shiftKeyPosition = false;
windowPtr = FrontWindow();
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
editRecHdl = (*docStrucHdl)->editRecHdl;
mouseXY = eventStrucPtr->where;
SetPort(windowPtr);
GlobalToLocal(&mouseXY);
if((partCode = FindControl(mouseXY,windowPtr,&controlHdl)) != 0)
{
gOldControlValue = GetControlValue(controlHdl);
TrackControl(controlHdl,mouseXY,scrollActionFunctionUPP);
}
else if(PtInRect(mouseXY,&(*editRecHdl)->viewRect))
{
if((eventStrucPtr->modifiers & shiftKey) != 0)
shiftKeyPosition = true;
TEClick(mouseXY,shiftKeyPosition,editRecHdl);
}
}
// doUpdate
void doUpdate(EventRecord *eventStrucPtr)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
GrafPtr oldPort;
windowPtr = (WindowPtr) eventStrucPtr->message;
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
editRecHdl = (*docStrucHdl)->editRecHdl;
GetPort(&oldPort);
SetPort(windowPtr);
BeginUpdate((WindowPtr)eventStrucPtr->message);
TEUpdate(&windowPtr->portRect,editRecHdl);
UpdateControls(windowPtr,windowPtr->visRgn);
doDrawDataPanel(windowPtr);
EndUpdate((WindowPtr)eventStrucPtr->message);
SetPort(oldPort);
}
// doActivate
void doActivate(EventRecord *eventStrucPtr)
{
WindowPtr windowPtr;
Boolean becomingActive;
windowPtr = (WindowPtr) eventStrucPtr->message;
becomingActive = ((eventStrucPtr->modifiers & activeFlag) == activeFlag);
doActivateDocWindow(windowPtr,becomingActive);
}
// doActivateDocWindow
void doActivateDocWindow(WindowPtr windowPtr,Boolean becomingActive)
{
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
editRecHdl = (*docStrucHdl)->editRecHdl;
if(becomingActive)
{
SetPort(windowPtr);
(*editRecHdl)->viewRect.bottom = ((((*editRecHdl)->viewRect.bottom -
(*editRecHdl)->viewRect.top) /
(*editRecHdl)->lineHeight) *
(*editRecHdl)->lineHeight) +
(*editRecHdl)->viewRect.top;
(*editRecHdl)->destRect.bottom = (*editRecHdl)->viewRect.bottom;
TEActivate(editRecHdl);
ActivateControl((*docStrucHdl)->vScrollbarHdl);
doAdjustScrollbar(windowPtr);
}
else
{
TEDeactivate(editRecHdl);
DeactivateControl((*docStrucHdl)->vScrollbarHdl);
}
}
// doOSEvent
void doOSEvent(EventRecord *eventStrucPtr)
{
switch((eventStrucPtr->message >> 24) & 0x000000FF)
{
case suspendResumeMessage:
gInBackground = (eventStrucPtr->message & resumeFlag) == 0;
if(!gInBackground)
SetCursor(&qd.arrow);
if(gNumberOfWindows > 0)
doActivateDocWindow(FrontWindow(),!gInBackground);
HiliteMenu(0);
break;
case mouseMovedMessage:
doAdjustCursor(FrontWindow());
break;
}
}
// doNewDocWindow
WindowPtr doNewDocWindow(void)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
Rect destAndViewRect;
if(!(windowPtr = GetNewCWindow(rWindow,NULL,(WindowPtr) -1)))
{
doErrorAlert(eWindow);
return(NULL);
}
SetPort(windowPtr);
TextSize(10);
if(!(docStrucHdl = (docStructureHandle) NewHandle(sizeof(docStructure))))
{
doErrorAlert(eDocStructure);
return(NULL);
}
SetWRefCon(windowPtr,(SInt32) docStrucHdl);
gNumberOfWindows ++;
(*docStrucHdl)->vScrollbarHdl = GetNewControl(rVScrollbar,windowPtr);
destAndViewRect = windowPtr->portRect;
destAndViewRect.right -= 15;
destAndViewRect.bottom -= 15;
InsetRect(&destAndViewRect,2,2);
MoveHHi((Handle) docStrucHdl);
HLock((Handle) docStrucHdl);
if(!((*docStrucHdl)->editRecHdl = TENew(&destAndViewRect,&destAndViewRect)))
{
DisposeWindow(windowPtr);
gNumberOfWindows --;
DisposeHandle((Handle) docStrucHdl);
doErrorAlert(eEditRecord);
return(NULL);
}
HUnlock((Handle) docStrucHdl);
TESetClickLoop(customClickLoopUPP,(*docStrucHdl)->editRecHdl);
TEAutoView(true,(*docStrucHdl)->editRecHdl);
TEFeatureFlag(teFOutlineHilite,1,(*docStrucHdl)->editRecHdl);
return(windowPtr);
}
// customClickLoop
pascal Boolean customClickLoop(void)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
GrafPtr oldPort;
RgnHandle oldClip;
Rect tempRect;
Point mouseXY;
SInt16 linesToScroll = 0;
windowPtr = FrontWindow();
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
editRecHdl = (*docStrucHdl)->editRecHdl;
GetPort(&oldPort);
SetPort(windowPtr);
oldClip = NewRgn();
GetClip(oldClip);
SetRect(&tempRect,-32767,-32767,32767,32767);
ClipRect(&tempRect);
GetMouse(&mouseXY);
if(mouseXY.v < windowPtr->portRect.top)
{
linesToScroll = 1;
doSetScrollBarValue((*docStrucHdl)->vScrollbarHdl,&linesToScroll);
if(linesToScroll != 0)
TEScroll(0,linesToScroll * ((*editRecHdl)->lineHeight),editRecHdl);
}
else if(mouseXY.v > windowPtr->portRect.bottom)
{
linesToScroll = -1;
doSetScrollBarValue((*docStrucHdl)->vScrollbarHdl,&linesToScroll);
if(linesToScroll != 0)
TEScroll(0,linesToScroll * ((*editRecHdl)->lineHeight),editRecHdl);
}
if(linesToScroll != 0)
doDrawDataPanel(windowPtr);
SetClip(oldClip);
DisposeRgn(oldClip);
SetPort(oldPort);
return(true);
}
// doSetScrollBarValue
void doSetScrollBarValue(ControlHandle controlHdl,SInt16 *linesToScroll)
{
SInt16 controlValue, controlMax;
controlValue = GetControlValue(controlHdl);
controlMax = GetControlMaximum(controlHdl);
*linesToScroll = controlValue - *linesToScroll;
if(*linesToScroll < 0)
*linesToScroll = 0;
else if(*linesToScroll > controlMax)
*linesToScroll = controlMax;
SetControlValue(controlHdl,*linesToScroll);
*linesToScroll = controlValue - *linesToScroll;
}
// doAdjustMenus
void doAdjustMenus(void)
{
MenuHandle fileMenuHdl, editMenuHdl;
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
SInt32 scrapOffset;
fileMenuHdl = GetMenuHandle(mFile);
editMenuHdl = GetMenuHandle(mEdit);
if(gNumberOfWindows > 0)
{
windowPtr = FrontWindow();
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
editRecHdl = (*docStrucHdl)->editRecHdl;
EnableItem(fileMenuHdl,iClose);
if((*editRecHdl)->selStart < (*editRecHdl)->selEnd)
{
EnableItem(editMenuHdl,iCut);
EnableItem(editMenuHdl,iCopy);
EnableItem(editMenuHdl,iClear);
}
else
{
DisableItem(editMenuHdl,iCut);
DisableItem(editMenuHdl,iCopy);
DisableItem(editMenuHdl,iClear);
}
if(GetScrap(NULL,'TEXT',&scrapOffset) > 0)
EnableItem(editMenuHdl,iPaste);
else
DisableItem(editMenuHdl,iPaste);
if((*editRecHdl)->teLength > 0)
{
EnableItem(fileMenuHdl,iSaveAs);
EnableItem(editMenuHdl,iSelectAll);
}
else
{
DisableItem(fileMenuHdl,iSaveAs);
DisableItem(editMenuHdl,iSelectAll);
}
}
else
{
DisableItem(fileMenuHdl,iClose);
DisableItem(fileMenuHdl,iSaveAs);
DisableItem(editMenuHdl,iClear);
DisableItem(editMenuHdl,iSelectAll);
}
DrawMenuBar();
}
// 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:
doFileMenu(menuItem);
break;
case mEdit:
doEditMenu(menuItem);
break;
case kHMHelpMenuID:
if(FrontWindow())
doActivateDocWindow(FrontWindow(),false);
doHelpMenu(menuItem);
break;
}
HiliteMenu(0);
}
// doFileMenu
void doFileMenu(SInt16 menuItem)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
switch(menuItem)
{
case iNew:
if(windowPtr = doNewDocWindow())
ShowWindow(windowPtr);
break;
case iOpen:
doOpenCommand();
break;
case iClose:
doCloseWindow(FrontWindow());
break;
case iSaveAs:
docStrucHdl = (docStructureHandle) (GetWRefCon(FrontWindow()));
editRecHdl = (*docStrucHdl)->editRecHdl;
doSaveAsFile(editRecHdl);
break;
case iQuit:
gDone = true;
break;
}
}
// doEditMenu
void doEditMenu(SInt16 menuItem)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
SInt32 totalSize, contigSize, newSize, scrapOffset;
SInt16 selectionLength;
windowPtr = FrontWindow();
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
editRecHdl = (*docStrucHdl)->editRecHdl;
switch(menuItem)
{
case iUndo:
break;
case iCut:
if(ZeroScrap() == noErr)
{
PurgeSpace(&totalSize,&contigSize);
selectionLength = doGetSelectLength(editRecHdl);
if(selectionLength > contigSize)
doErrorAlert(eNoSpaceCut);
else
{
TECut(editRecHdl);
doAdjustScrollbar(windowPtr);
if(TEToScrap() != noErr)
ZeroScrap();
}
}
break;
case iCopy:
if(ZeroScrap() == noErr)
{
TECopy(editRecHdl);
if(TEToScrap() != noErr)
ZeroScrap();
}
break;
case iPaste:
newSize = (*editRecHdl)->teLength + GetScrap(NULL,'TEXT',&scrapOffset);
if(newSize > kMaxTELength)
doErrorAlert(eNoSpacePaste);
else
{
if(TEFromScrap() == noErr)
{
TEPaste(editRecHdl);
doAdjustScrollbar(windowPtr);
}
}
break;
case iClear:
TEDelete(editRecHdl);
doAdjustScrollbar(windowPtr);
break;
case iSelectAll:
TESetSelect(0,(*editRecHdl)->teLength,editRecHdl);
break;
}
doDrawDataPanel(windowPtr);
}
// doGetSelectLength
SInt16 doGetSelectLength(TEHandle editRecHdl)
{
SInt16 selectionLength;
selectionLength = (*editRecHdl)->selEnd - (*editRecHdl)->selStart;
return(selectionLength);
}
// doAdjustScrollbar
void doAdjustScrollbar(WindowPtr windowPtr)
{
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
SInt16 numberOfLines, controlMax, controlValue;
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
editRecHdl = (*docStrucHdl)->editRecHdl;
numberOfLines = (*editRecHdl)->nLines;
if(*(*(*editRecHdl)->hText + (*editRecHdl)->teLength - 1) == kReturn)
numberOfLines += 1;
controlMax = numberOfLines - (((*editRecHdl)->viewRect.bottom -
(*editRecHdl)->viewRect.top) /
(*editRecHdl)->lineHeight);
if(controlMax < 0)
controlMax = 0;
SetControlMaximum((*docStrucHdl)->vScrollbarHdl,controlMax);
controlValue = ((*editRecHdl)->viewRect.top - (*editRecHdl)->destRect.top) /
(*editRecHdl)->lineHeight;
if(controlValue < 0)
controlValue = 0;
else if(controlValue > controlMax)
controlValue = controlMax;
SetControlValue((*docStrucHdl)->vScrollbarHdl,controlValue);
#if TARGET_CPU_PPC
if(gMacOS85Present)
{
SetControlViewSize((*docStrucHdl)->vScrollbarHdl,(*editRecHdl)->viewRect.bottom -
(*editRecHdl)->viewRect.top);
}
#endif
TEScroll(0,((*editRecHdl)->viewRect.top - (*editRecHdl)->destRect.top) -
(GetControlValue((*docStrucHdl)->vScrollbarHdl) *
(*editRecHdl)->lineHeight),editRecHdl);
}
// doAdjustCursor
void doAdjustCursor(WindowPtr windowPtr)
{
GrafPtr oldPort;
RgnHandle arrowRegion, iBeamRegion;
Rect cursorRect;
Point mouseXY;
if(gInBackground)
{
SetCursor(&qd.arrow);
return;
}
GetPort(&oldPort);
SetPort(windowPtr);
arrowRegion = NewRgn();
iBeamRegion = NewRgn();
SetRectRgn(arrowRegion,-32768,-32768,32766,32766);
cursorRect = windowPtr->portRect;
cursorRect.bottom -= 15;
cursorRect.right -= 15;
LocalToGlobal(&topLeft(cursorRect));
LocalToGlobal(&botRight(cursorRect));
RectRgn(iBeamRegion,&cursorRect);
DiffRgn(arrowRegion,iBeamRegion,arrowRegion);
GetMouse(&mouseXY);
LocalToGlobal(&mouseXY);
if(PtInRgn(mouseXY,iBeamRegion))
{
SetCursor(*(GetCursor(iBeamCursor)));
CopyRgn(iBeamRegion,gCursorRegion);
}
else
{
SetCursor(&qd.arrow);
CopyRgn(arrowRegion,gCursorRegion);
}
DisposeRgn(arrowRegion);
DisposeRgn(iBeamRegion);
SetPort(oldPort);
}
// doCloseWindow
void doCloseWindow(WindowPtr windowPtr)
{
docStructureHandle docStrucHdl;
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
DisposeControl((*docStrucHdl)->vScrollbarHdl);
TEDispose((*docStrucHdl)->editRecHdl);
DisposeHandle((Handle) docStrucHdl);
DisposeWindow(windowPtr);
gNumberOfWindows --;
}
// doSaveAsFile
void doSaveAsFile(TEHandle editRecHdl)
{
StandardFileReply fileReply;
WindowPtr windowPtr;
SInt16 fileRefNum;
SInt32 dataLength;
Handle editTextHdl;
StandardPutFile("\pSave as:","\pUntitled",&fileReply);
if(fileReply.sfGood)
{
windowPtr = FrontWindow();
SetWTitle(windowPtr,fileReply.sfFile.name);
if(!(fileReply.sfReplacing))
FSpCreate(&fileReply.sfFile,' KJB','TEXT',fileReply.sfScript);
FSpOpenDF(&fileReply.sfFile,fsCurPerm,&fileRefNum);
dataLength = (*editRecHdl)->teLength;
editTextHdl = (*editRecHdl)->hText;
FSWrite(fileRefNum,&dataLength,*editTextHdl);
FSClose(fileRefNum);
}
}
// doOpenCommand
void doOpenCommand(void)
{
StandardFileReply fileReply;
SFTypeList fileTypes;
fileTypes[0] = 'TEXT';
StandardGetFile(NULL,1,fileTypes,&fileReply);
if(fileReply.sfGood)
doOpenFile(fileReply.sfFile);
}
// doOpenFile
void doOpenFile(FSSpec fileSpec)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
SInt16 fileRefNum;
SInt32 textLength;
Handle textBuffer;
if((windowPtr = doNewDocWindow()) == NULL)
return;
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
editRecHdl = (*docStrucHdl)->editRecHdl;
SetWTitle(windowPtr,fileSpec.name);
FSpOpenDF(&fileSpec,fsCurPerm,&fileRefNum);
SetFPos(fileRefNum,fsFromStart,0);
GetEOF(fileRefNum,&textLength);
if(textLength > 32767)
textLength = 32767;
textBuffer = NewHandle((Size) textLength);
FSRead(fileRefNum,&textLength,*textBuffer);
MoveHHi(textBuffer);
HLock(textBuffer);
TESetText(*textBuffer,textLength,editRecHdl);
HUnlock(textBuffer);
DisposeHandle(textBuffer);
FSClose(fileRefNum);
(*editRecHdl)->selStart = 0;
(*editRecHdl)->selEnd = 0;
ShowWindow(windowPtr);
}
// doDrawDataPanel
void doDrawDataPanel(WindowPtr windowPtr)
{
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
RGBColor whiteColour = { 0xFFFF, 0xFFFF, 0xFFFF };
RGBColor blackColour = { 0x0000, 0x0000, 0x0000 };
RGBColor blueColour = { 0x1818, 0x4B4B, 0x8181 };
ControlHandle controlHdl;
Rect panelRect;
Str255 textString;
docStrucHdl = (docStructureHandle) (GetWRefCon(windowPtr));;
editRecHdl = (*docStrucHdl)->editRecHdl;
controlHdl = (*docStrucHdl)->vScrollbarHdl;
MoveTo(0,282);
LineTo(495,282);
RGBForeColor(&whiteColour);
RGBBackColor(&blueColour);
SetRect(&panelRect,0,283,495,300);
EraseRect(&panelRect);
MoveTo(3,295);
DrawString("\pteLength nLines lineHeight");
MoveTo(225,295);
DrawString("\pdestRect.top controlValue contrlMax");
SetRect(&panelRect,47,284,88,299);
EraseRect(&panelRect);
SetRect(&panelRect,124,284,149,299);
EraseRect(&panelRect);
SetRect(&panelRect,204,284,222,299);
EraseRect(&panelRect);
SetRect(&panelRect,286,284,323,299);
EraseRect(&panelRect);
SetRect(&panelRect,389,284,416,299);
EraseRect(&panelRect);
SetRect(&panelRect,472,284,495,299);
EraseRect(&panelRect);
NumToString((SInt32) (*editRecHdl)->teLength,textString);
MoveTo(47,295);
DrawString(textString);
NumToString((SInt32) (*editRecHdl)->nLines,textString);
MoveTo(124,295);
DrawString(textString);
NumToString((SInt32) (*editRecHdl)->lineHeight,textString);
MoveTo(204,295);
DrawString(textString);
NumToString((SInt32) (*editRecHdl)->destRect.top,textString);
MoveTo(286,295);
DrawString(textString);
NumToString((SInt32) GetControlValue(controlHdl),textString);
MoveTo(389,295);
DrawString(textString);
NumToString((SInt32) GetControlMaximum(controlHdl),textString);
MoveTo(472,295);
DrawString(textString);
RGBForeColor(&blackColour);
RGBBackColor(&whiteColour);
}
// doErrorAlert
void doErrorAlert(SInt16 errorCode)
{
AlertStdAlertParamRec paramRec;
Str255 errorString;
SInt16 itemHit;
paramRec.movable = true;
paramRec.helpButton = false;
paramRec.filterProc = NULL;
paramRec.defaultText = (StringPtr) kAlertDefaultOKText;
paramRec.cancelText = NULL;
paramRec.otherText = NULL;
paramRec.defaultButton = kAlertStdAlertOKButton;
paramRec.cancelButton = 0;
paramRec.position = kWindowDefaultPosition;
GetIndString(errorString,rErrorStrings,errorCode);
if(errorCode < eWindow)
{
StandardAlert(kAlertStopAlert,errorString,NULL,¶mRec,&itemHit);
ExitToShell();
}
else
StandardAlert(kAlertCautionAlert,errorString,NULL,¶mRec,&itemHit);
}
// doHelpMenu
void doHelpMenu(SInt16 menuItem)
{
MenuHandle helpMenuHdl;
SInt16 origHelpItems, numItems;
HMGetHelpMenuHandle(&helpMenuHdl);
numItems = CountMenuItems(helpMenuHdl);
origHelpItems = numItems - 1;
if(menuItem > origHelpItems)
doHelp();
}
//
// HelpDialog.c
//
#include <Appearance.h>
#include <ControlDefinitions.h>
#include <Dialogs.h>
#include <Resources.h>
#include <TextUtils.h>
// .............................................................................. defines
#define rHelpModal 128
#define iUserPane 2
#define iScrollBar 3
#define iPopupMenu 4
#define eHelpDialog 9
#define eHelpDocRecord 10
#define eHelpText 11
#define eHelpPicture 12
#define rTextIntroduction 128
#define rTextCreatingText 129
#define rTextModifyHelp 130
#define rPictIntroductionBase 128
#define rPictCreatingTextBase 129
#define kTextInset 4
// ............................................................................. typedefs
typedef struct
{
Rect bounds;
PicHandle pictureHdl;
} pictInfoStructure;
typedef struct
{
TEHandle editRecHdl;
ControlHandle scrollbarHdl;
SInt16 pictCount;
pictInfoStructure *pictInfoStructurePtr;
} docStructure, ** docStructureHandle;
typedef struct
{
RGBColor backColour;
PixPatHandle backPixelPattern;
Pattern backBitPattern;
} backColourPattern;
// ..................................................................... global variables
ModalFilterUPP eventFilterUPP;
ControlUserPaneDrawUPP userPaneDrawFunctionUPP;
ControlActionUPP actionFunctionUPP;
backColourPattern gBackColourPattern;
SInt16 gTextResourceID;
SInt16 gPictResourceBaseID;
RgnHandle gSavedClipRgn = NULL;
extern Boolean gMacOS85Present;
// .................................................................. function prototypes
void doHelp (void);
void doCloseHelp (DialogPtr,GrafPtr);
void doDisposeDescriptors (void);
pascal void userPaneDrawFunction (ControlHandle,SInt16);
Boolean doGetText (DialogPtr,SInt16,Rect);
Boolean doGetPictureInfo (DialogPtr,SInt16);
pascal void actionFunction (ControlHandle,SInt16);
void doScrollTextAndPicts (DialogPtr);
void doDrawPictures (DialogPtr,Rect *);
pascal Boolean eventFilter (DialogPtr,EventRecord *,SInt16 *);
void doSaveBackground (backColourPattern *);
void doRestoreBackground (backColourPattern *);
void doSetBackgroundWhite (void);
extern void doUpdate (EventRecord *);
extern void doErrorAlert (SInt16);
// doHelp
void doHelp(void)
{
DialogPtr dialogPtr;
docStructureHandle docStrucHdl;
GrafPtr oldPort;
ControlHandle controlHdl;
SInt16 itemHit, menuItem;
Rect userItemRect, destRect, viewRect;
// ......................................................... create routine descriptors
eventFilterUPP = NewModalFilterProc((ProcPtr) eventFilter);
userPaneDrawFunctionUPP = NewControlUserPaneDrawProc((ProcPtr) userPaneDrawFunction);
actionFunctionUPP = NewControlActionProc((ProcPtr) actionFunction);
// ........................................ create dialog and attach document structure
if(!(dialogPtr = GetNewDialog(rHelpModal,NULL,(WindowPtr) -1)))
{
doErrorAlert(eHelpDialog);
doDisposeDescriptors();
return;
}
if(!(docStrucHdl = (docStructureHandle) NewHandle(sizeof(docStructure))))
{
doErrorAlert(eHelpDocRecord);
DisposeDialog(dialogPtr);
doDisposeDescriptors();
return;
}
SetWRefCon(dialogPtr,(SInt32) docStrucHdl);
// ............................................... set graphics port and default button
GetPort(&oldPort);
SetPort(dialogPtr);
SetDialogDefaultItem(dialogPtr,kStdOkItemIndex);
// ..................................................... set user pane drawing function
GetDialogItemAsControl(dialogPtr,iUserPane,&controlHdl);
SetControlData(controlHdl,kControlNoPart,kControlUserPaneDrawProcTag,
sizeof(userPaneDrawFunctionUPP),(Ptr) &userPaneDrawFunctionUPP);
// ......................... set destination and view rectangles, create edit structure
userItemRect = (*controlHdl)->contrlRect;
InsetRect(&userItemRect,kTextInset,kTextInset / 2);
destRect = viewRect = userItemRect;
(*docStrucHdl)->editRecHdl = TEStyleNew(&destRect,&viewRect);
// ................... assign handle to scroll bar to relevant document structure field
GetDialogItemAsControl(dialogPtr,iScrollBar,&controlHdl);
(*docStrucHdl)->scrollbarHdl = controlHdl;
// ............... initialise picture information structure field of document structure
(*docStrucHdl)->pictInfoStructurePtr = NULL;
// ....................... assign resource IDs of first topic's 'TEXT'/'styl' resources
gTextResourceID = rTextIntroduction;
gPictResourceBaseID = rPictIntroductionBase;
// ................................. load text resources and insert into edit structure
if(!(doGetText(dialogPtr,gTextResourceID,viewRect)))
{
doCloseHelp(dialogPtr,oldPort);
doDisposeDescriptors();
return;
}
// .... search for option-space charas in text and load same number of 'PICT' resources
if(!(doGetPictureInfo(dialogPtr,gPictResourceBaseID)))
{
doCloseHelp(dialogPtr,oldPort);
doDisposeDescriptors();
return;
}
// .......................... create an empty region for saving the old clipping region
gSavedClipRgn = NewRgn();
// ................................. show window and save background colour and pattern
ShowWindow(dialogPtr);
doSaveBackground(&gBackColourPattern);
// ............................................................. enter ModalDialog loop
do
{
ModalDialog(eventFilterUPP,&itemHit);
if(itemHit == iPopupMenu)
{
SetControlValue((*docStrucHdl)->scrollbarHdl,0);
GetDialogItemAsControl(dialogPtr,iPopupMenu,&controlHdl);
menuItem = GetControlValue(controlHdl);
switch(menuItem)
{
case 1:
gTextResourceID = rTextIntroduction;
gPictResourceBaseID = rPictIntroductionBase;
break;
case 2:
gTextResourceID = rTextCreatingText;
gPictResourceBaseID = rPictCreatingTextBase;
break;
case 3:
gTextResourceID = rTextModifyHelp;
break;
}
doSetBackgroundWhite();
if(!(doGetText(dialogPtr,gTextResourceID,viewRect)))
{
doCloseHelp(dialogPtr,oldPort);
doDisposeDescriptors();
return;
}
if(!(doGetPictureInfo(dialogPtr,gPictResourceBaseID)))
{
doCloseHelp(dialogPtr,oldPort);
doDisposeDescriptors();
return;
}
doDrawPictures(dialogPtr,&viewRect);
doRestoreBackground(&gBackColourPattern);
}
} while(itemHit != kStdOkItemIndex);
// ........................................................................... clean up
doCloseHelp(dialogPtr,oldPort);
doDisposeDescriptors();
}
// doCloseHelp
void doCloseHelp(DialogPtr dialogPtr,GrafPtr oldPort)
{
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
SInt16 a;
docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
editRecHdl = (*docStrucHdl)->editRecHdl;
if(gSavedClipRgn)
DisposeRgn(gSavedClipRgn);
if((*docStrucHdl)->editRecHdl)
TEDispose((*docStrucHdl)->editRecHdl);
if((*docStrucHdl)->pictInfoStructurePtr)
{
for(a=0;a<(*docStrucHdl)->pictCount;a++)
ReleaseResource((Handle) (*docStrucHdl)->pictInfoStructurePtr[a].pictureHdl);
DisposePtr((Ptr) (*docStrucHdl)->pictInfoStructurePtr);
}
DisposeHandle((Handle) docStrucHdl);
DisposeDialog(dialogPtr);
SetPort(oldPort);
}
// doDisposeDescriptors
void doDisposeDescriptors(void)
{
DisposeRoutineDescriptor(eventFilterUPP);
DisposeRoutineDescriptor(userPaneDrawFunctionUPP);
DisposeRoutineDescriptor(actionFunctionUPP);
}
// userPaneDrawFunction
pascal void userPaneDrawFunction(ControlHandle controlHdl,SInt16 thePart)
{
Rect itemRect, viewRect;
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
DialogPtr dialogPtr;
Boolean inState;
dialogPtr = (*controlHdl)->contrlOwner;
itemRect = (*controlHdl)->contrlRect;
InsetRect(&itemRect,1,1);
itemRect.right += 15;
DrawThemeListBoxFrame(&itemRect,kThemeStateActive);
if(((WindowPeek) dialogPtr)->visible)
inState = ((WindowPeek) dialogPtr)->hilited;
DrawThemeListBoxFrame(&itemRect,inState);
doSetBackgroundWhite();
EraseRect(&itemRect);
docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
editRecHdl = (*docStrucHdl)->editRecHdl;
viewRect = (*editRecHdl)->viewRect;
TEUpdate(&viewRect,editRecHdl);
doDrawPictures(dialogPtr,&viewRect);
doRestoreBackground(&gBackColourPattern);
}
// doGetText
Boolean doGetText(DialogPtr dialogPtr,SInt16 textResourceID,Rect viewRect)
{
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
Handle helpTextHdl;
StScrpHandle stylScrpRecHdl;
SInt16 numberOfLines, heightOfText, heightToScroll;
docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
editRecHdl = (*docStrucHdl)->editRecHdl;
TESetSelect(0,32767,editRecHdl);
TEDelete(editRecHdl);
(*editRecHdl)->destRect = (*editRecHdl)->viewRect;
SetControlValue((*docStrucHdl)->scrollbarHdl,0);
helpTextHdl = GetResource('TEXT',textResourceID);
if(helpTextHdl == NULL)
{
doErrorAlert(eHelpText);
return false;
}
stylScrpRecHdl = (StScrpHandle) GetResource('styl',textResourceID);
if(stylScrpRecHdl == NULL)
{
doErrorAlert(eHelpText);
return false;
}
TEStyleInsert(*helpTextHdl,GetHandleSize(helpTextHdl),stylScrpRecHdl,editRecHdl);
ReleaseResource(helpTextHdl);
ReleaseResource((Handle) stylScrpRecHdl);
numberOfLines = (*editRecHdl)->nLines;
heightOfText = TEGetHeight((SInt32) numberOfLines,1,editRecHdl);
if(heightOfText > (viewRect.bottom - viewRect.top))
{
heightToScroll = TEGetHeight((SInt32) numberOfLines,1,editRecHdl) -
(viewRect.bottom - viewRect.top);
SetControlMaximum((*docStrucHdl)->scrollbarHdl,heightToScroll);
ActivateControl((*docStrucHdl)->scrollbarHdl);
#if TARGET_CPU_PPC
if(gMacOS85Present)
{
SetControlViewSize((*docStrucHdl)->scrollbarHdl,(*editRecHdl)->viewRect.bottom -
(*editRecHdl)->viewRect.top);
}
#endif
}
else
{
DeactivateControl((*docStrucHdl)->scrollbarHdl);
}
return true;
}
// doGetPictureInfo
Boolean doGetPictureInfo(DialogPtr dialogPtr,SInt16 firstPictID)
{
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
Handle textHdl;
SInt32 offset, textSize;
SInt16 numberOfPicts, a, lineHeight, fontAscent;
SInt8 optionSpace[1] = "\xCA";
pictInfoStructure *pictInfoPtr;
Point picturePoint;
TextStyle whatStyle;
docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
if((*docStrucHdl)->pictInfoStructurePtr != NULL)
{
for(a=0;a<(*docStrucHdl)->pictCount;a++)
ReleaseResource((Handle) (*docStrucHdl)->pictInfoStructurePtr[a].pictureHdl);
DisposePtr((Ptr) (*docStrucHdl)->pictInfoStructurePtr);
(*docStrucHdl)->pictInfoStructurePtr = NULL;
}
(*docStrucHdl)->pictCount = 0;
editRecHdl = (*docStrucHdl)->editRecHdl;
textHdl = (*editRecHdl)->hText;
textSize = GetHandleSize(textHdl);
offset = 0;
numberOfPicts = 0;
HLock(textHdl);
offset = Munger(textHdl,offset,optionSpace,1,NULL,0);
while((offset >= 0) && (offset <= textSize))
{
numberOfPicts++;
offset++;
offset = Munger(textHdl,offset,optionSpace,1,NULL,0);
}
if(numberOfPicts == 0)
{
HUnlock(textHdl);
return true;
}
pictInfoPtr = (pictInfoStructure *) NewPtr(sizeof(pictInfoStructure) * numberOfPicts);
(*docStrucHdl)->pictInfoStructurePtr = pictInfoPtr;
offset = 0L;
for(a=0;a<numberOfPicts;a++)
{
pictInfoPtr[a].pictureHdl = GetPicture(firstPictID + a);
if(pictInfoPtr[a].pictureHdl == NULL)
{
doErrorAlert(eHelpPicture);
return false;
}
offset = Munger(textHdl,offset,optionSpace,1,NULL,0);
picturePoint = TEGetPoint((SInt16)offset,editRecHdl);
TEGetStyle(offset,&whatStyle,&lineHeight,&fontAscent,editRecHdl);
picturePoint.v -= lineHeight;
offset++;
pictInfoPtr[a].bounds = (**pictInfoPtr[a].pictureHdl).picFrame;
OffsetRect(&pictInfoPtr[a].bounds,
(((*editRecHdl)->destRect.right + (*editRecHdl)->destRect.left) -
(pictInfoPtr[a].bounds.right + pictInfoPtr[a].bounds.left) ) / 2,
- pictInfoPtr[a].bounds.top + picturePoint.v);
}
(*docStrucHdl)->pictCount = a;
HUnlock(textHdl);
return true;
}
// actionFunction
pascal void actionFunction(ControlHandle scrollbarHdl,SInt16 partCode)
{
docStructureHandle docStrucHdl;
DialogPtr dialogPtr;
TEHandle editRecHdl;
SInt16 delta, oldValue, offset, lineHeight, fontAscent;
Point thePoint;
Rect viewRect;
TextStyle style;
if(partCode)
{
dialogPtr = (*scrollbarHdl)->contrlOwner;
docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
editRecHdl = (*docStrucHdl)->editRecHdl;
viewRect = (*editRecHdl)->viewRect;
thePoint.h = viewRect.left + kTextInset;
if(partCode != kControlIndicatorPart)
{
switch(partCode)
{
case kControlUpButtonPart:
thePoint.v = viewRect.top - 4;
offset = TEGetOffset(thePoint,editRecHdl);
thePoint = TEGetPoint(offset,editRecHdl);
TEGetStyle(offset,&style,&lineHeight,&fontAscent,editRecHdl);
delta = thePoint.v - lineHeight - viewRect.top;
break;
case kControlDownButtonPart:
thePoint.v = viewRect.bottom + 2;
offset = TEGetOffset(thePoint,editRecHdl);
thePoint = TEGetPoint(offset,editRecHdl);
delta = thePoint.v - viewRect.bottom;
break;
case kControlPageUpPart:
thePoint.v = viewRect.top + 2;
offset = TEGetOffset(thePoint,editRecHdl);
thePoint = TEGetPoint(offset,editRecHdl);
TEGetStyle(offset,&style,&lineHeight,&fontAscent,editRecHdl);
thePoint.v += lineHeight - fontAscent;
thePoint.v -= viewRect.bottom - viewRect.top;
offset = TEGetOffset(thePoint,editRecHdl);
thePoint = TEGetPoint(offset,editRecHdl);
TEGetStyle(offset,&style,&lineHeight,&fontAscent,editRecHdl);
delta = thePoint.v - viewRect.top;
if(offset == 0)
delta -= lineHeight;
break;
case kControlPageDownPart:
thePoint.v = viewRect.bottom - 2;
offset = TEGetOffset(thePoint,editRecHdl);
thePoint = TEGetPoint(offset,editRecHdl);
TEGetStyle(offset,&style,&lineHeight,&fontAscent,editRecHdl);
thePoint.v -= fontAscent;
thePoint.v += viewRect.bottom - viewRect.top;
offset = TEGetOffset(thePoint,editRecHdl);
thePoint = TEGetPoint(offset,editRecHdl);
TEGetStyle(offset,&style,&lineHeight,&fontAscent,editRecHdl);
delta = thePoint.v - lineHeight - viewRect.bottom;
if(offset == (**editRecHdl).teLength)
delta += lineHeight;
break;
}
oldValue = GetControlValue(scrollbarHdl);
if(((delta < 0) && (oldValue > 0)) || ((delta > 0) &&
(oldValue < GetControlMaximum(scrollbarHdl))))
{
GetClip(gSavedClipRgn);
ClipRect(&dialogPtr->portRect);
SetControlValue(scrollbarHdl,oldValue + delta);
SetClip(gSavedClipRgn);
}
}
doScrollTextAndPicts(dialogPtr);
}
}
// doScrollTextAndPicts
void doScrollTextAndPicts(DialogPtr dialogPtr)
{
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
SInt16 scrollDistance, oldScroll;
Rect updateRect;
doSetBackgroundWhite();
docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
editRecHdl = (*docStrucHdl)->editRecHdl;
oldScroll = (*editRecHdl)->viewRect.top -(*editRecHdl)->destRect.top;
scrollDistance = oldScroll - GetControlValue((*docStrucHdl)->scrollbarHdl);
if(scrollDistance == 0)
{
doRestoreBackground(&gBackColourPattern);
return;
}
TEScroll(0,scrollDistance,editRecHdl);
if((*docStrucHdl)->pictCount == 0)
{
doRestoreBackground(&gBackColourPattern);
return;
}
updateRect = (*editRecHdl)->viewRect;
if(scrollDistance > 0)
{
if(scrollDistance < (updateRect.bottom - updateRect.top))
updateRect.bottom = updateRect.top + scrollDistance;
}
else
{
if( - scrollDistance < (updateRect.bottom - updateRect.top))
updateRect.top = updateRect.bottom + scrollDistance;
}
doDrawPictures(dialogPtr,&updateRect);
doRestoreBackground(&gBackColourPattern);
}
// doDrawPictures
void doDrawPictures(DialogPtr dialogPtr,Rect *updateRect)
{
docStructureHandle docStrucHdl;
TEHandle editRecHdl;
SInt16 pictCount, pictIndex, vOffset;
PicHandle thePictHdl;
Rect pictLocRect, dummyRect;
docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
editRecHdl = (*docStrucHdl)->editRecHdl;
vOffset = (*editRecHdl)->destRect.top - (*editRecHdl)->viewRect.top - kTextInset;
pictCount = (*docStrucHdl)->pictCount;
for(pictIndex = 0;pictIndex < pictCount;pictIndex++)
{
pictLocRect = (*docStrucHdl)->pictInfoStructurePtr[pictIndex].bounds;
OffsetRect(&pictLocRect,0,vOffset);
if(!SectRect(&pictLocRect,updateRect,&dummyRect))
continue;
thePictHdl = (*docStrucHdl)->pictInfoStructurePtr[pictIndex].pictureHdl;
LoadResource((Handle) thePictHdl);
HLock((Handle) thePictHdl);
GetClip(gSavedClipRgn);
ClipRect(updateRect);
DrawPicture(thePictHdl,&pictLocRect);
SetClip(gSavedClipRgn);
HUnlock((Handle) thePictHdl);
}
}
// eventFilter
pascal Boolean eventFilter(DialogPtr dialogPtr,EventRecord *eventStrucPtr,
SInt16 *itemHit)
{
Boolean handledEvent;
GrafPtr oldPort;
Point mouseXY;
ControlHandle controlHdl;
docStructureHandle docStrucHdl;
handledEvent = false;
if((eventStrucPtr->what == updateEvt) &&
((WindowPtr) eventStrucPtr->message != dialogPtr))
{
doUpdate(eventStrucPtr);
}
else
{
GetPort(&oldPort);
SetPort(dialogPtr);
if(eventStrucPtr->what == mouseDown)
{
mouseXY = eventStrucPtr->where;
GlobalToLocal(&mouseXY);
if(FindControl(mouseXY,dialogPtr,&controlHdl))
{
docStrucHdl = (docStructureHandle) GetWRefCon(dialogPtr);
if(controlHdl == (*docStrucHdl)->scrollbarHdl)
{
TrackControl((*docStrucHdl)->scrollbarHdl,mouseXY,actionFunctionUPP);
*itemHit = iScrollBar;
handledEvent = true;
}
}
}
else
{
handledEvent = StdFilterProc(dialogPtr,eventStrucPtr,itemHit);
}
SetPort(oldPort);
}
return(handledEvent);
}
// doSaveBackground
void doSaveBackground(backColourPattern *gBackColourPattern)
{
GrafPtr currentPort;
GetPort(¤tPort);
GetBackColor(&gBackColourPattern->backColour);
gBackColourPattern->backPixelPattern = NULL;
if((**((CGrafPtr) currentPort)->bkPixPat).patType != 0)
gBackColourPattern->backPixelPattern = ((CGrafPtr) currentPort)->bkPixPat;
else
gBackColourPattern->backBitPattern =
*(PatPtr) (*(**((CGrafPtr) currentPort)->bkPixPat).patData);
}
// doRestoreBackground
void doRestoreBackground(backColourPattern *gBackColourPattern)
{
RGBBackColor(&gBackColourPattern->backColour);
if(gBackColourPattern->backPixelPattern)
BackPixPat(gBackColourPattern->backPixelPattern);
else
BackPat(&gBackColourPattern->backBitPattern);
}
// doSetBackgroundWhite
void doSetBackgroundWhite(void)
{
RGBColor whiteColour = { 0xFFFF, 0xFFFF, 0xFFFF };
RGBBackColor(&whiteColour);
BackPat(&qd.white);
}
//
Demonstration Program 1 Comments
When this program is run, the user should explore both the text editor and the Help
dialog.
Text Editor
In the text editor, the user should perform all the actions usually associated with a
simple text editor, that is:
* Open a new document window, open an existing 'TEXT' file for display in a new
document window, and save a document to a 'TEXT' file.
* Enter new text and use the Edit menu Cut, Copy, Paste, and Clear commands to edit
the text. (Pasting between documents and other applications is supported.)
* Select text by clicking and dragging, double-clicking a word, shift-clicking, and
choosing the Select All command from the Edit menu. Also select large amounts of
text by clicking in the text and dragging the cursor above or below the window so as
to invoke auto-scrolling.
* Scroll a large document by dragging the scroll box (live scrolling is used),
clicking once in a scroll arrow or gray area, and holding the mouse down in a scroll
arrow or gray area.
Whenever any action is taken, the user should observe the changes to the values displayed
in the data panel at the bottom of each window. In particular, the relationship between
the destination rectangle and scroll bar control value should be noted.
The user should also note that outline highlighting is activated for all windows and that
the forward-delete key is supported by the application. (The forward-delete key is not
supported by TextEdit.)
Help Dialog
The user should choose Text1 Help from the Help menu to open the Help dialog and then
scroll through the three help topics, which may be chosen in the pop-up menu at the
bottom of the dialog. The help topics contain documentation on the Help dialog which
supplements the source code comments below.
Text1.c
#define
kMaxTELength represents the maximum allowable number of bytes in a TextEdit edit
structure. kTab, kDel, and kReturn representing the character codes generated by the
tab, delete and return keys.
#typedef
The docStructure data type will be used for a small document structure comprising a
handle to an edit structure and a handle to a (vertical) scroll bar.
Global Variables
scrollActionFunctionUPP will be assigned a universal procedure pointer to an action
function for the scroll bar. customClickLoopUPP will be assigned a universal procedure
pointer to a custom click loop function. gMacOS85Present will be assigned true if Mac OS
8.5 or later is present. gCursorRegion is related to the WaitNextEvent's mouseRgn
parameter. gNumberOfWindows will keep track of the number of windows open at any one time.
gOldControlValue will be assigned the scroll bar's control value.
main
The main function initialises the system software managers, sets up the menus, opens
a new document window and enters the event loop. gMacOS85Present is assigned true of Mac
OS 8.5 or later is present. This global variable will govern whether certain Control
Manager functions relating to proportional scroll boxes, and which are available only in
Mac OS 8.5 or later, are called.
eventLoop
Note that WaitNextEvent's sleep parameter is assigned the value returned by the call to
LMGetCaretTime.
If WaitNextEvent returns a null event, and provided at least one window is open, the
application-defined function doIdle is called.
doIdle
doIdle is called whenever a NULL event is received.
The first line gets a pointer to the front window, allowing the next line to attempt to
retrieve a handle to that window's document structure. If the attempt is successful,
TEIdle is called to blink the insertion point.
doEvents
Note that, in the case of a mouse-down event in the content area, the application-defined
function doInContent is called and that, in the case of key events, the
application-defined function doKeyEvent is called provided the key press is not a Command
key equivalent.
doKeyEvent
doKeyEvent handles all key-down events that are not Command key equivalents.
The first three lines get a handle to the edit structure which forms part of the front
window's document structure.
The next line filters out the tab key character code. (TextEdit does not support the tab
key and some applications may need to provide a tab key handler.)
The next character code to be filtered out is the forward-delete key character code.
TextEdit does not recognise this key, so this else if block provides forward-delete key
support for the program. The first line in this block gets the current selection length
from the edit structure. If this is zero (that is, there is no selection range and an
insertion point is being displayed), the selEnd field is increased by one. This, in
effect, creates a selection range comprising the character following the insertion point.
TEDelete deletes the current selection range from the edit structure. Such deletions
could change the number of text lines in the edit structure, requiring the vertical
scroll bar to be adjusted; hence the call to the application-defined function
doAdjustScrollbar.
Processing of those character codes which have not been filtered out is performed in the
else block. A new character must not be allowed to be inserted in the edit structure if
the TextEdit limit of 32,767 characters will be exceeded. Accordingly, and given that
TEKey replaces the selection range with the character passed to it, the first step is to
get the current selection length. If the current length of the edit structure minus the
selection length plus 1 is less than 32,767, the character code is passed to TEKey for
insertion into the edit structure. In addition, and since all this could change the
number of lines in the edit structure, the scroll bar adjustment function is called.
If the TextEdit limit will be exceeded by accepting the character, an alert box is
invoked advising the user of the situation.
The last line calls the application-defined function which prints data extracted from the
edit text and control structures at the bottom of the window.
scrollActionFunction
scrollActionFunction is associated with the vertical scroll bar. It is the hook function
which will be repeatedly called by TrackControl while the mouse button remains down in
the scroll box, scroll arrows or gray areas of the vertical scroll bar.
The first line gets a pointer to the window which "owns" the control. The next two lines
get a handle to the edit structure associated with the window.
Within the outer if block, the first if block executes if the control part is not the
scroll box (indicator). The purpose of the switch is to get a value into the variable
linesToScroll. If the mouse-down was in a scroll arrow, that value will be 1. If the
mouse-down was in a gray area, that value will be equivalent to one less than the number
of text lines that will fit in the view rectangle. (Subtracting 1 from the total number
of lines that will fit in the view rectangle ensures that the line of text at the
bottom/top of the view rectangle prior to a gray area scroll will be visible at the
top/bottom of the window after the scroll.)
Immediately after the switch, the value in linesToScroll is changed to a negative value
if the mouse-down occurred in either the down scroll arrow or down gray area.
The next block ensures that no scrolling action will occur if the document is currently
scrolled fully up (control value equals control maximum) or fully down (control value
equals 0). In either case, linesToScroll will be set to 0, meaning that the call to
TEScroll near the end of the function will not occur.
SetControlValue sets the control value to the value just previously calculated, that is,
to the current control value minus the value in linesToScroll.
The next line sets the value in linesToScroll back to what it was before the line
linesToScroll = controlvalue - linesToScroll executed. This value, multiplied by the
value in the lineHeight field of the edit structure, is later passed to TEScroll as the
parameter which specifies the number of pixels to scroll.
If the control part is the scroll box (indicator), the variable linesToScroll is assigned
a value equal to the control's value as it was last time this function was called minus
the control's current value. The global variable which holds the control's "old" value
is then assigned the control's current value preparatory to the next call to this
function.
With the number of lines to scroll determined, TEScroll is called to scroll the text
within the view rectangle by the number of pixels specified in the second parameter.
(Positive values scroll the text towards the bottom of the screen. Negative values
scroll the text towards the top.)
The last line is for demonstration purposes only. It calls the application-defined
function which prints data extracted from the edit and control structures at the bottom
of the window.
doInContent
doInContent continues mouse-down processing.
The first three lines retrieve a handle to the edit structure associated with that
window.
The next two lines convert the mouse-down coordinates from global to local coordinates.
(Local coordinates will be required by upcoming calls to FindControl, TrackControl, and
PtInRect.)
If the mouse-down was in a control (that is, the scroll bar), the global variable
gOldControlValue is assigned the value of the control to cater for the case of the
mouse-down being within the scroll box (indicator), and TrackControl is called with the
universal procedure pointer to the application defined action function passed in the
third parameter. TrackControl retains control until the mouse button is released, during
which time the previously described action function scrollActionFunction is repeatedly
called.
If the mouse-down was not in a control, PtInRect checks whether it occurred within the
view rectangle. (Note that the view rectangle is in local coordinates, so the mouse-down
coordinates passed as the first parameter to the PtInRect call must also be in local
coordinates.) If the mouse-down was in the view rectangle, a check is made of the shift
key position at the time of the mouse-down. The result is passed as the second parameter
in the call to TEClick. (TEClick's behaviour depends on the position of the shift key.)
doUpdate
doUpdate handles update events. The first three lines get the handle to the edit
structure associated with the window.
Between the usual BeginUpdate and EndUpdate calls, TEUpdate is called to draw the text in
the edit structure, UpdateControls is called to draw the scroll bar, and the data panel
is redrawn.
doActivateDocWindow
doActivateDocWindow handles window activation/deactivation.
The first two lines retrieve a handle to the edit structure for the window.
If the window is becoming active, its graphics port is set as the current graphics port.
The bottom of the view rectangle is then adjusted so that the height of the view
rectangle is an exact multiple of the value in the lineHeight field of the edit
structure. (This avoids the possibility of only part of the full height of a line of
text appearing at the bottom of the view rectangle.) TEActivate activates the edit
structure associated with the window, ActivateControl activates the scroll bar, and
doAdjustScrollbar adjusts the scroll bar.
If the window is becoming inactive, TEDeactivate deactivates the edit structure
associated with the window and DeactivateControl deactivates the scroll bar.
doNewDocWindow
doNewDocWindow is called at program launch and when the user chooses New or Open from the
File menu. It opens a new window, attaches a document structure to that window, creates
a vertical scroll bar, creates a monostyled edit structure, installs the custom click
loop function, enables automatic scrolling, and enables outline highlighting.
GetNewCWindow opens a new window and sets its colour graphics port as the current colour
graphics port. (Since the edit structure assumes the drawing environment specified in
the colour graphics port structure, setting the colour graphics port must be done before
the call to TENew to create the edit structure.)
The next two lines set the text size and font. (These will be copied from the colour
graphics port to the edit structure when TENew is called.)
The next block creates a document structure and assigns a handle to that structure to the
window structure's refCon field. The following line increments the global variable which
keeps track of the number of open windows. GetNewControl creates a vertical scroll bar
and assigns a handle to it to the appropriate field of the document structure. The next
block establishes the view and destination rectangles two pixels inside the window's port
rectangle less the scroll bar.
MoveHHi and HLock move the document structure high and lock it. A monostyled edit
structure is then created by TENew and its handle is assigned to the appropriate field of
the document structure. (If this call is not successful, the window and scroll bar are
disposed of, an error alert is displayed, and the function returns.) The handle to the
document structure is then unlocked.
TESetClickLoop installs the universal procedure pointer to the custom click loop function
customClickLoop in the clickLoop field of the edit structure. TEAutoView enables
automatic scrolling for the edit structure. TEFeatureFlag enables outline highlighting
for the edit structure.
The last line returns a pointer to the newly opened window.
customClickLoop
customClickLoop replaces the default click loop function so as to provide for scroll bar
adjustment in concert with automatic scrolling. Following a mouse-down within the view
rectangle, customClickLoop is called repeatedly by TEClick as long as the mouse button
remains down.
The first three lines retrieve a handle to the edit structure associated with the window.
The next two lines save the current colour graphics port and set the window's colour
graphics port as the current port.
The window's current clip region will have been set by TextEdit to be equivalent to the
view rectangle. Since the scroll bar has to be redrawn, the clipping region must be
temporarily reset to include the scroll bar. Accordingly, GetClip saves the current
clipping region and the following two lines set the clipping region to the bounds of the
coordinate plane.
GetMouse gets the current position of the cursor. If the cursor is above the top of the
port rectangle, the text must be scrolled downwards. Accordingly, the variable
linesToScroll is set to 1. The subsidiary function doSetScrollBarValue (see below) is then
called to, amongst other things, reset the scroll bar's value. Note that the value in
linesToScroll may be modified by doSetScrollBarValue. If linesToScroll is not set to 0 by
doSetScrollBarValue, TEScroll is called to scroll the text by a number of pixels equivalent
to the value in the lineHeight field of the edit structure, and in a downwards direction.
If the cursor is below the bottom of the port rectangle, the same process occurs except
that the variable linesToScroll is set to -1, thus causing an upwards scroll of the text
(assuming that the value in linesToScroll is not changed to 0 by doSetScrollBarValue).
If scrolling has occurred, doDrawDataPanel redraws the data panel. SetClip restores the
clipping region to that established by the view rectangle and SetPort restores the saved
colour graphics port. Finally, the last line returns true. (A return of false would
cause TextEdit to stop calling customClickLoop, as if the user had released the mouse
button.)
doSetScrollBarValue
doSetScrollBarValue is called from customClickLoop. Apart from setting the scroll
bar's value so as to cause the scroll box to follow up automatic scrolling, the function
checks whether the limits of scrolling have been reached.
The first two lines get the current control value and the current control maximum value.
At the next block, the value in the variable linesToScroll will be set to either 0 (if
the current control value is 0) or equivalent to the control maximum value (if the
current control value is equivalent to the control maximum value. If these modifications
do not occur, the value in linesToScroll will remain as established at the first line in
this block, that is, the current control value minus the value in linesToScroll as passed
to the function.
SetControlValue sets the control's value to the value in linesToScroll. The last line
sets the value in linesToScroll to 0 if the limits of scrolling have already been
reached, or to the value as it was when the doSetScrollBarValue function was entered.
doAdjustMenus
doAdjustMenus adjusts the menus. Much depends on whether any windows are currently open.
If at least one window is open, Lines The first three lines in the if block get a handle
to the edit structure associated with the front window and the first call to EnableItem
enables the Close item in the File menu. If there is a current selection range, the Cut,
Copy, and Clear items are enabled, otherwise they are disabled. If there is any text in
the desk scrap (the call to GetScrap), the Paste item is enabled, otherwise it is
disabled. If there is any text in the edit structure, the SaveAs and Select All items
are enabled, otherwise they are disabled.
If no windows are open, the Close, SaveAs, Clear, and Select All items are disabled.
doFileMenu
doFileMenu handles File menu choices, calling the appropriate application-defined
functions according to the menu item chosen. In the SaveAs case, a handle to the edit
structure associated with the front window is retrieved and passed as a parameter to the
appropriate application-defined function.
(Note that, because TextEdit, rather than file operations, is the real focus of this
program, the file-related code has been kept to a minimum, even to the extent of having
no Save-related, as opposed to SaveAs-related, code.)
doEditMenu
doEditMenu handles choices from the Edit menu. Recall that, in the case of monostyled
edit structures, TECut, TECopy, and TEPaste do not copy/paste text to/from the desk
scrap. This program, however, supports copying/pasting to/from the desk scrap.
Before the usual switch is entered, a handle to the edit structure associated with the
front window is retrieved.
The iCut case handles the Cut command. Firstly, the call to ZeroScrap attempts to empty
the desk scrap. If the call succeeds, PurgeSpace establishes the size of the largest
block in the heap that would be available if a general purge were to occur. The next
line gets the current selection length. If the selection length is greater than the
available memory, the user is advised via an error message. Otherwise, TECut is called to
remove the selected text from the edit structure and copy it to the TextEdit private
scrap. The scroll bar is adjusted, and TEToScrap is called to copy the private scrap to
the desk scrap. If the latter call is not successful, ZeroScrap cleans up as best it can
by emptying the desk scrap.
The iCopy case handles the Copy command. If the call to empty the desk scrap is
successful, TECopy is called to copy the selected text from the edit structure to the
TextEdit private scrap. TEToScrap then copies the private scrap to the desk scrap.
The iPaste case handles the Paste command, which must not proceed if the paste would
cause the TextEdit limit of 32,767 bytes to be exceeded. The first line establishes a
value equal to the number of bytes in the edit structure plus the number of bytes of the
specified data type ('TEXT') in the desk scrap. If this value exceeds the TextEdit
limit, the user is advised via an error message. Otherwise, TEFromScrap copies the desk
scrap to TextEdit's private scrap, TEPaste inserts the private scrap into the edit
structure, and the following line adjusts the scroll bar.
The iClear case handles the Clear command. TEDelete deletes the current selection range
from the edit structure and the following line adjusts the scroll bar.
The iSelectAll case handle the Select All command. TESetSelect sets the selection range
according to the first two parameters (selStart and selEnd).
doGetSelectLength
doGetSelectLength returns a value equal to the length of the current selection.
doAdjustScrollbar
doAdjustScrollbar adjusts the vertical scroll bar.
The first two lines retrieve handles to the document structure and edit structure
associated with the window in question.
At the next block, the value in the nLines field of the edit structure is assigned to the
numberOfLines variable. The next action is somewhat of a refinement and is therefore not
essential. If the last character in the edit structure is the return character,
numberOfLines is incremented by one. This will ensure that, when the document is
scrolled to its end, a single blank line will appear below the last line of text.
At the next block, the variable controlMax is assigned a value equal to the number of
lines in the edit structure less the number of lines which will fit in the view
rectangle. If this value is less than 0 (indicating that the number of lines in the edit
structure is less than the number of lines that will fit in the view rectangle),
controlMax is set to 0. SetControlMaximum then sets the control maximum value. If
controlMax is 0, the scroll bar is automatically unhighlighted by the SetControlMaximum
call.
The first line of the next block assigns to the variable controlValue a value equal to
the number of text lines that the top of the destination rectangle is currently "above"
the top of the view rectangle. If the calculation returns a value less than 0 (that is,
the document has been scrolled fully down), controlValue is set to 0. If the calculation
returns a value greater than the current control maximum value (that is, the document has
been scrolled fully up), controlValue is set to equal that value. SetControlValue sets
the control value to the value in controlValue. For example, if the top of the view
rectangle is 2, the top of the destination rectangle is -34 and the lineHeight field of
the edit structure contains the value 13, the control value will be set to 3.
If the target is the PowerPC target, and if Mac OS 8.5 or later is present,
SetControlViewSize is called to advise the Control Manager of the height of the view
rectangle. If Smart Scrolling is selected on in the Options tab of the Mac OS 8.5 and
later Appearance control panel, this will cause the scroll boxes of the scroll bar to be
proportional.
With the control maximum value and the control value set, TEScroll is called to make sure
the text is scrolled to the position indicated by the scroll box. Extending the example
in the previous paragraph, the second parameter in the TEScroll call is 2 - (34 - (3 *
13)), that is, 0. In that case, no corrective scrolling actually occurs.
doAdjustCursor
doAdjustCursor adjusts the cursor to the I-Beam shape when the cursor is over the content
region less the scroll bar area, and to the arrow shape when the cursor is outside that
region. It is similar to the cursor adjustment function in the demonstration program at
Chapter 13 - Offscreen Graphics Worlds, Pictures, Cursors, and Icons.
doCloseWindow
doCloseWindow disposes of the specified window. The associated scroll bar, the
associated edit structure and the associated document structure are disposed of before
the call to DisposeWindow.
doSaveAsFile, doOpenCommand, doOpenFile
The functions doSaveAsFile, doOpenCommand, and doOpenFile are document saving and opening
functions, enabling the user to open and save 'TEXT' documents. Since the real focus of
this program is TextEdit, not file operations, the code is "bare bones" and as brief as
possible.
For a complete example of opening and saving monostyled 'TEXT' documents, see the
demonstration program at Chapter 16 - Files.
doDrawDataPanel
doDrawDataPanel draws the data panel at the bottom of each window. Displayed in this
panel are the values in the teLength, nLines, lineHeight and destRect.top fields of the
edit structure and the contrlValue and contrlMax fields of the scroll bar's control
structure.
doHelpMenu
doHelpMenu handles a choice of the Text1 Help item added by this program to the
system-managed Help Menu. The code reflects the fact that Apple reserves the right to
add items to the Help menu in future versions of the system software.
HMGetHelpMenuHandle gets a handle to the Help menu structure. CountMenuItems returns the
number of items in the Help menu. Since we know that we have added one item to this
menu, the next line will establish the original number of help items. If the value
passed to the doHelpMenu function is greater than this, it must therefore represent the
item number of our Text1 Help item, in which case the application-defined function which
opens the help dialog is called.
HelpDialog.c
#define
Constants are established for the 'DLOG' resource ID and items in the dialog, the index
of error strings within a 'STR#' resource, and 'TEXT', 'styl', and 'PICT' resource IDs.
kTextInset which will be used to inset the view and destination rectangles a few pixels
inside the user pane's rectangle.
#typedef
The first two data types are for a picture information structure and a document
structure. (Note that one field in the document structure is a pointer to a picture
information structure.) The third data type will be used for saving and restoring the
background colour and pattern.
doHelp
doHelp is called when the user chooses the Text1 Help item in the Help menu.
The dialog will utilise an event filter function, a user pane drawing function, and an
action function. The first three lines create the associated routine descriptors.
GetNewDialog creates the dialog. NewHandle creates a block for a document structure and
the handle is assigned to the dialog's window structure's refCon field.
SetDialogDefaultItem sets the dialog's push button as the default item and ensures that
the default ring will be drawn around that item.
At the next block, SetControlData sets the user pane drawing function.
At the next block, the destination and view rectangles are both made equal to the user
pane's rectangle, but inset four pixels from the left and right and two pixels from the
top and bottom. The call to TEStyleNew creates a multistyled edit structure based on
those two rectangles.
The next block assigns the handle to the scrollbar to the appropriate field of the
dialog's document structure.
A pointer to a picture information structure will eventually be assigned to a field in
the document structure. For the moment, that field is set to NULL.
At the next block, two global variables are assigned the resource IDs relating to the
first Help topic's 'TEXT'/'styl' resource and associated 'PICT' resources.
The next block calls the application-defined function doGetText which, amongst other
things, loads the specified 'TEXT'/'styl' resources and inserts the text and style
information into the edit structure.
The next block calls the application-defined function doGetPictureInfo which, amongst
other things, searches for option-space characters in the 'TEXT' resource and, if
option-space characters are found, loads a like number of 'PICT' resources beginning with
the specified ID.
NewRgn creates an empty region, which will be used to save the dialog's colour graphic's
port's clipping region.
To complete the initial setting up, ShowWindow is called to makes the dialog visible and
the application-defined function doSaveBackground is called to save the background colour
and pattern of the dialog as they were when the dialog was created.
The do-while ModalDialog loop continues until the user clicks the "Done" (OK) button or
hits the Return key.
The modal dialog uses an application-defined filter function which, as will be seen,
handles mouse-down events within the scrollbar. The only other event of interest is a
hit on the pop-up menu button. Accordingly, if ModalDialog returns a hit on that
control, SetControlValue is called to set the scroll bar's value to 0, the menu item
chosen is determined, and the switch assigns the appropriate 'TEXT'/'styl' and 'PICT'
resource IDs to the global variables which keep track of which of those resources are to
be loaded and displayed.
Before anything is drawn in that part of the dialog used to display the text and
pictures, the background colour and pattern are set to, respectively, the white colour
and the white pattern by a call to doSetBackgroundWhite. The next block then perform the
same "get text" and "get picture information" actions as were preformed at start-up, but
this time with the 'TEXT'/'styl' and 'PICT' resources as determined within the preceding
switch.
The call to doDrawPictures draws any pictures that might initially be located in the view
rectangle. With all drawing completed, the application-defined function
doRestoreBackground is called to restore the saved background colour and pattern.
When the user clicks the "Done" (OK) button or hits the Return key, application-defined
functions are called to close down the Help dialog and dispose of the routine
descriptors.
doCloseHelp
doCloseHelp closes down the Help dialog.
The first two lines retrieve a handle to the dialog's document structure. The next two
lines dispose of region used to save the clipping region. TEDispose disposes of the edit
structure. The next block disposes of any 'PICT' resources currently in memory, together
with the picture information structure. Finally, the dialog's document structure is
disposed of, the dialog itself is disposed of, and the graphics port saved in doHelp is
restored.
userPaneDrawFunction
userPaneDrawFunction is the user pane drawing function set within doHelp. It will be
called automatically whenever the dialog receives an update event.
The first block gets a pointer to the dialog to which the user pane control belongs, gets
the user pane's rectangle, insets that rectangle by one pixel all round, and then further
expands it to the right to the right edge of the scroll bar. At the next block, a list
box frame is drawn in the appropriate state, depending on whether the window is the
movable modal dialog is currently the active window. (The first call to
DrawThemeListBoxFrame is required so as to ensure that the list box frame is drawn when
the dialog is initially displayed.)
The next block erases the previously defined rectangle with the white colour using the
white pattern.
The three lines retrieve the view rectangle from the edit structure. The call to
TEUpdate draws the text in the edit structure in the view rectangle. The call to
drawPictures draws any pictures which might currently be located in the view rectangle.
The last line restores the background colour and pattern saved in doHelp.
doGetText
doGetText is called when the dialog is first opened and when the user chooses a new item
from the pop-up menu. Amongst other things, it loads the 'TEXT'/'styl' resources
associated with the current menu item and inserts the text and style information into the
edit structure.
The first two lines get a handle to the edit structure. The next two lines set the
selection range to the maximum value and then delete that selection. The destination
rectangle is then made equal to the view rectangle and the scroll bar's value is set to
0.
GetResource is called twice to load the specified 'TEXT'/'styl' resources, following
which TEStyleInsert is called to insert the text and style information into the edit
structure. Two calls to ReleaseResource then release the 'TEXT'/'styl' resources.
The next block gets the total height of the text in pixels.
At the next block, if the height of the text is greater than the height of the view
rectangle, the local variable heightToScroll is made equal to total height of the text
minus the height of the view rectangle. This value is then used to set the scroll bar's
maximum value. The scroll bar is then made active.
If the target is the PowerPC target, and if Mac OS 8.5 or later is present,
SetControlViewSize is called to advise the Control Manager of the height of the view
rectangle. If Smart Scrolling is selected on in the Options tab of the Mac OS 8.5 and
later Appearance control panel, this will cause the scroll boxes of the scroll bar to be
proportional.
If the height of the text is less than the height of the view rectangle, the scroll bar
is made inactive.
true is returned if the GetResource calls did not return with false.
doGetPictureInfo
doGetPictureInfo is called after getText when the dialog is opened and when the
user chooses a new item from the pop-up menu. Amongst other things, it searches for
option-space characters in the 'TEXT' resource and, if option-space characters are found,
loads a like number of 'PICT' resources beginning with the specified ID.
The first line gets a handle to the dialog's document structure.
If the picInfoRecPtr field of the document structure does not contain NULL, the currently
loaded 'PICT' resources are released, the picture information structures are disposed of,
and the picInfoRecPtr field of the document structure is set to NULL.
The next line sets to 0 the field of the document structure which keeps track of the
number of pictures associated with the current 'TEXT' resource.
The next two lines get a handle to the edit structure, then a handle to the block
containing the actual text. This latter is then used at to assign the size of that block
to a local variable. After two local variables are initialised, the block containing the
text is locked.
The next block counts the number of option-space characters in the text block. At the
following block, if there are no option-space characters in the block, the block is
unlocked and the function returns.
A call to NewPtr then allocates a nonrelocatable block large enough to accommodate a
number of picture information structures equal to the number of option-space characters
found. The pointer to the block is then assigned to the appropriate field of the
dialog's document structure.
The next line resets the offset value to 0.
The for loop repeats for each of the option-space characters found. GetPicture loads the
specified 'PICT' resource (the resource ID being incremented from the base ID at each
pass through the loop) and assigns the handle to appropriate field of the relevant
picture information structure. Munger finds the offset to the next option-space
character and TEGetPoint gets the point, based on the destination rectangle, of the
bottom left of the character at that offset. TEGetStyle is called to obtain the line
height of the character at the offset and this value is subtracted from the value in the
point's v field. The offset is incremented and the rectangle in the picture structure's
picFrame field is assigned to the bounds field of the picture information structure. The
next block then offsets this rectangle so that it is centred laterally in the destination
rectangle with its top offset from the top of the destination rectangle by the amount
established at the line picturePoint.v -= lineHeight;.
The third last line assigns the number of pictures loaded to the appropriate field of the
dialog's document structure. The block containing the text is then unlocked. The
function returns true if false has not previously been returned within the for loop.
actionFunction
actionFunction is the action function called from within the event filter function
eventFilter. It is repeatedly called by TrackControl while the mouse button remains down
within the scroll bar. Its ultimate purpose is to determine the new scrollbar value when
the mouse-down is within the scroll arrows or gray areas of the scroll bar, and then call
a separate application-defined function to effect the actual scrolling of the text and
pictures based on the new scrollbar value. (The scroll bar is the live scrolling
variant, so the CEDF automatically updates the control's value while the mouse remains
down in the scroll box.)
Firstly, if the cursor is still not within the control, execution falls through to the
bottom of the function and the action function exits.
The first block gets a pointer to the owner of the scrollbar, retrieves a handle to the
dialog's document structure, gets a handle to the edit structure, gets the view
rectangle, and assigns a value to the h field of a point variable equal to the left of
the view rectangle plus 4 pixels.
The switch executes only if the mouse-down is not in the scroll box.
In the case of the Up scroll arrow, the variable delta is assigned a value which will
ensure that, after the scroll, the top of the incoming line of text will be positioned
cleanly at top of the view rectangle.
In the case of the Down scroll arrow, the variable delta is assigned a value which will
ensure that, after the scroll, the bottom of the incoming line of text will be positioned
cleanly at bottom of the view rectangle.
In the case of the Up gray area, the variable delta is assigned a value which will ensure
that, after the scroll, the top of the top line of text will be positioned cleanly at the
top of the view rectangle and the line of text which was previously at the top will still
be visible at the bottom of the view rectangle.
In the case of the Down gray area, the variable delta is assigned a value which will
ensure that, after the scroll, the bottom of the bottom line of text will be positioned
cleanly at the bottom of the view rectangle and the line of text which was previously at
the bottom will still be visible at the top of the view rectangle.
The first line after the switch gets the pre-scroll scroll bar value. If the text is not
fully scrolled up and a scroll up is called for, or if the text is not fully scrolled
down and a scroll down is called for, the current clipping region is saved, the clipping
region is set to the dialog's port rectangle, the scroll bar value is set to the required
new value, and the saved clipping region is restored. (TextEdit may have set the
clipping region to the view rectangle, so it must be changed to include the scroll bar
area, otherwise the scroll bar will not be drawn.)
With the scroll bar's new value set and the scroll box redrawn in its new position, the
application-defined function for scrolling the text and pictures is called. Note that
this last line will also be called if the mouse-down was within the scroll box.
doScrollTextAndPicts
doScrollTextAndPicts is called from actionFunction. It scrolls the text within the view
rectangle and calls another application-defined function to draw any picture whose
rectangle intersects the "vacated" area of the view rectangle.
The first line sets the background colour to white and the background pattern to white.
The next two lines get a handle to the edit structure. The next line determines the
difference between the top of the destination rectangle and the top of the view rectangle
and the next subtracts from this value the scroll bar's new value. If the result is
zero, the text must be fully scrolled in one direction or the other, so the function
simply returns.
If the text is not already fully scrolled one way or the other, TEScroll scrolls the text
in the view rectangle by the number of pixels determined at the fifth line.
If there are no pictures associated with the 'TEXT' resource in use, the function returns
immediately after the text is scrolled.
The next consideration is the pictures and whether any of their rectangles, as stored in
the picture information structure, intersect the area of the view rectangle "vacated" by
the scroll. At the if/else block, a rectangle is made equal to the "vacated" area of the
view rectangle, the if block catering for the scrolling up case and the else block
catering for the scrolling down case. (Of course, if the scroll box has been dragged by
the user over a large distance, the "vacated" area will equate to the whole view
rectangle.) This rectangle is passed as a parameter in the call to drawPictures.
doDrawPictures
doDrawPictures determines whether any pictures intersect the rectangle passed to it as a
formal parameter and draws any pictures that do.
The first two lines get handles to the dialog's document structure and the edit
structure.
The next line determines the difference between the top of the destination rectangle and
the top of the view rectangle. This will be used later to offset the picture's rectangle
from destination rectangle coordinates to view rectangle coordinates. The next line
determines the number of pictures associated with the current 'TEXT' resource, a value
which will be used to control the number of passes through the following for loop.
Within the loop, the picture's rectangle is retrieved from the picture information
structure and offset to the coordinates of the view rectangle. SectRect determines
whether this rectangle intersects the rectangle passed to drawPictures from
scrollTextAndPicts. If it does not, the loop returns for the next iteration. If it
does, the picture's handle is retrieved, LoadResource checks whether the 'PICT' resource
is in memory and, if necessary, loads it, HLock locks the handle, DrawPicture draws the
picture, and HUnlock unlocks the handle. Before DrawPicture is called, the clipping
region is temporarily adjusted to equate to the rectangle passed to drawPictures from
scrollTextAndPicts so as to limit drawing to that rectangle.
eventFilter
eventFilter is the application-defined filter function for the dialog. It is essentially
the same as the event filter introduced in the demonstration program Dialogs (Chapter 8)
except that it also intercepts mouse-down events.
In the case of a mouse-down event, the location of the event is determined in local
coordinates. If FindControl and the following two lines determine that the mouse-down
was within the scrollbar, TrackControl is called to handle user interaction while the
mouse button remains down. While the mouse button remains down, TrackControl continually
calls actionFunction.
doSaveBackground, doRestoreBackground, and doSetBackgroundWhite
doSaveBackground and doRestoreBackground are essentially the same as the functions
introduced at Chapter 12 - Drawing With QuickDraw for saving and restoring the drawing
environment. In this case, the saving and restoring is limited to the background colour
and the background bit or pixel pattern. doSetBackgroundWhite sets the background colour
to white and the background pattern to the pattern white.
|