Demonstration Program
//
// Files1.h
//
//
// This program demonstrates:
//
// Application-defined file handling functions associated with:
//
// The user invoking the File menu Open..., Close, Save..., Save As..., Revert to
// Saved, and Quit commands of a typical application.
//
// Handling of the required Apple events Open Application, Open Documents and
// Quit Application.
//
// A customised Open dialog which allows the user, via an added pop-up menu button, to
// specify the types of file (TEXT or PICT) to be displayed in the dialog's list.
//
// A directory selection dialog.
//
// To keep the code not specifically related to files and file-handling to a minimum, an
// item is included in the Demonstration menu which allows the user to simulate making a
// window "touched" (that is, modifying the contents of the associated document).
// Choosing the first menu item in this menu sets the windowTouched flag in the window's
// document structure to true and draws the text "WINDOW TOUCHED" in the window in a
// large font size, this latter so that the user can keep track of which windows are
// "touched".
//
// The program utilises the following resources:
//
// An 'MBAR' resource, and 'MENU' resources for Apple, File, Edit and Demonstration
// menus (preload, non-purgeable).
//
// A 'WIND' resource (purgeable) (initially not visible).
//
// Two 'ALRT' resources (purgeable) and associated 'alrx', 'DITL', and 'dftb'
// resources (purgeable). The first is used to support the Revert to Saved menu item.
// The second is used when an attempt is made to close a modified document before
// that document has been saved.
//
// Two 'DLOG' resources ((purgeable) and associated 'dlgx' and 'DITL' resources
// (purgeable). The first is for the customised Open dialog. The second is for the
// directory selection dialog.
//
// Two 'CNTL' resources (purgeable) for the items added to the customised Open dialog.
// The first is for a pop-up menu button. The second is for a separator line.
//
// A 'MENU' resource for the popu-up menu button.
//
// A 'STR ' resource (purgeable) containing the "missing application name" string
// resource, which is copied to all document files created by the program.
//
// A 'STR#' resource containing error strings.
//
// A 'SIZE' resource with the acceptSuspendResumeEvents, isHighLevelEventAware, and
// is32BitCompatible flags set (non-purgeable).
//
// The 'BNDL' resource (non-purgeable), 'FREF' resources (non-purgeable), signature
// resource (non-purgeable), and icon family resources (purgeable), required to
// support the built application.
//
//
// ............................................................................. includes
#include <Appearance.h>
#include <AERegistry.h>
#include <Devices.h>
#include <Folders.h>
#include <Navigation.h>
#include <Resources.h>
#include <Sound.h>
#include <ToolUtils.h>
// ............................................................................. typedefs
typedef struct
{
TEHandle editStrucHdl;
PicHandle pictureHdl;
SInt16 fileRefNum;
FSSpec fileFSSpec;
Boolean windowTouched;
} docStructure, *docStructurePointer, **docStructureHandle;
typedef StandardFileReply *standardFileReplyPtr;
// .............................................................................. defines
#define mApple 128
#define iAbout 1
#define mFile 129
#define iNew 1
#define iOpen 2
#define iClose 4
#define iSave 5
#define iSaveAs 6
#define iRevert 7
#define iQuit 12
#define mDemonstration 131
#define iTouchWindow 1
#define iSelectDirectoryDialog 3
#define rNewWindow 128
#define rMenubar 128
#define rRevertAlert 128
#define rCloseFileAlert 129
#define rCustomOpenDialog 130
#define iPopupItem 10
#define rSelectDirectoryDialog 131
#define iSelectButton 10
#define rErrorStrings 128
#define eInstallHandler 1000
#define eMaxWindows 1001
#define eFileIsOpen opWrErr
#define kMaxWindows 10
#define kUserCancelled 1002
#define MAXLONG 0x7FFFFFFF
#define MIN(a,b) ((a) < (b) ? (a) : (b))
// .................................................................. function prototypes
void main (void);
void eventLoop (void);
void doInitManagers (void);
void doInstallAEHandlers (void);
void doEvents (EventRecord *);
void doMouseDown (EventRecord *);
void doUpdate (EventRecord *);
void doMenuChoice (SInt32);
void doFileMenu (SInt16);
void doAdjustMenus (void);
void doErrorAlert (SInt16);
void doCopyPString (Str255,Str255);
void doConcatPStrings (Str255,Str255);
void doTouchWindow (void);
pascal OSErr doOpenAppEvent (AppleEvent *,AppleEvent *,SInt32);
pascal OSErr doReopenAppEvent (AppleEvent *,AppleEvent *,SInt32);
pascal OSErr doOpenDocsEvent (AppleEvent *,AppleEvent *,SInt32);
pascal OSErr doQuitAppEvent (AppleEvent *,AppleEvent *,SInt32);
OSErr doHasGotRequiredParams (AppleEvent *);
OSErr doNewCommand (void);
OSErr doOpenCommand (void);
OSErr doCloseCommand (void);
OSErr doSaveCommand (void);
OSErr doSaveAsCommand (void);
OSErr doRevertCommand (void);
OSErr doQuitCommand (void);
OSErr doNewDocWindow (Boolean,OSType);
OSErr doOpenFile (FSSpec,OSType);
OSErr doReadTextFile (WindowPtr);
OSErr doReadPictFile (WindowPtr);
OSErr doCloseFile (WindowPtr,docStructureHandle);
OSErr doWriteFile (WindowPtr);
OSErr doWriteTextData (WindowPtr,SInt16);
OSErr doWritePictData (WindowPtr,SInt16);
OSErr doCopyAppNameResource (WindowPtr);
OSErr doCopyResource (ResType,SInt16,SInt16,SInt16);
pascal Boolean filterFunctionOpenDialog (CInfoPBPtr,void *);
pascal SInt16 hookFunctionOpenDialog (SInt16,DialogPtr,void *);
StandardFileReply doDirectorySelectionDialog (void);
pascal Boolean filterFunctionDirSelect (CInfoPBPtr,void *);
pascal SInt16 hookFunctionDirSelect (SInt16,DialogPtr,void *);
//
// Files1.c
//
// ............................................................................. includes
#include "Files1.h"
// ..................................................................... global variables
Boolean gDone;
Boolean gInBackground;
AEEventHandlerUPP doOpenAppEventUPP;
AEEventHandlerUPP doReopenAppEventUPP;
AEEventHandlerUPP doOpenDocsEventUPP;
AEEventHandlerUPP doQuitAppEventUPP;
SInt16 gAppResFileRefNum;
extern SInt16 gCurrentNumberOfWindows;
extern Rect gDestRect,gViewRect;
extern Boolean gDirectorySelectionFlag;
// main
void main(void)
{
Handle menubarHdl;
MenuHandle menuHdl;
// .................................................................initialise managers
doInitManagers();
// ................................. create routine decriptors for Apple event handlers
doOpenAppEventUPP = NewAEEventHandlerProc((ProcPtr) doOpenAppEvent);
doReopenAppEventUPP = NewAEEventHandlerProc((ProcPtr) doReopenAppEvent);
doOpenDocsEventUPP = NewAEEventHandlerProc((ProcPtr) doOpenDocsEvent);
doQuitAppEventUPP = NewAEEventHandlerProc((ProcPtr) doQuitAppEvent);
// ........................... set application's resource fork as current resource file
gAppResFileRefNum = CurResFile();
// .......................................................... set up menu bar and menus
menubarHdl = GetNewMBar(rMenubar);
if(menubarHdl == NULL)
doErrorAlert(MemError());
SetMenuBar(menubarHdl);
DrawMenuBar();
menuHdl = GetMenuHandle(mApple);
if(menuHdl == NULL)
doErrorAlert(MemError());
else
AppendResMenu(menuHdl,'DRVR');
// .............................................. install required Apple event handlers
doInstallAEHandlers();
// ................................................................... enter event loop
eventLoop();
}
// eventLoop
void eventLoop(void)
{
EventRecord eventStructure;
gDone = false;
while(!gDone)
{
if(WaitNextEvent(everyEvent,&eventStructure,MAXLONG,NULL))
doEvents(&eventStructure);
}
}
// doInitManagers
void doInitManagers(void)
{
MaxApplZone();
MoreMasters();
InitGraf(&qd.thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(NULL);
InitCursor();
FlushEvents(everyEvent,0);
RegisterAppearanceClient();
}
// doInstallAEHandlers
void doInstallAEHandlers(void)
{
OSErr osError;
osError = AEInstallEventHandler(kCoreEventClass,kAEOpenApplication,doOpenAppEventUPP,
0L,false);
if(osError != noErr) doErrorAlert(eInstallHandler);
osError = AEInstallEventHandler(kCoreEventClass,kAEReopenApplication,
doReopenAppEventUPP,0L,false);
if(osError != noErr) doErrorAlert(eInstallHandler);
osError = AEInstallEventHandler(kCoreEventClass,kAEOpenDocuments,doOpenDocsEventUPP,
0L,false);
if(osError != noErr) doErrorAlert(eInstallHandler);
osError = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,doQuitAppEventUPP,
0L,false);
if(osError != noErr) doErrorAlert(eInstallHandler);
}
// doEvents
void doEvents(EventRecord *eventStrucPtr)
{
SInt8 charCode;
switch(eventStrucPtr->what)
{
case kHighLevelEvent:
AEProcessAppleEvent(eventStrucPtr);
break;
case mouseDown:
doMouseDown(eventStrucPtr);
break;
case keyDown:
case autoKey:
charCode = eventStrucPtr->message & charCodeMask;
if((eventStrucPtr->modifiers & cmdKey) != 0)
{
doAdjustMenus();
doMenuChoice(MenuEvent(eventStrucPtr));
}
break;
case updateEvt:
doUpdate(eventStrucPtr);
break;
case osEvt:
switch((eventStrucPtr->message >> 24) & 0x000000FF)
{
case suspendResumeMessage:
gInBackground = (eventStrucPtr->message & resumeFlag) == 0;
break;
}
HiliteMenu(0);
break;
}
}
// doMouseDown
void doMouseDown(EventRecord *eventStrucPtr)
{
WindowPtr windowPtr;
SInt16 partCode;
partCode = FindWindow(eventStrucPtr->where,&windowPtr);
switch(partCode)
{
case inMenuBar:
doAdjustMenus();
doMenuChoice(MenuSelect(eventStrucPtr->where));
break;
case inContent:
if(windowPtr != FrontWindow())
SelectWindow(windowPtr);
break;
case inDrag:
DragWindow(windowPtr,eventStrucPtr->where,&qd.screenBits.bounds);
break;
case inGoAway:
if(TrackGoAway(windowPtr,eventStrucPtr->where) == true)
doCloseCommand();
break;
}
}
// doUpdate
void doUpdate(EventRecord *eventStrucPtr)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
GrafPtr oldPort;
Rect destRect;
windowPtr = (WindowPtr) eventStrucPtr->message;
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
GetPort(&oldPort);
SetPort(windowPtr);
BeginUpdate(windowPtr);
if((*docStrucHdl)->pictureHdl)
{
destRect = (*(*docStrucHdl)->pictureHdl)->picFrame;
OffsetRect(&destRect,170,54);
HLock((Handle) (*docStrucHdl)->pictureHdl);
DrawPicture((*docStrucHdl)->pictureHdl,&destRect);
HUnlock((Handle) (*docStrucHdl)->pictureHdl);
}
else if((*docStrucHdl)->editStrucHdl)
{
HLock((Handle) (*docStrucHdl)->editStrucHdl);
TEUpdate(&gDestRect,(*docStrucHdl)->editStrucHdl);
HUnlock((Handle) (*docStrucHdl)->editStrucHdl);
}
if((*docStrucHdl)->windowTouched)
{
TextSize(48);
MoveTo(30,170);
DrawString("\pWINDOW TOUCHED");
TextSize(12);
}
EndUpdate((WindowPtr)eventStrucPtr->message);
SetPort(oldPort);
}
// doMenuChoice
void doMenuChoice(SInt32 menuChoice)
{
SInt16 menuID, menuItem;
Str255 itemName;
SInt16 daDriverRefNum;
StandardFileReply stdFileReply;
Rect theRect;
Str255 theString, numberString;
menuID = HiWord(menuChoice);
menuItem = LoWord(menuChoice);
if(menuID == 0)
return;
switch(menuID)
{
case mApple:
GetMenuItemText(GetMenuHandle(mApple),menuItem,itemName);
daDriverRefNum = OpenDeskAcc(itemName);
break;
case mFile:
doFileMenu(menuItem);
break;
case mDemonstration:
if(menuItem == iTouchWindow)
doTouchWindow();
else if(menuItem == iSelectDirectoryDialog)
{
stdFileReply = doDirectorySelectionDialog();
if(FrontWindow())
{
SetPort(FrontWindow());
TextSize(10);
SetRect(&theRect,0,271,600,300);
EraseRect(&theRect);
if(gDirectorySelectionFlag)
{
doCopyPString(stdFileReply.sfFile.name,theString);
doConcatPStrings(theString, "\p Volume Reference Number: ");
NumToString((SInt32) stdFileReply.sfFile.vRefNum,numberString);
doConcatPStrings(theString,numberString);
doConcatPStrings(theString, "\p Parent Directory ID: ");
NumToString((SInt32) stdFileReply.sfFile.parID,numberString);
doConcatPStrings(theString,numberString);
MoveTo(10,290);
DrawString(theString);
}
}
}
break;
}
HiliteMenu(0);
}
// doFileMenu
void doFileMenu(SInt16 menuItem)
{
OSErr osError;
switch(menuItem)
{
case iNew:
if(osError = doNewCommand())
doErrorAlert(osError);
break;
case iOpen:
if(osError = doOpenCommand())
doErrorAlert(osError);
break;
case iClose:
if((osError = doCloseCommand()) && osError != kUserCancelled)
doErrorAlert(osError);
break;
case iSave:
if(osError = doSaveCommand())
doErrorAlert(osError);
break;
case iSaveAs:
if(osError = doSaveAsCommand())
doErrorAlert(osError);
break;
case iRevert:
if(osError = doRevertCommand())
doErrorAlert(osError);
break;
case iQuit:
if((osError = doQuitCommand()) && osError != kUserCancelled)
doErrorAlert(osError);
if(osError != kUserCancelled)
gDone = true;
break;
}
}
// doAdjustMenus
void doAdjustMenus(void)
{
MenuHandle menuHdl;
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
windowPtr = FrontWindow();
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
menuHdl = GetMenuHandle(mFile);
if(gCurrentNumberOfWindows > 0)
{
menuHdl = GetMenuHandle(mFile);
EnableItem(menuHdl,iClose);
if((*docStrucHdl)->windowTouched)
{
EnableItem(menuHdl,iSave);
EnableItem(menuHdl,iRevert);
}
else
{
DisableItem(menuHdl,iSave);
DisableItem(menuHdl,iRevert);
}
EnableItem(menuHdl,iSaveAs);
menuHdl = GetMenuHandle(mDemonstration);
if((*docStrucHdl)->windowTouched == false)
EnableItem(menuHdl,iTouchWindow);
else
DisableItem(menuHdl,iTouchWindow);
}
else
{
menuHdl = GetMenuHandle(mFile);
DisableItem(menuHdl,iClose);
DisableItem(menuHdl,iSave);
DisableItem(menuHdl,iSaveAs);
DisableItem(menuHdl,iRevert);
menuHdl = GetMenuHandle(mDemonstration);
DisableItem(menuHdl,iTouchWindow);
}
DrawMenuBar();
}
// doErrorAlert
void doErrorAlert(SInt16 errorCode)
{
AlertStdAlertParamRec paramRec;
Str255 errorString, theString;
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;
if(errorCode == eInstallHandler)
GetIndString(errorString,rErrorStrings,1);
else if(errorCode == eMaxWindows)
GetIndString(errorString,rErrorStrings,2);
else if(errorCode == eFileIsOpen)
GetIndString(errorString,rErrorStrings,3);
else
{
GetIndString(errorString,rErrorStrings,4);
NumToString((SInt32) errorCode,theString);
doConcatPStrings(errorString,theString);
}
if(errorCode != memFullErr)
StandardAlert(kAlertCautionAlert,errorString,NULL,¶mRec,&itemHit);
else
{
StandardAlert(kAlertStopAlert,errorString,NULL,¶mRec,&itemHit);
ExitToShell();
}
}
// doCopyPString
void doCopyPString(Str255 sourceString,Str255 destinationString)
{
SInt16 stringLength;
stringLength = sourceString[0];
BlockMove(sourceString + 1,destinationString + 1,stringLength);
destinationString[0] = stringLength;
}
// 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;
}
}
// doTouchWindow
void doTouchWindow(void)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
windowPtr = FrontWindow();
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
SetPort(windowPtr);
TextSize(48);
MoveTo(30,170);
DrawString("\pWINDOW TOUCHED");
TextSize(12);
(*docStrucHdl)->windowTouched = true;
}
// doOpenAppEvent
pascal OSErr doOpenAppEvent(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefCon)
{
OSErr osError;
osError = doHasGotRequiredParams(appEvent);
if(osError == noErr)
osError = doNewCommand();
return(osError);
}
// doReopenAppEvent
pascal OSErr doReopenAppEvent(AppleEvent *appEvent,AppleEvent *reply,
SInt32 handlerRefCon)
{
OSErr osError;
osError = doHasGotRequiredParams(appEvent);
if(osError == noErr)
if(!FrontWindow())
osError = doNewCommand();
return(osError);
}
// doOpenDocsEvent
pascal OSErr doOpenDocsEvent(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
FSSpec fileSpec;
AEDescList docList;
OSErr osError, ignoreErr;
SInt32 index, numberOfItems;
Size actualSize;
AEKeyword keyWord;
DescType returnedType;
FInfo fileInfo;
osError = AEGetParamDesc(appEvent,keyDirectObject,typeAEList,&docList);
if(osError == noErr)
{
osError = doHasGotRequiredParams(appEvent);
if(osError == noErr)
{
AECountItems(&docList,&numberOfItems);
if(osError == noErr)
{
for(index=1;index<=numberOfItems;index++)
{
osError = AEGetNthPtr(&docList,index,typeFSS,&keyWord,&returnedType,
(Ptr) &fileSpec,sizeof(fileSpec),&actualSize);
if(osError == noErr)
{
osError = FSpGetFInfo(&fileSpec,&fileInfo);
if(osError == noErr)
{
if(osError = doOpenFile(fileSpec,fileInfo.fdType))
doErrorAlert(osError);
}
}
else
doErrorAlert(osError);
}
}
}
else
doErrorAlert(osError);
ignoreErr = AEDisposeDesc(&docList);
}
else
doErrorAlert(osError);
return(osError);
}
// doQuitAppEvent
pascal OSErr doQuitAppEvent(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
OSErr osError;
osError = doHasGotRequiredParams(appEvent);
if(osError == noErr)
{
while(FrontWindow())
{
osError = doCloseCommand();
if(osError != noErr && osError != kUserCancelled)
doErrorAlert(osError);
if(osError == kUserCancelled)
return;
}
}
gDone = true;
return(osError);
}
// doHasGotRequiredParams
OSErr doHasGotRequiredParams(AppleEvent *appEvent)
{
DescType returnedType;
Size actualSize;
OSErr osError;
osError = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,
NULL,0,&actualSize);
if(osError == errAEDescNotFound)
return(noErr);
else if(osError == noErr)
return(errAEParamMissed);
}
//
// NewOpenCloseSave.c
//
// ............................................................................. includes
#include "Files1.h"
// ..................................................................... global variables
WindowPtr gWindowPtr;
SInt16 gCurrentNumberOfWindows = 0;
Rect gDestRect,gViewRect;
SFTypeList gFileTypes;
extern SInt16 gAppResFileRefNum;
// doNewCommand
OSErr doNewCommand(void)
{
OSErr osError;
OSType documentType = 'TEXT';
osError = doNewDocWindow(true,documentType);
return(osError);
}
// doOpenCommand
OSErr doOpenCommand(void)
{
StandardFileReply fileReply;
OSType documentType;
OSErr osError = noErr;
FileFilterYDUPP filterFunctionOpenDialogUPP;
DlgHookYDUPP hookFunctionOpenDialogUPP;
Point dialogLocation;
filterFunctionOpenDialogUPP = NewFileFilterYDProc((ProcPtr) filterFunctionOpenDialog);
hookFunctionOpenDialogUPP = NewDlgHookYDProc((ProcPtr) hookFunctionOpenDialog);
gFileTypes[0] = 'TEXT';
gFileTypes[1] = 'PICT';
dialogLocation.v = -1;
dialogLocation.h = -1;
CustomGetFile(filterFunctionOpenDialogUPP,2,gFileTypes,&fileReply,rCustomOpenDialog,
dialogLocation,hookFunctionOpenDialogUPP,NULL,NULL,NULL,NULL);
DisposeRoutineDescriptor(filterFunctionOpenDialogUPP);
DisposeRoutineDescriptor(hookFunctionOpenDialogUPP);
documentType = fileReply.sfType;
if(fileReply.sfGood)
osError = doOpenFile(fileReply.sfFile,documentType);
return(osError);
}
// doCloseCommand
OSErr doCloseCommand(void)
{
WindowPtr windowPtr;
SInt16 windowKind;
docStructureHandle docStrucHdl;
OSErr osError = noErr;
windowPtr = FrontWindow();
windowKind = ((WindowPeek) windowPtr)->windowKind;
switch(windowKind)
{
case kApplicationWindowKind:
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
osError = doCloseFile(windowPtr,docStrucHdl);
if(osError == kUserCancelled)
return(kUserCancelled);
else if(osError == noErr)
{
DisposeWindow(windowPtr);
gCurrentNumberOfWindows --;
}
break;
case kDialogWindowKind:
// Hide or close modeless dialog, as required.
break;
}
return(osError);
}
// doSaveCommand
OSErr doSaveCommand(void)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
OSErr osError = noErr;
windowPtr = FrontWindow();
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
if((*docStrucHdl)->fileRefNum)
{
osError = doWriteFile(windowPtr);
SetPort(windowPtr);
EraseRect(&windowPtr->portRect);
InvalRect(&windowPtr->portRect);
}
else
osError = doSaveAsCommand();
return(osError);
}
// doSaveAsCommand
OSErr doSaveAsCommand(void)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
StandardFileReply fileReply;
OSType fileType;
SInt16 fileRefNum;
OSErr osError = noErr;
windowPtr = FrontWindow();
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
StandardPutFile("\pSave as:","\pUntitled",&fileReply);
if(fileReply.sfGood)
{
if(!(fileReply.sfReplacing))
{
if((*docStrucHdl)->editStrucHdl)
fileType = 'TEXT';
else if((*docStrucHdl)->pictureHdl)
fileType = 'PICT';
osError = FSpCreate(&fileReply.sfFile,'KKKB',fileType,smSystemScript);
if(osError != noErr)
return(osError);
}
(*docStrucHdl)->fileFSSpec = fileReply.sfFile;
if((*docStrucHdl)->fileRefNum != 0)
{
osError = FSClose((*docStrucHdl)->fileRefNum);
(*docStrucHdl)->fileRefNum = 0;
}
if(osError == noErr)
osError = FSpOpenDF(&(*docStrucHdl)->fileFSSpec,fsRdWrPerm,&fileRefNum);
if(osError == noErr)
{
(*docStrucHdl)->fileRefNum = fileRefNum;
SetWTitle(windowPtr,fileReply.sfFile.name);
osError = doWriteFile(windowPtr);
}
}
SetPort(windowPtr);
EraseRect(&windowPtr->portRect);
InvalRect(&windowPtr->portRect);
return(osError);
}
// doRevertCommand
OSErr doRevertCommand(void)
{
WindowPtr windowPtr;
docStructureHandle docStrucHdl;
Str255 fileName;
SInt16 itemHit;
OSErr osError = noErr;
windowPtr = FrontWindow();
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
SetPort(windowPtr);
GetWTitle(windowPtr,fileName);
ParamText(fileName,NULL,NULL,NULL);
SetPort(windowPtr);
itemHit = CautionAlert(rRevertAlert,NULL);
if(itemHit == 1)
{
EraseRect(&windowPtr->portRect);
if((*docStrucHdl)->editStrucHdl)
osError = doReadTextFile(windowPtr);
else if((*docStrucHdl)->pictureHdl)
{
KillPicture((*docStrucHdl)->pictureHdl);
(*docStrucHdl)->pictureHdl = NULL;
osError = doReadPictFile(windowPtr);
}
(*docStrucHdl)->windowTouched = false;
InvalRect(&windowPtr->portRect);
}
return(osError);
}
// doQuitCommand
OSErr doQuitCommand(void)
{
OSErr osError = noErr;
while(FrontWindow())
{
osError = doCloseCommand();
if(osError != noErr)
return(osError);
}
return(osError);
}
// doNewDocWindow
OSErr doNewDocWindow(Boolean showWindow,OSType documentType)
{
docStructureHandle docStrucHdl;
if(gCurrentNumberOfWindows == kMaxWindows)
return(eMaxWindows);
if(!(gWindowPtr = GetNewCWindow(rNewWindow,NULL,(WindowPtr)-1)))
return(MemError());
SetPort(gWindowPtr);
if(!(docStrucHdl = (docStructureHandle) NewHandle(sizeof(docStructure))))
{
DisposeWindow(gWindowPtr);
gCurrentNumberOfWindows --;
return(MemError());
}
SetWRefCon(gWindowPtr,(SInt32) docStrucHdl);
(*docStrucHdl)->editStrucHdl = NULL;
(*docStrucHdl)->pictureHdl = NULL;
(*docStrucHdl)->fileRefNum = 0;
(*docStrucHdl)->windowTouched = false;
if(documentType == 'TEXT')
{
gDestRect = gWindowPtr->portRect;
InsetRect(&gDestRect,6,6);
gViewRect = gDestRect;
MoveHHi((Handle) docStrucHdl);
HLock((Handle) docStrucHdl);
if(!((*docStrucHdl)->editStrucHdl = TENew(&gDestRect,&gViewRect)))
{
DisposeWindow(gWindowPtr);
gCurrentNumberOfWindows --;
DisposeHandle((Handle) docStrucHdl);
return(MemError());
}
HUnlock((Handle) docStrucHdl);
}
if(showWindow)
ShowWindow(gWindowPtr);
gCurrentNumberOfWindows ++;
return(noErr);
}
// doOpenFile
OSErr doOpenFile(FSSpec fileSpec,OSType documentType)
{
OSErr osError;
SInt16 fileRefNum;
docStructureHandle docStrucHdl;
if(osError = doNewDocWindow(false,documentType))
return(osError);
SetWTitle(gWindowPtr,fileSpec.name);
if(osError = FSpOpenDF(&fileSpec,fsRdWrPerm,&fileRefNum))
{
DisposeWindow(gWindowPtr);
gCurrentNumberOfWindows --;
return(osError);
}
docStrucHdl = (docStructureHandle) GetWRefCon(gWindowPtr);
(*docStrucHdl)->fileRefNum = fileRefNum;
(*docStrucHdl)->fileFSSpec = fileSpec;
if(documentType == 'TEXT')
{
if(osError = doReadTextFile(gWindowPtr))
return(osError);
}
else if(documentType == 'PICT')
{
if(osError = doReadPictFile(gWindowPtr))
return(osError);
}
ShowWindow(gWindowPtr);
return(noErr);
}
// doCloseFile
OSErr doCloseFile(WindowPtr windowPtr,docStructureHandle docStrucHdl)
{
Str255 fileName;
SInt16 itemHit;
OSErr osError;
if((*docStrucHdl)->windowTouched)
{
GetWTitle(windowPtr,fileName);
ParamText(fileName,NULL,NULL,NULL);
itemHit = CautionAlert(rCloseFileAlert,NULL);
if(itemHit == 2)
return(kUserCancelled);
else if(itemHit == 1)
{
if(osError = doSaveCommand())
return(osError);
}
}
if((*docStrucHdl)->fileRefNum != 0)
{
if(!(osError = FSClose((*docStrucHdl)->fileRefNum)))
{
osError = FlushVol(NULL,(*docStrucHdl)->fileFSSpec.vRefNum);
(*docStrucHdl)->fileRefNum = 0;
}
}
if((*docStrucHdl)->editStrucHdl)
TEDispose((*docStrucHdl)->editStrucHdl);
if((*docStrucHdl)->pictureHdl)
KillPicture((*docStrucHdl)->pictureHdl);
DisposeHandle((Handle) docStrucHdl);
return(osError);
}
// doWriteFile
OSErr doWriteFile(WindowPtr windowPtr)
{
docStructureHandle docStrucHdl;
FSSpec fileSpecActual, fileSpecTemp;
UInt32 currentTime;
Str255 tempFileName;
SInt16 tempFileVolNum, tempFileRefNum;
SInt32 tempFileDirID;
OSErr osError;
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
fileSpecActual = (*docStrucHdl)->fileFSSpec;
GetDateTime(¤tTime);
NumToString((SInt32) currentTime,tempFileName);
osError = FindFolder(fileSpecActual.vRefNum,kTemporaryFolderType,kCreateFolder,
&tempFileVolNum,&tempFileDirID);
if(osError == noErr)
osError = FSMakeFSSpec(tempFileVolNum,tempFileDirID,tempFileName,&fileSpecTemp);
if(osError == noErr || osError == fnfErr)
osError = FSpCreate(&fileSpecTemp,'trsh','trsh',smSystemScript);
if(osError == noErr)
osError = FSpOpenDF(&fileSpecTemp,fsRdWrPerm,&tempFileRefNum);
if(osError == noErr)
{
if((*docStrucHdl)->editStrucHdl)
osError = doWriteTextData(windowPtr,tempFileRefNum);
else if((*docStrucHdl)->pictureHdl)
osError = doWritePictData(windowPtr,tempFileRefNum);
}
if(osError == noErr)
osError = FSClose(tempFileRefNum);
if(osError == noErr)
osError = FSClose((*docStrucHdl)->fileRefNum);
if(osError == noErr)
osError = FSpExchangeFiles(&fileSpecTemp,&fileSpecActual);
if(osError == noErr)
osError = FSpDelete(&fileSpecTemp);
if(osError == noErr)
osError = FSpOpenDF(&fileSpecActual,fsRdWrPerm,&(*docStrucHdl)->fileRefNum);
if(osError == noErr)
osError = doCopyAppNameResource(windowPtr);
return(osError);
}
// doReadTextFile
OSErr doReadTextFile(WindowPtr windowPtr)
{
docStructureHandle docStrucHdl;
SInt16 fileRefNum;
TEHandle textEditHdl;
SInt32 numberOfBytes;
Handle textBuffer;
OSErr osError;
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
fileRefNum = (*docStrucHdl)->fileRefNum;
textEditHdl = (*docStrucHdl)->editStrucHdl;
(*textEditHdl)->txSize = 10;
(*textEditHdl)->lineHeight = 15;
SetFPos(fileRefNum,fsFromStart,0);
GetEOF(fileRefNum,&numberOfBytes);
if(numberOfBytes > 32767)
numberOfBytes = 32767;
if(!(textBuffer = NewHandle((Size) numberOfBytes)))
return(MemError());
osError = FSRead(fileRefNum,&numberOfBytes,*textBuffer);
if(osError == noErr || osError == eofErr)
{
MoveHHi(textBuffer);
HLockHi(textBuffer);
TESetText(*textBuffer,numberOfBytes,(*docStrucHdl)->editStrucHdl);
HUnlock(textBuffer);
DisposeHandle(textBuffer);
}
else
return(osError);
return(noErr);
}
// doReadPictFile
OSErr doReadPictFile(WindowPtr windowPtr)
{
docStructureHandle docStrucHdl;
SInt16 fileRefNum;
SInt32 numberOfBytes;
OSErr osError;
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
fileRefNum = (*docStrucHdl)->fileRefNum;
GetEOF(fileRefNum,&numberOfBytes);
SetFPos(fileRefNum,fsFromStart,512);
numberOfBytes -= 512;
if(!((*docStrucHdl)->pictureHdl = (PicHandle) NewHandle(numberOfBytes)))
return(MemError());
osError = FSRead(fileRefNum,&numberOfBytes,*(*docStrucHdl)->pictureHdl);
if(osError == noErr || osError == eofErr)
return(noErr);
else
return(osError);
}
// doWriteTextData
OSErr doWriteTextData(WindowPtr windowPtr,SInt16 tempFileRefNum)
{
docStructureHandle docStrucHdl;
TEHandle textEditHdl;
Handle editText;
SInt32 numberOfBytes;
SInt16 volRefNum;
OSErr osError;
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
textEditHdl = (*docStrucHdl)->editStrucHdl;
editText = (*textEditHdl)->hText;
numberOfBytes = (*textEditHdl)->teLength;
osError = SetFPos(tempFileRefNum,fsFromStart,0);
if(osError == noErr)
osError = FSWrite(tempFileRefNum,&numberOfBytes,*editText);
if(osError == noErr)
osError = SetEOF(tempFileRefNum,numberOfBytes);
if(osError == noErr)
osError = GetVRefNum(tempFileRefNum,&volRefNum);
if(osError == noErr)
osError = FlushVol(NULL,volRefNum);
if(osError == noErr)
(*docStrucHdl)->windowTouched = false;
return(osError);
}
// doWritePictData
OSErr doWritePictData(WindowPtr windowPtr,SInt16 tempFileRefNum)
{
docStructureHandle docStrucHdl;
PicHandle pictureHdl;
SInt32 numberOfBytes, dummyData;
SInt16 volRefNum;
OSErr osError;
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
pictureHdl = (*docStrucHdl)->pictureHdl;
numberOfBytes = 512;
dummyData = 0;
osError = SetFPos(tempFileRefNum,fsFromStart,0);
if(osError == noErr)
osError = FSWrite(tempFileRefNum,&numberOfBytes,&dummyData);
numberOfBytes = GetHandleSize((Handle) (*docStrucHdl)->pictureHdl);
if(osError == noErr)
{
HLock((Handle) (*docStrucHdl)->pictureHdl);
osError = FSWrite(tempFileRefNum,&numberOfBytes,*(*docStrucHdl)->pictureHdl);
HUnlock((Handle) (*docStrucHdl)->pictureHdl);
}
if(osError == noErr)
osError = SetEOF(tempFileRefNum,512 + numberOfBytes);
if(osError == noErr)
osError = GetVRefNum(tempFileRefNum,&volRefNum);
if(osError == noErr)
osError = FlushVol(NULL,volRefNum);
if(osError == noErr)
(*docStrucHdl)->windowTouched = false;
return(osError);
}
// doCopyAppNameResource
OSErr doCopyAppNameResource(WindowPtr windowPtr)
{
docStructureHandle docStrucHdl;
OSType fileType;
OSErr osError;
SInt16 fileRefNum;
docStrucHdl = (docStructureHandle) GetWRefCon(windowPtr);
if((*docStrucHdl)->editStrucHdl)
fileType = 'TEXT';
else if((*docStrucHdl)->pictureHdl)
fileType = 'PICT';
FSpCreateResFile(&(*docStrucHdl)->fileFSSpec,'KKKB',fileType,smSystemScript);
osError = ResError();
if(osError == noErr)
fileRefNum = FSpOpenResFile(&(*docStrucHdl)->fileFSSpec,fsRdWrPerm);
if(fileRefNum > 0)
osError = doCopyResource('STR ',-16396,gAppResFileRefNum,fileRefNum);
else
osError = ResError();
if(osError == noErr)
CloseResFile(fileRefNum);
osError = ResError();
return(osError);
}
// doCopyResource
OSErr doCopyResource(ResType resourceType,SInt16 resourceID,SInt16 sourceFileRefNum,
SInt16 destFileRefNum)
{
Handle sourceResourceHdl;
Str255 sourceResourceName;
ResType ignoredType;
SInt16 ignoredID;
UseResFile(sourceFileRefNum);
sourceResourceHdl = GetResource(resourceType,resourceID);
if(sourceResourceHdl != NULL)
{
GetResInfo(sourceResourceHdl,&ignoredID,&ignoredType,sourceResourceName);
DetachResource(sourceResourceHdl);
UseResFile(destFileRefNum);
AddResource(sourceResourceHdl,resourceType,resourceID,sourceResourceName);
if(ResError() == noErr)
UpdateResFile(destFileRefNum);
}
ReleaseResource(sourceResourceHdl);
return(ResError());
}
//
// FiltersAndHooks.c
//
// ............................................................................. includes
#include "Files1.h"
// ..................................................................... global variables
SInt16 gCurrentType = 1;
Str255 gPrevSelectedName;
Boolean gDirectorySelectionFlag;
extern SFTypeList gFileTypes;
// filterFunctionOpenDialog
pascal Boolean filterFunctionOpenDialog(CInfoPBPtr pbPtr,void *dataPtr)
{
if(pbPtr->hFileInfo.ioFlFndrInfo.fdType == gFileTypes[gCurrentType - 1])
return false;
else
return true;
}
// hookFunctionOpenDialog
pascal SInt16 hookFunctionOpenDialog(SInt16 item,DialogPtr theDialog,void *dataPtr)
{
SInt16 theType;
Handle controlHdl;
Rect theRect;
switch(item)
{
case sfHookFirstCall:
GetDialogItem(theDialog,iPopupItem,&theType,&controlHdl,&theRect);
SetControlValue((ControlHandle) controlHdl,gCurrentType);
return sfHookNullEvent;
break;
case iPopupItem:
GetDialogItem(theDialog,iPopupItem,&theType,&controlHdl,&theRect);
theType = GetControlValue((ControlHandle) controlHdl);
if(theType != gCurrentType)
{
gCurrentType = theType;
return sfHookRebuildList;
}
break;
}
return item;
}
// doDirectorySelectionDialog
StandardFileReply doDirectorySelectionDialog(void)
{
StandardFileReply stdFileReplyStruct;
SFTypeList fileTypes;
Point dialogLocation;
FileFilterYDUPP filterFunctionDirSelectUPP;
DlgHookYDUPP hookFunctionDirSelectUPP;
filterFunctionDirSelectUPP = NewFileFilterYDProc((ProcPtr) filterFunctionDirSelect);
hookFunctionDirSelectUPP = NewDlgHookYDProc((ProcPtr) hookFunctionDirSelect);
gPrevSelectedName[0] = 0;
gDirectorySelectionFlag = true;
dialogLocation.v = -1;
dialogLocation.h = -1;
CustomGetFile(filterFunctionDirSelectUPP,-1,fileTypes,&stdFileReplyStruct,
rSelectDirectoryDialog,dialogLocation,hookFunctionDirSelectUPP,NULL,NULL,
NULL,&stdFileReplyStruct);
DisposeRoutineDescriptor(filterFunctionDirSelectUPP);
DisposeRoutineDescriptor(hookFunctionDirSelectUPP);
return stdFileReplyStruct;
}
// filterFunctionDirSelect
pascal Boolean filterFunctionDirSelect(CInfoPBPtr pbPtr,void *dataPtr)
{
SInt32 attributes;
Boolean result;
attributes = (SInt32) pbPtr->hFileInfo.ioFlAttrib;
result = !(BitTst(&attributes,31 - 4));
return result;
}
// hookFunctionDirSelect
pascal SInt16 hookFunctionDirSelect(SInt16 item,DialogPtr theDialog,void *dataPtr)
{
SInt16 itemType, width;
Handle itemHdl;
Rect itemRect;
Str255 theName, theString = "\pSelect '";
standardFileReplyPtr stdFileReplyPtr;
stdFileReplyPtr = (standardFileReplyPtr) dataPtr;
if(stdFileReplyPtr->sfIsFolder || stdFileReplyPtr->sfIsVolume)
{
doCopyPString(stdFileReplyPtr->sfFile.name,theName);
if(IdenticalString(theName,gPrevSelectedName,NULL) != 0)
{
doCopyPString(theName,gPrevSelectedName);
GetDialogItem(theDialog,iSelectButton,&itemType,&itemHdl,&itemRect);
width = (itemRect.right - itemRect.left) - StringWidth("\pSelect ' ");
TruncString(width,theName,smTruncMiddle);
doConcatPStrings(theString,theName);
doConcatPStrings(theString,"\p'");
SetControlTitle((ControlHandle) itemHdl,theString);
}
}
if(item == iSelectButton)
return sfItemCancelButton;
else if(item == sfItemCancelButton)
gDirectorySelectionFlag = false;
return item;
}
//
Demonstration Program Comments
When the program is run, the user should:
* Exercise the File menu by opening the supplied TEXT and PICT files, saving those
files, saving those files under new names, closing files, opening the new files,
attempting to open files which are already open, attempting to save files to new
files with existing names, making open windows "touched" by choosing the first item
in the Demonstration menu item, reverting to the saved versions of files associated
with "touched" windows, choosing Quit when "touched" and non-"touched" windows are
open, and so on.
* Choose, via the File types pop-up menu button, the file types required to be
displayed in the customised Open dialog.
* Choose the Directory Selection Dialog item from the Demonstration menu to display
the directory selection dialog (a customised Open dialog), and select a directory
using the Select button at the bottom of the dialog. (The name of the selected
directory will be drawn in the bottom-left corner of the front window.)
The program may be run from within CodeWarrior to demonstrate responses to the File menu
commands and the directory selection dialog.
The built application, together with the supplied TEXT and PICT files, may be used to
demonstrate the additional aspect of integrating the receipt of required Apple events
with the overall file handling mechanism. To prove the correct handling of the required
Apple events, the user should:
* Open the application by double-clicking the application icon, noting that a new
document window is opened after the application is launched and the Open Application
event is received.
* Double click on a document icon, or select one or more document icons and either
drag those icons to the application icon or choose Open from the Finder's File menu,
noting that the application is launched and the selected files are opened when the
Open Documents event is received.
* Double click on a document icon, or select one or more document icons and either
drag those icons to the application icon or choose Open from the Finder's File menu,
noting that the application is launched and the selected files are opened when the
Open Documents event is received.
* With several documents open, some with "touched" windows, choose Restart or Shut
Down from the Finder's Special menu (thus invoking a Quit Application event), noting
that, for "touched" windows, an alert box is presented asking the user whether the
file should be saved before the shutdown process proceeds.
Files1.h
#typedef
Each window created by the program will have an associated document structure, accessed
via the window structure's refCon field. The docStructure structure will be used for
document structures.
The editRec field will be assigned a handle to a TextEdit edit structure ('TEXT' files).
The pictureHdl field will be assigned a handle to a Picture structure ('PICT' files).
The fileRefNum and fileFSSpec fields will be assigned the file reference number and the
file system specification structure of the file associated with the window. The
windowTouched field will be set to true when a window has been made "touched", that is,
when the associated document in memory has been modified by the user.
The second type defined will be used in the dialog hook function for the directory
selection dialog.
#define
After the usual constants relating to menus, windows, and alert boxes are established,
additional constants are established for the customised Open dialog's 'DLOG' resource and
its additional item, the directory selection dialog's 'DLOG' resource and its additional
item, a 'STR#' resource containing error strings, and three specific error conditions.
kMaxWindows is used to limit the number of windows the user can open. kUserCancelled is
used when the user clicks the Cancel button of a particular alert box
Files1.c
Files1.c is simply the basic "engine" which supports the demonstration. There is little
in this file which has not featured in previous demonstration programs.
Global Variables
gDone controls termination of the main loop and thus of the program. gInBackground
relates to foreground/background switching. The next three globals will be assigned
universal procedure pointers to the required Apple event handlers. gAppResFileRefNum
will be assigned the file reference number of the application's resource fork.
main
Within the main function, routine descriptors for the required Apple events (less the
Print Documents event) are created and a call is made to the application-defined function
which installs the handlers. Also, the file reference number of the application's
resource fork (which is opened automatically at application launch) is assigned to the
global variable gAppResFileRefNum.
doInstallAEHandlers
doInstallAEHandlers installs handlers for the Open Application, Open Documents, and Quit
Application events. (Note that, so as to avoid the necessity to include
application-defined printing functions in this program, a handler for the Print Documents
event is not included in this demonstration.)
doUpdate
doUpdate performs such window updating as is necessary for the satisfactory execution of
the demonstration aspects of the program.
doMenuChoice
If the second item in the Demonstration menu is chosen, the application-defined function
which presents the directory selection dialog is called. This function returns a
standard file reply structure. If a window is open, a rectangle in the bottom corner of
the front window is erased. If the dialog was dismissed using the Select button (not the
Cancel button), the selected directory name, volume reference number, and parent
directory ID are extracted from the standard file reply structure and drawn in the bottom
of the window.
doFileMenu
doFileMenu handles File menu choices. In each case, the relevant application-defined
function is called and, if that function returns an error, the application-defined
function doErrorAlert is called. Note that, in the case of the Quit command, gDone is
set to true after doQuitCommand returns, thus causing the program to terminate.
doErrorAlert
doErrorAlert handles errors, invoking an appropriate alert box (caution or stop) advising
of the nature of the problem by error code number or straight text. Note that he program
will only be terminated in the case of the memFullErr error (no more space in the
application heap).
doTouchWindow
doTouchWindow is called when the user chooses the Touch Window item in the Demonstration
menu. Changing the content of the in-memory version of a file is only simulated in this
program. The text "WINDOW TOUCHED" is drawn in window and the windowTouched field of the
document structure is set to true.
doOpenAppEvent, doOpenDocsEvent, and doQuitAppEvent
The handlers for the required Apple events are essentially identical to those in the
demonstration program at Chapter 10 - Required Apple Events.
Most programs should simply open a new untitled window on receipt of an Open Application
event. Accordingly, doOpenAppEvent simply calls the same function (doNewCommand) as is
called when the user chooses New from the File menu.
On receipt of a Re-Open Application event, if no windows are currently open, doNewCommand
is called to open a window.
The demonstration program supports both 'TEXT' and 'PICT' files. On receipt of an Open
Application event, it is thus necessary to determine the type of each file specified in
the event. Accordingly, within doOpenDocsEvent, the call to FSpGetFInfo returns the
Finder information from the volume catalog entry for the file relating to the specified
FSSpec structure. The fdType field of the FInfo structure "filled-in" by FSpGetFInfo
contains the file type. This, together with the FSSpec structure, is then passed in the
call to doOpenFile. (doOpenFile is also called when the user chooses Open from the File
menu.)
Within the function doQuitAppEvent, the while loop entered at repeats for each open
window. Within the loop, doCloseCommand is called. doCloseCommand, in turn, calls
doCloseFile. doCloseFile presents a Yes/No/Cancel caution alert. If an error is
returned by this sequence, and if the user did not click the Cancel button in the alert,
the error handler is called. If the user clicked the Cancel button, it is necessary to
interrupt the sequence of closing all open windows and re-enter the main event loop.
When the while loop eventually exits, gDone is set to true, causing the program to
terminate.
NewOpenCloseSave.c
Global Variables
gWindowPtr is assigned the pointer to the graphics port of each new window as it is
opened. gCurrentNumberofWindows keeps a count of the number of windows opened. gDestRect
and gViewRect are used to set the destination and view rectangles for the edit structures
associated with 'TEXT' files. gFileTypes will control the file types to be displayed in
the Open dialog box.
doNewCommand
doNewCommand is the first of the file-handling functions. It is called when the user
chooses New from the File menu and when an Open Application event is received.
Since this demonstration does not support the actual entry of text or the drawing of
graphics, the document type passed to doNewDocWindow immaterial. The document type
'TEXT' is passed in this instance simply to keep doNewDocWindow happy.
doOpenCommand
doOpenCommand is called when the user chooses Open from the File menu.
The first two lines create routine descriptors for the file filter and dialog hook
functions utilised by the customised Open dialog box. At the next two lines, the first
two elements of the gFileTypes are assigned the file types to be displayed. -1 is then
assigned to both fields of dialogLocation.
The call to CustomGetFile presents the customised Open dialog box. The first parameter
is a UPP to an application-defined file filter function. As will be seen, this filter
function will either allow or block the display of one or other of the two file types
specified in the first two elements of gFileTypes. The sixth parameter ordinarily
specifies the location of the top-left of the dialog box on the screen; however, the
assignment of -1 to both fields of dialogLocation will cause CustomGetFile to centre the
dialog box on the screen. Note also that the next parameter is a UPP to the
application-defined dialog hook function which handles user interaction with the dialog.
When the dialog box is dismissed, the routine descriptors are disposed of.
The sfType field of the StandardFileReply structure "filled-in" by CustomGetFile
(fileReply) contains the file type of the file selected by the user and the sfFile field
contains the file system specification. If the user clicks the OK button (the third last
line), these are passed to the application-defined function doOpenFile.
To use the standard Open dialog, delete the UPP and Point variables and their
associated code, make gFilesType a local variable, and replace the call to
CustomGetFile with:
StandardGetFile(NULL,2,gFileTypes,&fileReply); |
doCloseCommand
doCloseCommand is called when the user chooses Close from the File menu or clicks in the
window's go-away box. It is also called successively for each open window when a Quit
Application event is received.
The first two lines get the WindowPtr for the front window and establish whether the
front window is a document window or a modeless dialog box.
If the front window is a document window, the handle to the window's document structure
is retrieved from the window structure's refCon field. The WindowPtr and this handle are
then passed to the application-defined function doCloseFile. If the window is "touched",
doCloseFile presents an alert box asking the user whether the document should be saved
before it is closed. If the user clicks the Cancel button of that alert box, doCloseFile
returns kUserCancelled, in which case doCloseCommand returns kUserCancelled. If the user
clicks either the Yes or No buttons of the alert box, and if doCloseFile returns no
error, the window is closed as the final act in closing the file, and the global variable
which keeps track of the number of open windows is decremented.
No modeless dialog boxes are used by this program. However, if the front window was a
modeless dialog box, the appropriate action would be taken at the second case.
doSaveCommand
doSaveCommand is called when the user chooses Save from the File menu. It may also be
called by doCloseFile if the user is attempting to close a "touched" window.
Th first two lines get the WindowPtr for the front window and retrieve the handle to
that window's document structure. If a file currently exists for the document in this
window, the application-defined function doWriteFile is called, otherwise the
application-defined function doSaveAsCommand is called.
doSaveAsCommand
doSaveAsCommand is called when the user chooses Save As... from the File menu. It is
also called by doSaveCommand if the user chooses Save when the front window contains a
document for which no file currently exists.
The first two lines get the WindowPtr for the front window and retrieve the handle to
that window's document structure.
The call to StandardPutFile presents the Save dialog box. The remaining code executes
only if the user clicks on the Save button.
If the sfReplacing field of the StandardFileReply structure "filled-in" by
StandardPutFile indicates that an existing file is not being replaced, the file type is
retrieved from the document structure for the front window and FSpCreate is called to
create a new file of that type, specifying the application's signature as the creator.
The file system specification structure returned in the sfFile field of the
StandardFileReply structure tis then assigned to the fileFSSpec field of the document
structure.
If a file currently exists for the document, that file is closed by the call to FSClose.
The data fork of the newly created file is then opened by a call to FSpOpenDF, the
fileRefNum field of the document structure is assigned the file reference number returned
by FSpOpenDF, the window's title is set to the new file's name, and the
application-defined function doWriteFile is called to write the document to the new file.
doRevertCommand
doRevertCommand is called when the user chooses Revert to Saved from the File menu.
The first three lines get the WindowPtr for the front window, retrieve the handle to that
window's document structure, and retrieve the file reference number from the document
structure.
The call to GetWTitle gets the window's title (that is, the filename) for insertion by
ParamText into the text of the alert box invoked by the call to CautionAlert. (The alert
box asks the user to confirm, or otherwise, the reversion to the last saved version.)
If the user clicks the OK button, the window's content area is erased and the appropriate
application-defined function (doReadTextFile or doReadPictFile) is called depending on
whether the file type is 'TEXT' or 'PICT'. In addition, the window's "touched" field in
the document structure is set to false and InvalRect is called to force a redraw of the
window's content region.
doQuitCommand
doQuitCommand is called when the user chooses Quit from the File menu and when a Quit
Application event is received.
The while loop continues to execute until no more windows remain open. On each pass
through the loop, doCloseCommand is called to manage the process of closing (and, where
necessary, saving) all documents and disposing of the associated windows.
doNewDocWindow
doNewDocWindow is called by doNewCommand, doOpenFile and the Open Application event
handler. It creates a new window and associated document structure.
If the current number of open windows is the maximum allowable by this program, the
function immediately exits, passing an error code which will cause an advisory error
alert box to be displayed.
The call to GetNewCWindow opens a new window. SetPort sets that window's graphics port
as the current port for drawing.
The call to NewHandle allocates memory for the window's document structure. If this call
is not successful, the window is disposed of and the function returns with the error code
returned by MemError.
The call to SetWRefCon assigns the handle to the document structure to the window
structure's refCon field. The next four lines initialise fields of the document
structure.
If the document type is 'TEXT', the if block executes, creating a TextEdit edit structure
and assigning a handle to that structure to the editRec field of the document structure.
(Note that the processes here are not explained in detail because TextEdit and edit
structures are not central to the demonstration. For the purposes of the demonstration,
it is sufficient to understand that the text data retrieved from, and saved to, disk is
stored in a TextEdit edit structure. TextEdit is addressed in detail at Chapter 19 -
Text and TextEdit.)
If the Boolean value passed to doNewDocWindow was set to true, the call to ShowWindow
makes the window visible, otherwise the window is left invisible. The penultimate line
increments the global variable which keeps track of the number of open windows.
doOpenFile
doOpenFile is called by doOpenCommand and the Open Documents event handler, which pass to
it the file system specification structure and document type. doOpenFile opens a new
document window and calls the application-defined functions which read in the file.
The call to doNewDocWindow opens a new window and creates an associated document
structure. SetWTitle sets the window's title. FSpOpenDF opens the file's data fork. If
this call is not successful, the window is disposed of and the function returns. The
next three lines assign the file reference number and file system specification structure
to the relevant fields of the document structure.
The next block calls the appropriate function for reading in the file, depending on
whether the file type is of type 'TEXT' or 'PICT'. If the file is read in successfully,
ShowWindow makes the window visible.
doCloseFile
doCloseFile is called by doCloseCommand. doCloseFile does not allow a "touched" window
to be closed without offering the user the option of first saving the associated document
to file.
If the window is touched, a caution alert is presented asking the user whether the
document should be saved. (GetWTitle and ParamText insert the window title into the text
in the alert box.) The alert box contains Yes, No and Cancel buttons. If the user
clicks Cancel, the function returns kUserCancelled. If the user clicks Yes, the
application-defined function doSaveCommand() is called to save the file.
If the user clicks Yes or No, the next block executes:
* If the document has a file, FSClose closes the file, and FlushVol stores to disk all
unwritten data currently in the volume buffer.
* If the document is a text document, the text edit structure is disposed of. If it
is a picture document, the Picture structure is disposed of. Finally, the document
structure is disposed of.
doWriteFile
doWriteFile is called by doSaveCommand and doSaveAsCommand. In conjunction with two
supporting application-defined functions, it writes the document to disk using the
"safe-save" procedure.
The first two lines retrieve a handle to the document structure and the file system
specification from the document structure.
The next two lines create a temporary file name which is bound to be unique. FindFolder
finds the temporary folder on the file's volume, or creates a temporary folder if
necessary. FSMakeFSSpec makes a file system specification structure for the temporary
file, using the volume reference number and parent directory ID returned by the
FindFolder call. FSpCreate creates the temporary file in that directory on that volume,
and FSpOpenDF opens the file's data fork.
Within the next if block, the appropriate application-defined function is called to write
the document's data to the temporary file.
The two calls to FSClose close both the temporary and existing files prior to the call to
FSpExchangeFiles, which swaps the files' data by changing the information in the volume's
catalog. The temporary file is then deleted and the data fork of the existing file is
re-opened.
The application-defined function doCopyAppNameResource is called to copy the missing
application name string resource from the resource fork of the application file to the
resource fork of the new document file.
doReadTextFile
doReadTextFile is called by doOpenFile and doRevertCommand to read in data from an open
file of type 'TEXT'.
The first two lines retrieve the file reference number from the document structure.
The next three lines retrieve the handle to the TextEdit edit structure from the document
structure and modify the text size and line height fields of the edit structure.
SetFPos sets the file mark to the beginning of the file. GetEOF gets the number of bytes
in the file. If the number of bytes exceeds that which can be stored in a TextEdit edit
structure (32,767), the number of bytes which will be read from the file is restricted to
32,767.
NewHandle allocates a buffer equal to the size of the file (or 32,767 bytes if the
preceding if statement executed). FSRead reads the data from the file into the buffer.
MoveHHi and HLockHi move the buffer high in the heap and lock it preparatory to the call
to TESetText. TESetText copies the text in the buffer into the existing hText handle of
the TextEdit edit structure. The buffer is then unlocked and disposed of.
(Note: TextEdit is addressed in detail at Chapter 19 - Text and TextEdit.)
doReadPictFile
doReadPictFile is called by doOpenFile and doRevertCommand to read in data from an open
file of type 'PICT'.
The first two lines retrieve the file reference number from the document structure.
GetEOF gets the number of bytes in the file. SetFPos sets the file mark 512 bytes (the
size of a 'PICT' file's header) past the beginning of the file, and the next line
subtracts the header size from the total size of the file. NewHandle allocates memory
for the Picture structure and FSRead reads in the file's data.
doWriteTextData
doWriteTextData is called by doWriteFile to write text data to the specified file.
The first two lines retrieve the handle to the TextEdit edit structure from the document
structure. The number of bytes of text is then retrieved from the teLength field of the
text edit structure.
SetFPos sets the file mark to the beginning of the file. FSWrite writes the specified
number of bytes to the file. SetEOF adjusts the file's size. FlushVol stores to disk
all unwritten data currently in the volume buffer.
The penultimate line sets the windowTouched field of the document structure to indicate
that the document data on disk equates to the document data in memory.
doWritePictData
doWritePictData is called by doWriteFile to write picture data to the specified file.
The first two lines retrieve the handle to the relevant Picture structure from the
document structure. SetFPos sets the file mark to the start of the file. FSWrite writes
zeros in the first 512 bytes (the size of a 'PICT' file's header). GetHandleSize gets
the size of the Picture structure and FSWrite writes the bytes in the Picture structure
to the file. SetEOF adjusts the file's size and FlushVol stores to disk all unwritten
data currently in the volume buffer.
The penultimate line sets the windowTouched field of the document structure to indicate
that the document data on disk equates to the document data in memory.
doCopyAppNameResource
doCopyAppNameResource is called by doWriteFile when a newly created file has been written
to for the first time. It copies the missing application name string resource from the
resource fork of the application file to the resource fork of the new file.
The first line retrieves a handle to the file's document structure. The next four lines
establish the file type involved. FSpCreateResFile creates the resource fork in the new
file and FSpOpenResFile opens the resource fork. The application-defined function for
copying specified resources between specified files (doCopyResource) is then called. In
this case, the specified resource is the missing application name string resource, the
source resource file is the resource fork of the application file, and the destination
resource file is the resource fork of the new file.
CloseResFile closes the resource fork of the new file.
doCopyResource
doCopyResource copies specified resources between specified files. In this program, it
is called only by doCopyAppNameResource.
UseResFile sets the application's resource fork as the current resource file.
GetResource reads the specified resource into memory.
GetResInfo, given a handle, gets the resource type, ID and name. (Note that this line is
included only because of the generic nature of doCopyResource. The calling function has
passed doCopyResource the type and ID in this instance.)
DetachResource removes the resource's handle from the resource map without removing the
resource from memory, and converts the resource handle into a generic handle. UseResFile
makes the new file's resource fork the current resource file. AddResource makes the now
arbitrary data in memory into a resource, assigns a resource ID, type and name to that
resource, and inserts an entry in the resource map for the current resource file.
UpdateResFile then writes the resource map and data to disk.
FiltersAndHooks.c
FiltersAndHooks.c contains the file filter and dialog hook functions for the customised
Open dialog and the directory selection dialog, together with the function which calls up
the directory selection dialog.
filterFunctionOpenDialog
filterFunctionOpenDialog is the file filter function for the customised Open dialog.
The global variable gCurrentType contains an index into the gFileTypes array (see the
function doOpenCommand, above). As will be seen, the index changes according to the item
chosen by the user using the two-item pop-up menu button added to the dialog box.
If the file type passed in for evaluation matches the current file type indexed by
gFileTypes, the filter returns false, indicating that CustomGetFile should put it in the
list. All other file types are blocked as a result of the filter function returning
true.
hookFunctionOpenDialog
hookFunctionOpenDialog is the hook function for the customised Open dialog. It handles
item selection within the dialog. CustomGetFile calls this function immediately after
calling ModalDialog, passing the function the item number returned by ModalDialog.
A dialog hook function is required to return the item number passed to it or some other
item number. This includes "psuedo item numbers", that is, constants which do not
represent actual items in the item list. There are two types of psuedo items: psuedo
items passed to a dialog hook function by the Standard File Package; psuedo items passed
to the Standard File Package by a dialog hook function.
The sfHookFirstCall constant (see the first case in the switch statement) is an example
of the first kind of psuedo item. This psuedo item is sent to the dialog hook function
immediately before the dialog is displayed. The dialog hook function typically reacts to
this item by performing any necessary initialisation. In hookFunctionOpenDialog, this
initialisation involves setting the value of the pop-up menu button control (that is, the
current menu item) to equate to the initial value in the global variable gCurrentType
(1).
A dialog hook function can pass back psuedo items to request some action by the Standard
File Package, or to indicate that it needs no further action by the Standard File
Package. The last action following the receipt of the sfHookFirstCall psuedo item is to
return the sfHookNullEvent, which indicates that no further action is required.
At the second case, if the pop-up menu button was hit, the control's value is retrieved
and compared with the value currently in the global gCurrentType. If the two do not
match, gCurrentType is assigned the current value of the control (that is, the menu item
currently chosen) and the psuedo item sfHookRebuildList is returned. This psuedo item
instructs the Standard File Package to rebuild the list of files and folders.
If the item passed to hookFunctionOpenDialog was not sfHookFirstCall or the pop-up menu
button item, the last line returns that item number to the Standard File Package for
processing.
DIALOG HOOK FUNCTIONS FOR CUSTOMISED SAVE DIALOGS |
A dialog hook function for a customised Save dialog must account for the
fact that user action can result in another dialog opening on top of the Save
dialog. For example, if the user hits the New Folder button, the New Folder
dialog box will open. CustomPutFile will call your dialog hook function for
item selections in subsidiary dialogs as well as the main dialog. Your dialog
hook function must therefore return immediately if the dialog pointer received
does not pertain to the customised Save dialog itself. The content of the dialog
window's refCon field may be used to discriminate between the various dialogs.
The refCon field of a standard Save dialog's window contains sfMainDialogRefCon
(1937007718). Accordingly, you can assign that value to your customised dialog
window's refCon field and include the following as the beginning of your dialog
hook function:
if((GetWRefCon((WindowPtr) theDialog)) != (SInt32) sfMainDialogRefCon)
return; |
doDirectorySelectionDialog
doDirectorySelectionDialog is called when the user chooses the Directory Selection Dialog
item in the Demonstration menu.
The directory selection dialog box is a customised version of the standard Open dialog.
It adds a static text item at the top and a "Select" push button immediately below the
list.
The first two lines create routine descriptors for the filter and hook functions. Two
global variables are then initialised. The first is used to store the selected directory
name and the second (gDirectorySelectionFlag) is a flag which the hook function will set
to false if the user hits the Cancel button in the dialog box. The values assigned to
the fields of the dialogLocation variable ensure that the dialog will be displayed in the
centre of the screen.
CustomGetFile displays the dialog. When the user dismisses the dialog, the routine
descriptors are disposed of and the standard file reply structure "filled in" by
CustomGetFile is returned to the calling function (doMenuChoice), where, if the dialog
was dismissed using the Select button (not the Cancel button), the selected directory
name, volume reference number, and parent directory ID are extracted and drawn in the
bottom of the window.
filterFunctionDirSelect
filterFunctionDirSelect is the filter function for the directory selection dialog. It
inspects the appropriate bit in the file attributes field of the catalog information
parameter block passed to it. If the directory bit (bit 4) is set, false is returned,
indicating that the item should appear in the list; otherwise, true is returned to
exclude the item from the list.
hookFunctionDirSelect
hookFunctionDirSelect is the hook function for the directory selection dialog.
No initialisation is required by this hook function. Accordingly, although an
sfHookFirstCall psuedo item will be received just before the dialog is displayed, it is
disregarded.
If the sfIsFolder or sfIsVolume fields of the standard file reply structure pointed to by
dataPtr indicate that the selected item is a folder or a volume, and if the name
extracted from the standard file reply structure is not the same as that stored in the
global variable gPrevSelectedName, the new name is copied to gPrevSelectedName. Also,
the new string for the Select push button's title is built up prior to a call to
SetControlTitle. If the string is too wide for the push putton, it is centre-truncated.
If the item hit was the Select push button, a return is forced by faking a cancel (that
is, sfItemCancelButton is returned even though the Cancel button was not hit. This will
cause the dialog to be dismissed. If, on the other hand, the Cancel button was hit, the
global variable gDirectorySelectionFlag is set to false and the last line returns the
item number received (sfItemCancelButton), causing the dialog to be dismissed. (In this
demonstration, gDirectorySelectionFlag simply allows or defeats the drawing of the
selected directory name, volume reference number, and parent directory ID in the bottom
of the window.)
|