home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-11-18 | 101.9 KB | 3,104 lines | [TEXT/MPS ] |
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- #
- # Apple Macintosh Developer Technical Support
- #
- # Standard File Sample Application
- #
- # StdFile
- #
- # StdFile.p - Pascal Source
- #
- # Copyright © 1989 Apple Computer, Inc.
- # All rights reserved.
- #
- # Versions:
- # 1.00 04/89
- # 2.00 05/90
- # 2.01 06/92
- #
- # Components:
- # StdFile.p April 1, 1989
- # StdFile.h April 1, 1988
- # StdFile.r April 1, 1988
- # PStdFile.make April 1, 1988
- #
- # ----------
- # OBJECTIVES
- # ----------
- # This program attempts to demonstrate the following techniques:
- #
- # - Normal use of SFGetFile and SFPutFile
- # - Normal use of SFPGetFile and SFPPutFile. This includes use of Custom
- # Dialogs with handling of extra items through the implementation of a
- # Standard File Dialog Hook.
- # - First time initialization
- # - Extra Simple buttons (Disory/ThisDir/Options)
- # - Radio buttons (file format, types of files to show)
- # - Aliasing click on some buttons to clicks on other buttons
- # - Regenerating the list of files displayed
- # - Creating a full pathname from the reply record (using Working Directories
- # or DirID)
- # - Selecting a directory (ala MPW's "GetFileName -d")
- # - Simple file filter (checks file type)
- # - Complex file filter (looking inside the file)
- # - Adding and deleting and extra List Manager lists. This is shown for both
- # SFGetFile and SFPutFile.
- # - Select multiple files by adding a second list to the Dialog Box.
- # - Setting the starting Directory/Volume
- # - Describe pending update event clogging
- # - Ask the user to select a file, and then remember where it is the next
- # time the application is launched.
- #
- # --------------------------
- # Standard File Generalities
- # --------------------------
- #
- # The prompt string of SFGetFile is ignored in all calls. As noted on page
- # I-523 of Inside Mac, it is there for historical reasons only. The
- # Standard File package is built around a modal dialog window. Standard
- # File repeatedly calls ModalDialog waiting for user events. It is
- # possible for an application to hook into the dialog event loop by using
- # the following methods.
- #
- # File Filter
- # -----------
- # The file filter is called to give the application opportunity to filter
- # out files that is wishes not to be shown in the dialog. This file filter
- # is called before displaying the dialog, which is also before the Dialog
- # Hook or Dialog Filter are called. If this function returns TRUE, this
- # means the file should not be shown in the list. In other words the file
- # is to be filtered from the user.
- #
- # Dialog Hook
- # -----------
- # The dialog hook is called after Standard File calls ModalDialog. The
- # hook is a function that requires that an item number be passed back from
- # it. Normally, this is the same item number that was passed to us, but
- # not necessarily. For instance, we could return values that cause the
- # file names to be re-drawn or have the whole event ignored. Before the
- # dialog is drawn, our dialog hook gets called with a -1. This gives us
- # the opportunity to change things like Button titles, etc. It's in
- # important to note that Standard File seems to ignore the item returned by
- # the dialog hook when it is called the first time. The dialog hook will
- # also be passed a fake item of 100, which is used to mean a nullEvent.
- # This can be used as an idle event for the dialog hook.
- #
- # Dialog Filter
- # -------------
- # This is used in the SFPGetFile and SFPPutFile routines. It is for the
- # advanced versions of calling Standard File for programmers that desire a
- # custom dialog window. The dialog filter is the same filter procedure
- # used by the Dialog Manager. In the call to ModalDialog, you can supply a
- # filter. The dialog filter used by Standard File is passed to
- # ModalDialog. This filter is called by the Dialog Manager after it calls
- # GetNextEvent. This gives the filter the first opportunity to handle the
- # event. If the dialog filter returns TRUE, this tells the Dialog Manager
- # that the filter has handled the event. You will use the dialog filter to
- # handle items you've added to your custom dialog.
- #
- #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- PROGRAM SFSample;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- USES
- Types, QuickDraw, ToolUtils, Events, Controls, Windows, Dialogs,
- Menus, Devices, SegLoad, Files, Traps, Fonts, Resources, Memory,
- Packages, Lists, DiskInit, Script, LowMem, CursorCtl, Errors, FixMath,
- GestaltEqu, StandardFile;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- CONST
-
- {k prefix for constants, r for resources, m for menu, i for menu item,
- and s for string resource item}
-
- {Resource ID's for some alerts}
- rAboutAlert = 1000; {the about dialog}
- rExitAlert = 1001; {emergency exit user alert}
- rUserAlert = 1002; {error message user alert}
- rFInfoAlert = 1003; {display file info alert}
- rShowSelectionAlert = 1004;
-
- {Resource ID's for my Standard File Dialogs}
- rListGetDLOG = 2000;
- kFileListItem = 11; {Item number for list of files}
- kMultiOpenItem = 12; {Item number of multiple Open files}
- kRemoveItem = 13; {Item number of Remove button}
-
- rSFPGetFileDLOG = 2001;
- kTextButton = 11; {DoNormalPGet dialog items}
- kAppButton = 12;
-
- rSFPPutFileDLOG = 2002;
- kSFPPutNormalBtn = 9; {SFPPutFile with a Dialog Hook}
- kSFPPutTextBtn = 10;
- kSFPPutFreeSpace = 11;
-
- rGetDirectoryDLOG = 2003;
- kGetDirButton = 11; {DoGetDirectory dialog items}
- kGetDirNowButton = 12;
- kGetDirMessage = 13;
-
- rPutListsFileDLOG = 2004;
- kListItem = 9; {ListPut dialog items}
- kReplaceItem = 2;
-
- rOptionsDLOG = 2005;
- kOptionsButton = 9; {Put Options dialog items}
- kFormatString = 10;
-
- rOptionsSubDLOG = 2006;
- kDefaultFormat = 3; {Put Sub-Options dialog items}
- kFrameItem = 9;
-
- rGetIdleUpdates = 2007;
- kTimeItem = 11; {DoIdleUpdates dialog items}
- kSizeItem = 12;
-
- kExistingFileALRT = -3996; {"Replace file?" dialog ID from System file}
-
- {menu resources; m for menu and i for menu item}
- rMenuBar = 1000; {Menu resources}
-
- mApple = 128; {indexes for Apple menu items}
- iAboutMe = 1;
-
- mFile = 129; {indexes for File menu items}
- iQuit = 12;
-
- mEdit = 130; {indexes for Edit menu items}
- iUndo = 1;
- iCut = 3;
- iCopy = 4;
- iPaste = 5;
- iClear = 6;
-
- mSFExamples = 131; {indexes for Examples menu items}
- iNormalGet = 1;
- iNormalPGet = 2;
- iFileFilter = 3;
- iGetDirectory = 4;
- iMultiFile = 5;
-
- iNormalPut = 7;
- iNormalPPut = 8;
- iForceDirectory = 9;
- iPutListsFile = 10;
- iPutOptions = 11;
-
- iIdleUpdates = 13;
- iRememberFile = 14;
-
- {Miscellaneous Strings from Str# resource}
- rStrMisc = 1000;
- sKFree = 1; {K free}
- sSelectFolder = 2; {Select a Folder}
- sSelectedCancel = 3; {The Cancel button.}
- sSaveAsMsg = 4; {Save As…}
- sTempFName = 5; {suffix of temporary file name}
- sFileSize = 6; {K on disk}
-
- rStrList = 1001; {Used to create a List Manager list}
-
- rErrStrings = 1002; {error String Str# ID}
- sStandardErr = 1; {An error has occurred.}
- sMemErr = 2; {A Memory Manager error has occurred.}
- sResErr = 3; {A Resource Manager error has occurred.}
- sLowMemory = 4; {Memory is too low to continue...}
- sNoMenus = 5; {Could not find application's menu...}
- sFileSystem = 6; {A File System error has occurred.}
- sLostFile = 7; {The file to be remembered has been lost}
- sOldAUX = 8; {Requires A/UX version 2.0 or later.}
- sNoHFS = 9; {Requires a System that supports HFS}
-
- {Useful Stadard File constants}
- kSFTopLeft = $00500050; {topLeft corner of Standard File dialog}
- kReDrawList = 101; {causes the file list to be recalculated}
- kShowAllFiles = -1; {show all files in the StdFile list}
- kShowIt = FALSE; {FALSE means I do not filter out...}
- kNoSelection = 0; {reply.fType is NIL for no selection}
- kNullModalEvt = 100; {Item number for null event from ModalDialog}
- kNoItem = 0; {no item, ignore it}
- kFirstTime = -1; {the first time our hook it's passed a -1}
-
- {Useful File Manager constants}
- kFSAsynch = TRUE; {asynchronous File Manager call}
- kInvisibleBit = 14; {bit set in ioFlFndrInfo.fdFlags if invisible}
- kFolderBit = 4; {bit set in ioFlAttrib for a folder}
-
- {misc. application constants}
- kScrollbarWidth = 16; {kScrollBarWidth can be used in
- calculating values for control
- positioning and sizing.}
- kScrollbarAdjust = kScrollbarWidth - 1;
-
- kListFrameInset = 1; {inset rectangle adjustment for list frame}
- kCntlActivate = 0; {enabled control’s hilite state}
- kCntlDeactivate = $FF; {disabled control’s hilite state}
- kCntlOn = 1; {control’s value when truned on}
- kCntlOff = 0; {control’s value when truned off}
- kButtonFrameSize = 3; {button frame’s pen size}
- kButtonFrameInset = - 4; {inset rectangle adjustment around button}
- kSysEnvironsVersion = 1; {Version of the SysEnvRec I understand.}
- kBroughtToFront = 3; {Number of get event calls to pull us forward}
- kWantSeconds = TRUE; {IUTimeString flag for using seconds}
- kMinSpace = 32 * 1024; {minimum available memory I allow in heap}
- kMinAUXVersion = $200; {minimum version of A/UX I'll run with}
- kDefaultAUXVersion = $100; {assumed version 1.x.x of A/UX}
- rUpdateWindow = 1000; {WIND resource for updates}
- rMemorizedFile = 1000; {FILE resource of file to remember}
- kNumberOfMasters = 1; {number of master pointer blocks}
- rAppSignature = 'sc18'; {app signature/creator ID}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- TYPE
-
- {This record is enough to specify a file before rebooting. File names were limited
- to 63 chars with MFS, but HFS limits names to 31 chars}
-
- FileSpec = RECORD
- vRefNum: INTEGER;
- parID: LONGINT;
- name: Str63;
- END;
- FileSpecList = ARRAY[1..1000] OF FileSpec;
- FileSpecPtr = ^FileSpecList;
- FileSpecHdl = ^FileSpecPtr;
-
- {This record is enough to specify a file after rebooting. This record is kept
- as a resource when the user selects a file.}
-
- FileDesc = RECORD
- volName: Str27; {volume names are limited to 27 chars}
- parID: LONGINT;
- name: Str63; {file names are limited to 63 chars}
- END;
- FileDescPtr = ^FileDesc;
- FileDescHdl = ^FileDescPtr;
-
- {This record is used for the unsigned long integers which Pascal really doesn't handle}
-
- TwoIntsMakesALong = RECORD {build a signed LONGINT...}
- CASE INTEGER OF {...from an unsigned INTEGER}
- 1:
- (long: LONGINT);
- 2:
- (ints: ARRAY [0..1] OF INTEGER);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- VAR
-
- {gMac is used to hold the result of a SysEnvirons call. This makes it
- convenient for any routine to check the environment. It is considered
- global information, anyway.}
-
- gMac: SysEnvRec; {set up by Initialize}
-
- {gInBackground is maintained by our osEvent handling routines. Any part of
- the program can check it to find out if it is currently in the background.}
-
- gInBackground: BOOLEAN; {maintained by Initialize and EventLoop}
-
- {This determines if we can call WaitNextEvent, which is always present for any
- System 6.0x user but only possible for earilier systems running MultiFinder.}
-
- gHasWaitNextEvent: BOOLEAN; {set by Initialize}
-
- {These next three globals are used to remember the application's location.
- This is the volume and the folder that hold the application at launch time.}
-
- gAppName: Str255; {application's name}
- gAppParID: LONGINT; {dirID of application's folder}
- gAppVRefNum: INTEGER; {volume containing application}
-
- {Sometimes it is necessary to have a global SFReply record.}
-
- gReply: SFReply; {used in some SF samples}
-
- {This is a flag used to signal a normal file format or not used in a couple examples.}
-
- gNormal: BOOLEAN;
-
- {This is a handle to an array of my FileSpecs. It is accessed by a few routines, so
- it needed to be global.}
-
- gMultiFiles: FileSpecHdl; {array of files to open}
-
- {This is a handle to a List Manager list. It is accessed by a few routines, so
- it needed to be global.}
-
- gListHandle: ListHandle; {list use for multiple get files}
-
- {This is the selected dirID that the user selected while in SFGet.}
-
- gMyCurDir: LONGINT; {for SetDirectory sample}
-
- {This is a string set by a few of the examples used to determine which file format
- the user wanted to save the file as.}
-
- gPutFormat: Str255; {string of file format}
-
- gLsBkUp: LONGINT;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Initialize}
-
- FUNCTION NumToolboxTraps: INTEGER;
-
- {InitGraf is always implemented (trap $A86E). If the trap table is big
- enough, trap $AA6E will always point to either Unimplemented or some other
- trap, but will never be the same as InitGraf. Thus, you can check the size
- of the trap table by asking if the address of trap $A86E is the same as $AA6E.}
-
- BEGIN
- IF NGetTrapAddress(_InitGraf, ToolTrap) = NGetTrapAddress($AA6E, ToolTrap) THEN
- NumToolboxTraps := $200
- ELSE
- NumToolboxTraps := $400;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Initialize}
-
- FUNCTION GetTrapType(theTrap: INTEGER): TrapType;
-
- {Determines the type of a trap based on its trap number. If bit 11 is clear,
- then it is an OS trap. Otherwise, it is a Toolbox trap.}
-
- BEGIN
- { OS traps start with A0, Tool with A8 or AA. }
- IF BAND(theTrap, $0800) = 0 THEN {per Darin}
- GetTrapType := OSTrap
- ELSE
- GetTrapType := ToolTrap;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Initialize}
-
- FUNCTION TrapExists(theTrap: INTEGER): BOOLEAN;
-
- { Check to see if a given trap is implemented. This is only used by the
- Initialize routine in this program, so we put it in the Initialize segment. }
-
- VAR
- theTrapType: TrapType;
-
- BEGIN
- theTrapType := GetTrapType(theTrap);
- IF (theTrapType = ToolTrap) THEN BEGIN
- theTrap := BAND(theTrap, $07FF);
- IF theTrap >= NumToolboxTraps THEN
- theTrap := _Unimplemented;
- END;
-
- TrapExists := NGetTrapAddress(_Unimplemented, ToolTrap) <> NGetTrapAddress(theTrap,
- theTrapType);
- END; { TrapExists }
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Initialize}
-
- FUNCTION GetAUXVersion: INTEGER;
-
- {The glue code for Gestalt will return appropriate values if the
- system doesn't support the trap. If an error is returned from Gestalt,
- then either we weren't running on A/UX, or the Gestalt trap
- was unable to fulfill the request so we revert to using low memory
- globals instead. This will work for A/UX version 1.x. All other errors
- are ignored (implies A/UX not present).
-
- NOTE: We right shift AUXversion by 8 bits to get major version number
- contained in the high byte, since we're not concerned with the minor
- version. Anything better than 2.0 is good enough for me.}
-
- CONST
- kAUXbit = 9; {bit set in LMGetHWCfgFlags for A/UX}
- kShiftHighByte = 8; {shift 8 bits to the right}
-
- VAR
- AUXversion: LONGINT;
- err: INTEGER;
-
- BEGIN
- err:= Gestalt(gestaltAUXVersion, AUXVersion);
-
- IF (err <> noErr) THEN BEGIN
- IF BTst(LMGetHWCfgFlags, kAUXbit) THEN
- AUXversion:= kDefaultAUXVersion {A/UX is running, default version}
- ELSE
- AUXVersion:= 0; {less than 1 means no version}
- END;
- GetAUXVersion:= BSR(AUXversion, kShiftHighByte);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- {The following set of routines are used to access a couple of low memory
- globals that are necessary when extending Standard File. One example is
- trying to get the current directory while in a file filter. These routines
- were used to bottleneck all the low memory usage. If the system one day
- supports them with a trap call, then we can easily update these routines.}
-
- FUNCTION GetSFCurDir: LONGINT;
-
- BEGIN
- GetSFCurDir:= LMGetCurDirStore;
- END;
-
- PROCEDURE SetSFCurDir(dirID: LONGINT);
-
- BEGIN
- LMSetCurDirStore(dirID);
- END;
-
- FUNCTION GetSFVRefNum: INTEGER;
-
- BEGIN
- GetSFVRefNum:= -LMGetSFSaveDisk;
- END;
-
- PROCEDURE SetSFVRefNum(vRefNum: INTEGER);
-
- BEGIN
- LMSetSFSaveDisk(-vRefNum);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION GetKFreeSpace(vRefNum: INTEGER): LONGINT;
-
- {Return the amount of free space on the volume in KBytes. This builds a
- LONGINT based on an unsigned INTEGER, which looks like a negitive value to
- Pascal. -1 is return as the size if there is an error.}
-
- VAR
- pb: HParamBlockRec;
- convert: TwoIntsMakesALong;
- err: OSErr;
-
- BEGIN
- WITH pb DO BEGIN {set up the block for PBHGetVInfo}
- ioNamePtr := NIL; {we don't care about the name}
- ioVRefNum := vRefNum;
- ioVolIndex := 0; {use ioVRefNum only}
- END; {with}
- err := PBHGetVInfo(@pb, FALSE);
-
- IF (err = noErr) THEN BEGIN
- convert.ints[0] := 0;
- convert.ints[1] := pb.ioVFrBlk;
- GetKFreeSpace := (convert.long * pb.ioVAlBlkSiz) DIV 1024;
- END
- ELSE {we couldn't get free space size!}
- GetKFreeSpace := - 1;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION FailLowMemory(memRequest: LONGINT): BOOLEAN;
-
- {Call PurgeSpace and see if the requested amount of memory exists in the
- heap including a minimal amount of heap space. Also, if my grow zone’s
- reserve has been release, that’s considered a failure. I don’t perform
- any purging here. The Memory Manager will do this if it needs the space.
- This routine can be called with a memRequest = 0. This checks if the heap
- space is getting critical.}
-
- VAR
- total, contig: LONGINT;
-
- BEGIN
- PurgeSpace(total, contig);
- FailLowMemory:= total < (memRequest + kMinSpace);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION GetWTitleHeight(variant: INTEGER): INTEGER;
-
- {Try and determine the window’s title bar height. This isn’t easy,
- especially if the window is invisible. We all know how the standard Apple
- System WDEF works, at least at this date we do. I assume that method as
- shown below. I could do what MacApp does with the Function CallWDefProc.
- If the user has installed a replacement for the System WDEF, then it’s
- their problem to deal with. My method will work as long as Apple doesn’t
- change how the WDEF in System 6.0 calculates the title bar height. This
- will allow my application to work on international Macs that have a larger
- system font than Chicago. I check the window’s variant code for one that
- includes a title bar. I will have to change this routine to adjust for
- the modal-moveable window type, which hasn’t been defined yet.
-
- In this routine, I violate my rule about not using the GetPort, SetPort,
- SetPort sequence mentioned at the start of the file. Mostly, I do this
- because it’s not all that apparent that a routine called GetWTitleHeight
- will change the port, so I make sure that it doesn’t.}
-
- VAR
- info: FontInfo;
- curGraf,
- wMgrPort: GrafPtr;
- wTitleHeight: INTEGER;
-
- BEGIN
- IF variant IN [documentProc, noGrowDocProc,
- zoomDocProc, zoomNoGrow, rDocProc] THEN BEGIN
- GetPort(curGraf);
- GetWMgrPort(wMgrPort); {I need to know the font...}
- SetPort(wMgrPort); {info in the System’s port}
- GetFontInfo(info);
- SetPort(curGraf); {restore current port}
- WITH info DO
- wTitleHeight:= ascent + descent + leading + 2;
- IF wTitleHeight < 19 THEN
- wTitleHeight:= 19; {the title is always at least 19}
- GetWTitleHeight:= wTitleHeight;
- END ELSE
- GetWTitleHeight:= 0; {other window types have no title}
- END; {GetWTitleHeight}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE CenterWindowRect(variant: INTEGER; VAR theRect: Rect);
-
- {Given a window’s portRect, this routine will center the rectangle on the
- main device within the desktop region. This excludes the menu bar area.
- It follows the Apple Human Interface Guidelines for where to place a
- window centered on the screen. That is, 1/3th of the desktop shows above
- the window and 2/3ths below it. It returns a new rectangle set to the
- centered position. The top and left of this rectangle can be used with
- MoveWindow. This routine only considers the main device. CenterWindowRect
- could take a GDevice as a parameter to center the VAR rect with. This
- would also allow Alerts or dialogs that are closely associated with a
- document window to be centered relative to the monitor that contains that
- document. This is also one of the Human Interface Guidelines. MacApp 2.0
- has a utility CalcScreenRect that you can borrow from to include this.
-
- WARNING: This routine may move or purge memory.}
-
- VAR
- rectSize: Point;
- wTitleHeight: INTEGER;
-
- BEGIN
- wTitleHeight:= GetWTitleHeight(variant); {get title height}
- WITH theRect DO BEGIN {get size of rect}
- SetPt(rectSize, right, bottom + wTitleHeight); {include it in size}
- SubPt(topLeft, rectSize);
- END;
- WITH qd.screenBits.bounds DO BEGIN {1/3th below menubar}
- theRect.top:= ((bottom - top - LMGetMBarHeight - rectSize.v) DIV 3)
- + LMGetMBarHeight;
- theRect.left:= ((right - left) - rectSize.h) DIV 2; {centered horz}
- END;
- WITH theRect DO BEGIN {return adjusted rect}
- SetPt(botRight, left, top);
- AddPt(rectSize, botRight);
- top:= top + wTitleHeight; {remove title height from rect}
- END;
- END; {CenterWindowRect}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION CenteredAlert(alertID: INTEGER): INTEGER;
-
- {Given an Alert ID, this routine will center the alert’s rectangle before
- showing it on the main screen. This follows the Apple Human Interface
- Guidelines for where to place a centered window on the screen. If the
- Alert is closely associated with another window, considerations should be
- given to what the window is located. If this other window is on a screen
- other then the main monitor, then it would be best to center the Alert on
- that other monitor.}
-
- VAR
- alertHandle: AlertTHndl;
- alertRect: Rect;
- itemHit: INTEGER;
-
- BEGIN
- alertHandle:= AlertTHndl(Get1Resource('ALRT', alertID));
- IF alertHandle <> NIL THEN BEGIN
- HNoPurge(Handle(alertHandle));
- alertRect:= alertHandle^^.boundsRect;
- CenterWindowRect(dBoxProc, alertRect);
- alertHandle^^.boundsRect:= alertRect;
- HPurge(Handle(alertHandle));
- END;
- CenteredAlert:= Alert(alertID, NIL);
- END; {CenteredAlert}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE EmergencyExit(message: INTEGER);
-
- {Display an alert that tells the user an error occurred, then exit the
- program. This routine is used as an ultimate bail-out for serious errors
- that prohibit the continuation of the application. Errors that do not
- require the termination of the application are handled with AlertUser.
- Error checking and reporting has a place even in the simplest application.
-
- BUG NOTE: GetIndString will return a bogus String if the index is not
- positive.}
-
- VAR
- msg1: Str255;
- theItem: INTEGER;
-
- BEGIN
- SetCursor(qd.arrow);
- IF message > 0 THEN
- GetIndString(msg1, rErrStrings, message)
- ELSE
- msg1:= ''; {in case there is no message}
- ParamText(msg1, '', '', '');
- theItem:= CenteredAlert(rExitAlert);
- ExitToShell; {we're out of here}
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE SetRadioButton(dlgPtr: DialogPtr; itemNo: INTEGER; state: INTEGER);
-
- {Handy routine for setting the value of a radio button. Given a dialog
- pointer, and item number, and a state, this routine will take care of the
- rest.}
-
- VAR
- iKind: INTEGER;
- iHandle: Handle;
- iRect: Rect;
-
- BEGIN
- GetDialogItem(dlgPtr, itemNo, iKind, iHandle, iRect);
- SetControlValue(ControlHandle(iHandle), state);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE PositionTwoRects(outerRect: Rect; VAR innerRect: Rect; horzRatio, vertRatio: Fixed);
-
- {Given two rectangles, this routine positions the second within the first one
- so that the it maintains the spacing specified by the horzRatio and vertRatio
- parameters. In other words, to center an inner rectangle hoizontally, but
- have its center be 1/3 from the top of the outer rectangle, call this
- routine with horzRatio = FixRatio (1, 2), vertRatio = FixRatio (1, 3). We use
- Fixed rather than floating point because Fixed has plenty of accuracy and is very fast. }
-
- VAR
- outerRectHeight: INTEGER;
- outerRectWidth: INTEGER;
- innerRectHeight: INTEGER;
- innerRectWidth: INTEGER;
- yLocation: INTEGER;
- xLocation: INTEGER;
-
- BEGIN
- outerRectHeight := outerRect.bottom - outerRect.top;
- outerRectWidth := outerRect.right - outerRect.left;
-
- innerRectHeight := innerRect.bottom - innerRect.top;
- innerRectWidth := innerRect.right - innerRect.left;
-
- yLocation := Fix2Long (FixMul (Long2Fix (outerRectHeight - innerRectHeight), vertRatio))
- + outerRect.top;
- xLocation := Fix2Long (FixMul (Long2Fix (outerRectWidth - innerRectWidth), horzRatio))
- + outerRect.left;
- WITH innerRect DO BEGIN
- top := yLocation;
- left := xLocation;
- bottom := yLocation + innerRectHeight;
- right := xLocation + innerRectWidth;
- END;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE OutlineButton(button: UNIV ControlHandle);
-
- {Given any control handle, this will draw an outline around it. This is
- used for the default button of a window. The extra nice feature here is
- that I’ll erase the outline for buttons that are inactive. Seems like
- there should be a Toolbox call for getting a control’s hilite state.
- Since there isn’t, I have to look into the control record myself. This
- should be called for update and activate events.
-
- The method for determining the oval diameters for the roundrect is a
- little different than that recommended by Inside Mac. IM I-407 suggests
- that you use a hardcoded (16,16) for the diameters. However, this only
- looks good for small roundrects. For larger ones, the outline doesn’t
- follow the inner roundrect because the CDEF for simply buttons doesn’t
- use (16,16). Instead, it uses half the height of the button as the
- diameter. By using this formula, too, our outlines look better.
-
- WARNING: This will set the current port to the control’s window.}
-
- VAR
- theRect: Rect;
- curPen: PenState;
- buttonOval: INTEGER;
-
- BEGIN
- IF button <> NIL THEN BEGIN
- SetPort(button^^.contrlOwner);
- GetPenState(curPen);
- PenNormal;
- theRect := button^^.contrlRect;
- InsetRect(theRect, kButtonFrameInset, kButtonFrameInset);
- buttonOval := (theRect.bottom - theRect.top) DIV 2;
- IF (button^^.contrlHilite = kCntlActivate) THEN
- PenPat(qd.black)
- ELSE
- PenPat(qd.gray);
- PenSize(kButtonFrameSize, kButtonFrameSize);
- FrameRoundRect(theRect, buttonOval, buttonOval);
- SetPenState(curPen);
- END;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE DoAbout;
-
- VAR
- apNameHndl: StringHandle;
- curVersion: VersRecHndl;
- apName: Str255;
- verNum: Str255;
- itemHit: INTEGER;
-
- BEGIN
- curVersion := VersRecHndl(Get1Resource('vers', 1));
- IF curVersion <> NIL THEN BEGIN
- verNum := StringPtr(ORD(@curVersion^^.shortVersion) + ORD(curVersion^^.
- shortVersion[0]) + 1)^ { get long version string }
- END ELSE
- verNum := '????'; {at least initialize it}
-
- ParamText(gAppName, verNum, '', '');
- itemHit := CenteredAlert(rAboutAlert);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE AlertUser(error: INTEGER; messageID: INTEGER);
-
- {Display an alert to inform the user of an error. MessageID acts as an
- index into a Str# resource of error messages. This will attempt to
- replace the error number with a String if the sound is a Sound Manager
- error.
-
- BUG NOTE: GetIndString will return a bogus String if the index is not
- positive.}
-
- VAR
- msg1, msg2: Str255;
- theItem: INTEGER;
-
- BEGIN
- IF messageID > 0 THEN
- GetIndString(msg1, rErrStrings, messageID)
- ELSE
- msg1:= ''; {in case there is no message}
- IF error <> noErr THEN
- NumToString(error, msg2)
- ELSE
- msg2:= '';
- ParamText(msg1, msg2, '', '');
- theItem:= CenteredAlert(rUserAlert);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION MyHGetVol(volName: StringPtr; VAR vRefNum: INTEGER; VAR dirID: LONGINT): OSErr;
-
- {BUG NOTE: The high level call, HGetVol, should do this dirty work for us.
- Unfortunately it is returning the ioVRefNum which Inside Mac warns may be
- a working directory. The field ioWDVRefNum will always be the real vRefNum.}
-
- VAR
- myWDBRec: WDPBRec;
-
- BEGIN
- myWDBRec.ioCompletion:= NIL;
- myWDBRec.ioNamePtr:= NIL;
- MyHGetVol:= PBHGetVol(@myWDBRec, kFSAsynch);
- vRefNum:= myWDBRec.ioWDVRefNum; {the real vRefNum}
- dirID:= myWDBRec.ioWDDirID;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION GetTempFileName: Str63;
-
- {Build a name for a temporary file by adding a suffix (contained in a
- resource) to the application's name.}
-
- VAR
- tempStr: Str255;
-
- BEGIN
- GetIndString(tempStr, rStrMisc, sTempFName);{build file name}
- GetTempFileName:= CONCAT(gAppName, tempStr);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE Terminate;
-
- {Clean up the application and exit. Delete the temporary file.}
-
- VAR
- err: OSErr;
-
- BEGIN
- err:= HDelete(gAppVRefNum, gAppParID, GetTempFileName);
- ExitToShell; {exit if no cancellation}
- END; {Terminate}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE DrawItemFrame(dlgPtr: DialogPtr; itemNo: INTEGER);
-
- {Draws a frame around the dialog item.}
-
- VAR
- iRect: Rect;
- ps: PenState;
- iHandle: Handle;
- iKind: INTEGER;
-
- BEGIN
- GetDialogItem(dlgPtr, itemNo, iKind, iHandle, iRect);
- GetPenState(ps);
- PenSize(1, 1);
- FrameRect(iRect);
- SetPenState(ps);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION PathNameFromDirID(DirID: LONGINT; vRefnum: INTEGER;
- VAR fullPathName: Str255): OSErr;
-
- {Given a DirID and real vRefnum, this routine will create and return the
- full pathname that it corresponds to. It does this by calling
- PBGetCatInfo for the given directory, and finding out its name and the
- DirID of its parent. It the performs the same operation on the parent,
- sticking its name onto the beginning of the first directory. This whole
- process is carried out until we have processed the root directory
- (identified with a DirID of fsRtDirID.
-
- SPECIAL NOTE: This routine is once check for A/UX and would add the "/"
- delimiter. A/UX 2.0 supports the ":" just like HFS, and 2.0 is the
- officially supported version. So, we no longer need to special case the
- code. An important comment that was originally in this routine should be
- considered again.
-
- HOWEVER, BECAUSE OF THIS DEPENDENCY ON THE IDIOSYNCRASIES OF FILE
- SYSTEMS, GENERATING FULL PATHNAMES FOR OTHER THAN DISPLAY PURPOSES IS
- DISCOURAGED; IT'S CHANGED IN THE PAST WHEN A/UX WAS IMPLEMENTED, AND IT
- MAY CHANGE AGAIN IN THE FUTURE IF SUPPORT FOR OTHER FILE SYSTEMS SUCH AS
- PRODOS, MS-DOS, OR OS/2 ARE ADDED.
-
- The full path name cannot exceed 255 chars. If this happens, an error
- bdNamErr is returned. So be careful out there!}
-
- VAR
- dirName: Str255;
- fullName: Str255;
- myCInfo: CInfoPBRec;
- err: OSErr;
-
- BEGIN
- err:= noErr;
- fullName:= '';
- dirName:= '';
- myCInfo.ioNamePtr:= @dirName;
- myCInfo.ioDrParID:= DirID;
-
- REPEAT
- WITH myCInfo DO BEGIN
- ioVRefNum:= vRefnum;
- ioFDirIndex:= -1; {-1 means use ioDrDirID}
- ioDrDirID:= ioDrParID;
- END;
- err:= PBGetCatInfo(@myCInfo, NOT kFSAsynch);
- IF err = noErr THEN BEGIN
- dirName:= CONCAT(dirName, ':');
- IF LENGTH(dirName) + LENGTH(fullName) > 255 THEN
- err:= bdNamErr {too big to eat!}
- ELSE
- fullName:= CONCAT(dirName, fullName);
- END;
- UNTIL (myCInfo.ioDrDirID = fsRtDirID) | (err <> noErr);
-
- fullPathName:= fullName;
- PathNameFromDirID:= err;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION PathNameFromWD(wdRefNum: LONGINT; VAR pathName: Str255): OSErr;
-
- {Given an HFS working directory, this routine returns the full pathname
- that it corresponds to. It does this by calling PBGetWDInfo to get the
- real VRefNum and DirID of the working directory. It then calls
- PathNameFromDirID, and returns its result.
-
- BUG NOTE: The special case portion of this routine that worked around an
- U/UX 1.1 bug has been removed. This application only runs with A/UX 2.0.
- The following comment has been left here for historical purposes only.
-
- PBGetWDInfo has a bug under A/UX 1.1. If vRefNum is a real vRefNum and
- not a wdRefNum, then it returns garbage. Since A/UX has only one volume
- (in the Macintosh sense) and only one root directory, this can occur only
- when a file has been selected in the root directory (/). So we look for
- this and hardcode the DirID and vRefNum.
-
- SPECIAL NOTE: Make sure you have read and understood Tech Note 238.}
-
- VAR
- dirID: LONGINT;
- procID: LONGINT;
- vRefNum: INTEGER;
- err: OSErr;
-
- BEGIN
- err:= GetWDInfo(wdRefNum, vRefNum, dirID, procID);
- IF err = noErr THEN
- err:= PathNameFromDirID(dirID, vRefNum, pathName);
- PathNameFromWD:= err;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE ShowSelection(name, msg: Str255);
-
- {This routine shows an Alert of the selected item (name) and an optional
- message (msg) that is used as the file's type.}
-
- VAR
- theItem: INTEGER;
-
- BEGIN
- ParamText(name, msg, '', '');
- theItem:= CenteredAlert(rShowSelectionAlert);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE ShowCancelled;
-
- {This simply calls up the alert showing the user selected the cancel
- button of Standard File.}
-
- VAR
- tStr: Str255;
- theItem: INTEGER;
-
- BEGIN
- GetIndString(tStr, rStrMisc, sSelectedCancel);
- ParamText(tStr, '', '', '');
- theItem:= CenteredAlert(rShowSelectionAlert);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION ShowFoundFile(fName: Str63; parID: LONGINT; vRefNum: INTEGER): OSErr;
-
- {This alert shows the file name and the modification date to prove that the
- file can be located by the given parameters.}
-
- VAR
- msg1: Str255;
- fHPBRec: HParamBlockRec;
- theItem: INTEGER;
- err: OSErr;
-
- BEGIN
- WITH fHPBRec DO BEGIN
- ioCompletion:= NIL;
- ioNamePtr:= @fName;
- ioFDirIndex:= 0;
- ioDirID:= parID;
- ioVRefNum:= vRefNum;
- END;
- err:= PBHGetFInfo(@fHPBRec, NOT kFSAsynch);
- IF err = noErr THEN BEGIN
- IUDateString(fHPBRec.ioFlMdDat, longDate, msg1);
- ParamText(fName, msg1, '', '');
- theItem:= CenteredAlert(rFInfoAlert);
- END;
- ShowFoundFile:= err;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE UIPFileList(theWindow: WindowPtr; item: INTEGER);
-
- {Dialog user item procedure to draw the multi-file list. The multi-file
- list item is requrest to be drawn by the Dialog Manager. A handle to the
- list must be stored in the global variable "gListHandle" because the
- Dialog Manager isn’t kind enough to let us pass a little information to
- our user-item procedures in the good computer science way, dagnabbit. The
- "theWindow" parameter is actually a pointer to our custom SFGetFile
- dialog. The "item" parameter specifies the item number to be drawn.
- Since only one item in our dialog uses UIPFileList, we don’t really have
- to check to see if the item number is in fact the multi-file list, but we
- might as well so that people think we’re some cool programmers who take
- care to check everything.
-
- Check to be absolutely sure we’re talking about the file list item before
- we do anything to it. This alert shows the file name and the modification
- date to prove that the file can be located by the given parameters.}
-
- BEGIN
- IF item = kFileListItem THEN BEGIN
- LUpdate(theWindow^.visRgn, gListHandle);{Draw visible part of list}
- DrawItemFrame(theWindow, kFileListItem); {...and frame it}
- END;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE CreateFGetList(dlgPtr: DialogPtr; VAR listRect: Rect;
- VAR listH: ListHandle);
-
- {Build a three column list. Set the column's width to cover the entire
- list's visible rectangle. This is done by adjusting the cellSize. This
- keeps the other two columns hidden from the user. I also do not allow a
- horizontal scrolling to prevent the user from viewing the hidden data.
- It's a sick hack. I really don't like this idea, nor would I encourage
- anyone else to do this. Sorta, "Do as I say, not as I do." I also
- adjust the list rectangle for best scrolling. This is something that few
- people do. They must be hard coding a size. The adjusted rectangle is
- returned so the caller will know the new size.}
-
- VAR
- lBounds: Rect; {Rectangle of list data}
- cellSize: Point; {size of a cell}
- info: FontInfo;
- newHeight: INTEGER; {adjustement to list rect size}
-
- BEGIN
- InsetRect(listRect, kListFrameInset, kListFrameInset);
- listRect.right:= listRect.right - kScrollbarAdjust;
- GetFontInfo(info);
- cellSize.h:= listRect.right - listRect.left;
- cellSize.v:= info.ascent + info.descent + info.leading;
- newHeight:= cellSize.v;
- WHILE (newHeight + cellSize.v) < (listRect.bottom - listRect.top) DO
- newHeight:= newHeight + cellSize.v;
- listRect.bottom:= listRect.top + newHeight;
- SetRect(lBounds, 0, 0, 3, 0); {three columns in list}
- listH:= LNew(listRect, {Location of list in window}
- lBounds, {Initial size of array}
- cellSize, {Cell size (0 = default)}
- 0, {Resource ID of LDEF}
- dlgPtr, {Pointer to our Std File dialog}
- TRUE, {needs to be drawn?}
- FALSE, {grow box?}
- FALSE, {horizontal scrolling?}
- TRUE); {vertical scrolling?}
- InsetRect(listRect, -kListFrameInset, -kListFrameInset);
- listRect.right:= listRect.right + kScrollbarAdjust;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE DeselectAllCells(listH: ListHandle);
-
- VAR
- listCell: Cell;
- done: BOOLEAN;
-
- BEGIN
- listCell:= Point(0);
- done:= FALSE;
- REPEAT
- IF LGetSelect(TRUE, listCell, listH) THEN
- LSetSelect(FALSE, listCell, listH)
- ELSE
- done:= TRUE;
- UNTIL done;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE AddFItem(name: Str255; curDir: LONGINT; vRefNum: INTEGER;
- listH: ListHandle);
-
- {This takes the given information and adds it to the list. First thing to
- do is check for the file to have already been added to the list. If it
- has, then we only have to show the user that the item is already there.
- Before we eixt, we select just the current cell and the scroll it into view.}
-
- VAR
- listCell: Cell; {File list cell}
- itemDirID: LONGINT;
- itemVRefNum: INTEGER;
- dataLength: INTEGER;
- done: BOOLEAN;
-
- BEGIN
- listCell:= Point(0);
- done:= FALSE;
- REPEAT
- IF LSearch(@name[1], LENGTH(name), NIL, listCell, listH) THEN BEGIN
- listCell.h:= 1;
- dataLength:= SizeOf(itemDirID);
- LGetCell(@itemDirID, dataLength, listCell, listH);
- listCell.h:= 2;
- dataLength:= SizeOf(itemVRefNum);
- LGetCell(@itemVRefNum, dataLength, listCell, listH);
- IF (curDir = itemDirID) & (vRefNum = itemVRefNum) THEN BEGIN
- done:= TRUE; {found the existing item}
- END;
- END
- ELSE BEGIN
- listCell.h:= 0;
- listCell.v:= LAddRow(1, listH^^.dataBounds.bottom, listH);
- LSetCell(@gReply.fName[1], LENGTH(name), listCell, listH);
- listCell.h:= 1;
- LSetCell(@curDir, SizeOf(curDir), listCell, listH);
- listCell.h:= 2;
- LSetCell(@vRefNum, SizeOf(vRefNum), listCell, listH);
- LSetSelect(TRUE, listCell, listH);
- done:= TRUE;
- END;
- UNTIL done;
- DeselectAllCells(listH);
- listCell.h:= 0; {first column, current row}
- LSetSelect(TRUE, listCell, listH);
- LAutoScroll(listH);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE GetFItems(listH: ListHandle);
-
- {We need to copy all of the files from the global list and store these into
- a global array of file descriptions.}
-
- VAR
- theCell: Point;
- numOfFiles: INTEGER;
- dataLength: INTEGER;
- index: INTEGER;
-
- BEGIN
- numOfFiles:= listH^^.dataBounds.bottom;
- gMultiFiles:= FileSpecHdl(NewHandle(SizeOf(FileDesc) * numOfFiles));
- MoveHHI(Handle(gMultiFiles)); {helps to reduce fragmentation}
- HLock(Handle(gMultiFiles));
- FOR index:= 1 TO numOfFiles DO BEGIN
- dataLength:= SizeOf(gMultiFiles^^[index].vRefNum);
- SetPt(theCell, 2, index - 1);
- LGetCell(@gMultiFiles^^[index].vRefNum, dataLength, theCell, listH);
- dataLength:= SizeOf(gMultiFiles^^[index].parID);
- SetPt(theCell, 1, index - 1);
- LGetCell(@gMultiFiles^^[index].parID, dataLength, theCell, listH);
- dataLength:= SizeOf(gMultiFiles^^[index].name);
- SetPt(theCell, 0, index - 1);
- LGetCell(@gMultiFiles^^[index].name[1], dataLength, theCell, listH);
- gMultiFiles^^[index].name[0]:= CHR(dataLength);
- END;
- HUnlock(Handle(gMultiFiles));
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE RemoveFItems(listH: ListHandle);
-
- {Delete all selected files from the multi-file list. Don’t want all that
- flickerin’, so turn off drawing. Keep deleting selected rows until no
- selected rows are left. Finally, turn on drawing and erase the list.
- It’ll get drawn on the next update event. This takes the given
- information and adds it to the list.}
-
- VAR
- listRect: Rect; {Rectangle for list}
- listCell: Cell; {File list cell}
-
- BEGIN
- LSetDrawingMode(FALSE, listH);
- listCell:= Point(0);
- WHILE LGetSelect(TRUE, listCell, listH) DO BEGIN
- LDelRow(1, listCell.v, listH);
- listCell:= Point(0);
- END;
- listCell:= Point(0);
- LSetSelect(TRUE, listCell, listH);
- LAutoScroll(listH);
- listRect:= listH^^.rView;
- LSetDrawingMode(TRUE, listH);
- InvalRect(listRect);
- EraseRect(listRect);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE CreateFPutList(dlgPtr: DialogPtr; VAR listRect: Rect;
- VAR listH: ListHandle; VAR cellStr: Str255);
-
- {This creates a list used to hold a number of file types. The selected
- item is used to determine which format the file should be saved as. The
- list rectangle is adjusted to the best scrolling size, and the adjusted
- size is returned to the caller. It's also important to select the first
- item in the list as a default file format and that only one item can be
- selected.}
-
- VAR
- entry: Str255;
- lBounds: Rect;
- newSize: Point;
- theCell: Point;
- newRow: INTEGER;
- dataLen: INTEGER;
-
- BEGIN
- InsetRect(listRect, kListFrameInset, kListFrameInset);
- listRect.right:= listRect.right - kScrollbarAdjust;
- SetRect(lBounds, 0, 0, 1, 0); {one dimentional list}
- listH:= LNew(listRect, {position in window}
- lBounds, {initial size of array}
- Point(0), {cell size (0 = default)}
- 0, {resource ID of LDEF}
- dlgPtr, {window pointer}
- TRUE, {drawit}
- FALSE, {has grow}
- FALSE, {scrollHoriz}
- TRUE); {scrollVert}
- newSize:= listH^^.cellSize; {get the size of one cell}
- WHILE (newSize.v + listH^^.cellSize.v) < (listRect.bottom - listRect.top) DO
- newSize.v:= newSize.v + listH^^.cellSize.v;
- LSize(newSize.h, newSize.v, listH); {adjust for best scrolling}
- listH^^.selFlags:= lOnlyOne; {single selections only}
-
- theCell:= Point(0);
- REPEAT
- GetIndString(entry, rStrList, theCell.v + 1);
- IF (entry <> '') THEN BEGIN
- newRow:= LAddRow(1, listH^^.databounds.bottom, listH);
- LSetCell(@entry[1], LENGTH(entry), theCell, listH);
- END;
- theCell.v:= theCell.v + 1;
- UNTIL (entry = '');
- theCell:= Point(0);
- LSetSelect(TRUE, theCell, listH);
- datalen:= SizeOf(cellStr);
- LGetCell(@cellStr[1], datalen, theCell, listH);
- cellStr[0]:= CHR(dataLen);
-
- listRect.bottom:= listRect.top + newSize.v;
- InsetRect(listRect, -kListFrameInset, -kListFrameInset);
- listRect.right:= listRect.right + kScrollbarAdjust;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE DisposeFPutList(listH: ListHandle; VAR cellStr: Str255);
-
- {Just get rid of the list, but before we do that get the currently selected
- cell's data which is the user's choice of file format.}
-
- VAR
- theCell: Point;
- dataLen: INTEGER;
-
- BEGIN
- SetPt(theCell, 0, 0);
- IF LGetSelect(TRUE, theCell, listH) THEN BEGIN
- datalen:= SizeOf(cellStr);
- LGetCell(@cellStr[1], datalen, theCell, listH);
- cellStr[0]:= CHR(dataLen); {length byte}
- END;
- LDispose(listH);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {Simple SFGetFile example}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {Simplest form of SFGetFile. This routine puts up a GetFile dialog with a
- request to show all files. When this is done, a check is made to see
- which of the Save or Cancel buttons were pressed. If the Save button was
- pressed, ShowSelection is called to display which file was selected. If
- the Cancel button was pressed, a dialog is shown saying so.
-
- A full path name is shown of the selected file. This is done as an
- example. Generally full path names should be avoided. If you wanted to
- remember the location of a file, such as the next time the application is
- launch, you should remember the volume name, directory ID, and file name.
- As an example of how to do this, refer to the routine DoRememberFile.}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE ShowPath;
-
- VAR
- pathStr: Str255;
- typeList: SFTypeList;
- reply: SFReply;
- err: OSErr;
-
- BEGIN
- SFGetFile(Point(kSFTopLeft), {location}
- '', {prompt String}
- NIL, {file filter}
- kShowAllFiles, {numtypes}
- typeList, {array to types to show}
- NIL, {dialog hook}
- reply); {record for returned values}
- IF reply.good THEN BEGIN
- err:= PathNameFromWD(reply.vRefnum, pathStr);
- IF err = noErr THEN BEGIN
- IF LENGTH(pathStr) + LENGTH(reply.fName) > 255 THEN
- AlertUser(bdNamErr, sFileSystem)
- ELSE
- ShowSelection(CONCAT(pathStr, reply.fName), '');
- END
- ELSE
- AlertUser(err, sFileSystem);
- END
- ELSE
- ShowCancelled;
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {SFPGetFile with Dialog Hook and Simple File Filter}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {Depending on the value of the global variable "gNormal", this
- demonstrations shows either TEXT files or APPL files. The value of
- "gNormal" is determined by the states of two radio buttons that are added
- to the dialog box. Our dialog hook routine is used to initialize the radio
- buttons and handle hits on them. When there is a hit on a radio button,
- "gNormal" is set to either TRUE or FALSE, and a special command is sent
- back to Standard File telling it to regenerate the list of file names it
- is displaying. This sample consists of 3 parts:
-
- DoNormalPGet
- ------------
- This routine initializes a variable for our file filter, and then calls
- SFPGetFile with pointers to two other routines and a resource number for
- a special dialog box with extra items in it.
-
- SimpleFFilter
- ----------------
- Specified in our SFPGetFile call to be called to specify whether or not a
- file should be displayed. If the global variable 'gNormal' is TRUE, then
- all text are displayed. Otherwise, Applications will be displayed.
-
- MySFGetDlgHook
- ----------------
- A routine that is called to handle hits on the non-standard items in our
- dialog box. The dialog hook is also used to perform some special
- initialization on the items in the dialog box. Standard file does this by
- calling this routine with a bogus 'item' number of -1. When we get this
- number, we initialize our radio buttons, and change the text in the Open
- button.
-
- The radio buttons are used to determine what files will appear in the
- files list. It does this by appropriately setting the 'gNormal' variable
- for SimpleFFilter, and then telling Standard File that the file list
- needs to be regenerated by passing back 101 as the function result.}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION MySFGetDlgHook(MySFitem: INTEGER; dlgPtr: DialogPtr): INTEGER;
-
- BEGIN
- MySFGetDlgHook:= MySFitem;
- CASE MySFitem OF
-
- kFirstTime: BEGIN
- SetRadioButton(dlgPtr, kAppButton, kCntlOff);
- SetRadioButton(dlgPtr, kTextButton, kCntlOn);
- END;
-
- kTextButton: BEGIN
- IF NOT gNormal THEN BEGIN
- SetRadioButton(dlgPtr, kAppButton, kCntlOff);
- SetRadioButton(dlgPtr, kTextButton, kCntlOn);
- gNormal:= TRUE;
- MySFGetDlgHook:= kReDrawList;
- END;
- END;
-
- kAppButton: BEGIN
- IF gNormal THEN BEGIN
- SetRadioButton(dlgPtr, kTextButton, kCntlOff);
- SetRadioButton(dlgPtr, kAppButton, kCntlOn);
- gNormal:= FALSE;
- MySFGetDlgHook:= kReDrawList;
- END;
- END;
-
- END;
- END;
-
-
- FUNCTION SimpleFFilter(p: ParmBlkPtr): BOOLEAN;
-
- BEGIN
- SimpleFFilter:= NOT kShowIt; {Don't show it -- default}
- WITH p^.ioFlFndrInfo DO
- IF gNormal THEN BEGIN
- IF fdType = 'TEXT' THEN
- SimpleFFilter:= kShowIt;
- END
- ELSE
- IF fdType = 'APPL' THEN
- SimpleFFilter:= kShowIt;
- END;
-
-
- PROCEDURE DoNormalPGet;
-
- VAR
- typeList: SFTypeList;
- reply: SFReply;
- err: OSErr;
- ffUPP: FileFilterUPP;
- dhUPP: DlgHookUPP;
-
- BEGIN
- gNormal:= TRUE;
-
- ffUPP := NewFileFilterProc(@SimpleFFilter);
- dhUPP := NewDlgHookProc(@MySFGetDlgHook);
- SFPGetFile(Point(kSFTopLeft), {location}
- '', {vestigial String}
- ffUPP, {file filter}
- kShowAllFiles, {numtypes}
- typeList, {array to types to show}
- dhUPP, {dialog hook}
- reply, {record for returned values}
- rSFPGetFileDLOG, {ID of Custom Dialog}
- NIL); {ModalDialog filterProc}
- DisposeRoutineDescriptor(dhUPP);
- DisposeRoutineDescriptor(ffUPP);
- IF reply.good THEN
- ShowSelection(reply.fName, '')
- ELSE
- ShowCancelled;
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {SFGetFile with a complex File Filter}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {Sample of a less trivial file filter. This routine is responsible for
- displaying only files that have 'ALRT' resources. It shows how to
- correctly look inside a file for qualifying information, and demonstrates
- how to get around a particular quirk when attempting multiple access to
- the resource fork.
-
- DoFFilter
- ---------
- Calls SFGetFile and specifies the File Filter.
-
- ComplexFFilter
- --------------
- This routine used to do skanky things like looking at a low memory global
- to determine of a new resource file map was opened or not. Even more
- nastiness was the it created a working directory. Well, this was all too
- sick and a completely different approach is now taken. Opening a
- resource file as read-only will always create a new map. This should
- generally be avoided, but here I'm opening it and then immediately
- closing it without allowing anything else to access the file.
-
- Opening resource forks can be tricky if it's already open. It would be
- very bad to use a resource fork that is already open by another
- application. The problem being that the Resource Manager doesn't deal
- with multiple users. Read Tech notes #116 and #185. There's also
- another problem. If a resource fork is opened by two applications and
- one closes the file then the entire resource fork may closed out from
- underneath the other application. I do, however, want to show the user
- files that contain ALRT resources even if the file is currently open. I
- use HOpenResFile with read only permission, which will give me a unique
- resource reference. I look for an ALRT resource and then immediately
- close the file. Do not use a resource file opened with read only
- permission because another application could come along and start writing
- to it. Another reason to use HOpenResFile is to avoid a necessary
- working directory. I can use the DirID kept updated by Standard File.
- Performing this search on each file is time consuming so I show a
- spinning cursor to show the user I'm working. Opening a resource fork
- may load resources mark preload. To avoid this, I call SetResLoad to
- FALSE. Read Tech Note #203 for other reasons not to play with resources.}
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION ComplexFFilter(p: ParmBlkPtr): BOOLEAN;
-
- {SPECIAL NOTE: The ParamBlockRec is a relocatable object in the heap. The
- file name is one of the fields in this handle. Because of this we need
- to copy the fields that we use to a local variable to avoid deferenced
- handle problems.}
-
- VAR
- fileName: Str255;
- oldTicks: LONGINT;
- resRef, curRes: INTEGER;
- vRefNum: INTEGER;
-
- BEGIN
- oldTicks:= TickCount;
- ComplexFFilter:= NOT kShowIt; {don’t show anything until I say so}
- fileName:= p^.ioNamePtr^; {must save name}
- vRefNum:= p^.ioVRefNum; {must save volume (or WD) reference}
- curRes:= CurResFile;
- SetResLoad(FALSE);
- resRef:= HOpenResFile(vRefNum, GetSFCurDir, fileName, fsRdPerm);
- IF (resRef <> - 1) THEN BEGIN
- UseResFile(resRef);
- IF Count1Resources('ALRT') > 0 THEN
- ComplexFFilter:= kShowIt; {hey, we found an Alert in here}
- CloseResFile(resRef);
- END; {restore everything}
- SetResLoad(TRUE);
- UseResFile(curRes);
- RotateCursor(TickCount - oldTicks);
- END;
-
-
- PROCEDURE DoFFilter;
-
- VAR
- typeList: SFTypeList;
- reply: SFReply;
-
- BEGIN
- SpinCursor(0); {get the spinning cursor ready}
- SFGetFile(Point(kSFTopLeft), {location}
- '', {vestigial String}
- @ComplexFFilter, {file filter}
- kShowAllFiles, {numtypes}
- typeList, {array to types to show}
- NIL, {dialog hook}
- reply); {record for returned values}
- IF reply.good THEN
- ShowSelection(reply.fName, '')
- ELSE
- ShowCancelled;
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {DoGetDirectory with a Dialog Hook and File Filter}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {This demonstrates how to modify SFGetFile to allow you to select a
- directory. This mimics the "GetFileName -d" function of MPW. As a matter
- of fact, the DLOG and DITL used in this sample were taken directly out of
- MPW. There are 2 major additions in the dialog used in this sample:
-
- 1. a Simple button that lets one select the directory that is currently
- highlighted in the list of directories,
- 2. Simple button at the top of the dialog that lets the user select the
- directory that we are currently *IN*.
-
- DoGetDirectory
- --------------
- Sets up the pointers to the Dialog Hook and File Filter. Once the user
- has selected a folder, we look at the global gMyCurDir which was set
- within the Dialog Hook. We take this DirID and call PBGetCatInfo to find
- the name of this folder. This is so we can display the name to the user.
-
- MyGetDirDlgHook
- ----------------
- No new techniques are really used in this sample. Hits on the two simple
- buttons are handled by a dialog hook called GetDirDlgHook. Depending on
- which button is hit, we set the global variable gMyCurDir to either
- gReply.fType (for the currently highlighted directory) or what Standard
- File has set as the current directory. We then simulate a hit on the Open
- button so that Standard File will return to our application and the
- FSReply.good will be TRUE.
-
- While an item is selected, the gReply.fType will be set to a value.
- gReply.fType is set to 0 when nothing is selected. We test for this
- during idle time (item = 100) and activate the Directory button
- appropriately. We also use the "Item = -1" feature to set up a prompt
- String.
-
- FolderFFilter
- -------------
- Normally, folders are ALWAYS shown, and aren't even passed to this file
- filter for judgement. Under such circumstances, it is only necessary to
- blindly return TRUE (allow no files whatsoever). However, Standard File
- is not documented in such a manner, and this feature may not be TRUE in
- the future. Therefore, we DO check to see if the entry passed to us
- describes a file or a directory. This can be done by checking a bit
- in the ioFlAttrib, which is set for folders.}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION FolderFFilter(p: ParmBlkPtr): BOOLEAN;
-
- BEGIN
- FolderFFilter:= NOT BTst(p^.ioFlAttrib, kFolderBit);
- END;
-
-
- FUNCTION GetDirDlgHook(item: INTEGER; dlgPtr: DialogPtr): INTEGER;
-
- VAR
- messageTitle: Str255;
- iRect: Rect;
- iHandle: Handle;
- iKind: INTEGER;
-
- BEGIN
- GetDirDlgHook:= item; {default, return same item}
- CASE item OF
- kFirstTime: BEGIN {Read prompt String from resource}
- GetIndString(messageTitle, rStrMisc, sSelectFolder);
- GetDialogItem(dlgPtr, kGetDirMessage, iKind, iHandle, iRect);
- SetDialogItemText(iHandle, messageTitle);
- END;
-
- kGetDirButton: BEGIN
- IF LONGINT(gReply.fType) <> 0 THEN BEGIN
- gMyCurDir:= LONGINT(gReply.fType);
- GetDirDlgHook:= getOpen; {simulate Open button}
- END;
- END;
-
- kGetDirNowButton: BEGIN
- gMyCurDir:= GetSFCurDir;
- GetDirDlgHook:= getOpen; {simulate Open button}
- END;
-
- kNullModalEvt: BEGIN
- IF LONGINT(gReply.fType) = kNoSelection THEN BEGIN
- GetDialogItem(dlgPtr, kGetDirButton, iKind, iHandle, iRect);
- HiliteControl(ControlHandle(iHandle), kCntlDeactivate);
- END
- ELSE BEGIN
- GetDialogItem(dlgPtr, kGetDirButton, iKind, iHandle, iRect);
- HiliteControl(ControlHandle(iHandle), kCntlActivate);
- END;
- END;
-
- END;
- END;
-
-
- PROCEDURE DoGetDirectory;
-
- VAR
- folderName: Str31; {directories can be 31 chars}
- typeList: SFTypeList;
- myCInfo: CInfoPBRec;
- err: OSErr;
- ffUPP: FileFilterUPP;
- dhUPP: DlgHookUPP;
-
- BEGIN
- ffUPP := NewFileFilterProc(@FolderFFilter);
- dhUPP := NewDlgHookProc(@GetDirDlgHook);
- SFPGetFile(Point(kSFTopLeft), {location}
- '', {vestigial String}
- ffUPP, {file filter}
- kShowAllFiles, {numtypes}
- typeList, {array to types to show}
- dhUPP, {Dialog hook}
- gReply, {record for returned values}
- rGetDirectoryDLOG, {Resource ID dialog}
- NIL); {ModalDialog filter proc}
- DisposeRoutineDescriptor(dhUPP);
- DisposeRoutineDescriptor(ffUPP);
-
- IF gReply.good THEN BEGIN
- folderName:= ''; {initialize string}
- WITH myCInfo DO BEGIN
- ioCompletion:= NIL; {no completion}
- ioNamePtr:= @folderName; {VAR to string}
- ioFDirIndex:= -1; {-1 means use ioDrDirID}
- ioVRefNum:= GetSFVRefNum; {this is the volume}
- ioDrDirID:= gMyCurDir; {this is the directory}
- END;
- err:= PBGetCatInfo(@myCInfo, NOT kFSAsynch);
- IF err = noErr THEN
- ShowSelection(folderName, '')
- ELSE
- AlertUser(err, sFileSystem);
- END
- ELSE
- ShowCancelled;
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {DoMultiFile}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {This demonstrates how to get multiple files with one call to Standard
- File. It collects a set of files while in the SFGetFile dialog by using
- of a secondary list. This is similar to the method used by many of the
- MPW tools. The original version of this example was a failed experiment.
- It attempted remember all of the files passed to the file filter, and
- using those names to create a new, application governed file list that
- will replace the one displayed by Standard File. However, this
- experiment seems doomed to failure, for the following reasons:
-
- * Icons are not displayed next to the names of files. This would require
- a custom LDEF. The resulting presentation without the small icons was
- more disconcerting that I thought.
- * The current directory button (the one displayed above the list of file
- names) disappears. This seems due to the fact that the pop-up menu-like
- item is not actually a dialog item, and is somehow attached to the list
- of file names handled by Standard File. Since I move that list off of the
- window so that I can display my own, the current directory button seems
- to go with it.
- * Updating the list is difficult. The normal sequence of events would
- desirably be: 1) have the file filter create a linked list of names to
- appear in the dialog, 2) insert those names into the List Manager list,
- and 3) display the list. However, by following this outline, the list
- does not get updated, as the update region for that area is empty. So, at
- some time, we need to invalidate the area that holds our list. But there
- is no convenient place in which to do that. We can't call InvalRect
- within our file filter routine, as we don't have the DialogPtr at the
- time (needed to get the bounding rectangle of the list item). Neither can
- we call InvalRect within step 2, as we are between
- BeginUpdate/EndUpdates, and calling InvalRect would be useless.
- * We can't display directories. They are not offered to the judgement of
- our file filter, and hence cannot be added to our list.
- * Key presses would have do be handled manually by our dialog hook.
-
- Considering all of these problems, another possible solution would be to
- do away with Standard File altogether and do your own non-Standard File.
- You could show a dialog (maybe even a modeless dialog) and contained a
- list of file in the selected folder. This really isn't all too
- difficult. The only disadvantage is that it may not look familiar to the
- user, and the MultiFinder hack to force the application's Open item
- wouldn't work. This process is documented in Tech Note 205.
-
- DoMultiFile
- -----------
- Points to the Dialog Filter and Dialog Hook. If the user selected the
- Open button then there is a handle containing an array of files. We
- index through this array and show some information for each file proving
- that we located the file. Once all the files have been found, then
- dispose of the handle.
-
- ListGetDlgHook
- --------------
- This routine is at the dialog item level, rather than the event level.
- Normally, double-clicking on a file name in the Standard File list
- simulates a click in the Open button. We want it to add an item to our
- multi-file list instead, so we've turned the Open button into an Add
- button and sabotaged it's functionality so that it doesn't dismiss the
- Standard File dialog. Then, we added our own Open button which tricks
- the Standard File code into thinking the normal Open button was clicked,
- and our dialog is dismissed.
-
- If there was an event (item <> 100) then handle the activating of our
- Open button and the Remove buttons. Activate the Open button if there
- are any files in our file list. Activate the Remove button if there are
- any selected files in our file list. If the Open or Cancel buttons were
- selected, then dispose of the list since the user has dismissed the
- dialog.
-
- FListDlgFilter
- --------------
- This routine's raison d'être is to handle clicks in our file list (that
- is, OUR file list at the bottom of the dialog; not SF's file list at the
- top of the dialog). If FListDlgFilter weren't around, clicks in our file
- list would be ignored. First we check to see if the mouse was clicked
- somewhere in the rectangle of our file list. If it wasn't, we return
- FALSE, and SF handles the event on it's own. If it was, call LClick and
- let the List Manager handle all the hilighting and scrolling.
-
- If there was a mouse click in our file list item, then itemHit returns
- the item number of our file list. If FListDlgFilter found out that the
- mouse click wasn't in our file list, then itemHit is unchanged and we
- return FALSE which tells the Dialog Manager to figures out which item has
- been affected by the latest event.}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION FListDlgFilter(dlgPtr: DialogPtr; VAR evt: EventRecord;
- VAR itemHit: INTEGER): Boolean;
-
- VAR
- iRect: Rect;
- clickPos: Point;
- iHandle: Handle;
- iKind: INTEGER;
- ignore: Boolean;
-
- BEGIN
- FListDlgFilter:= FALSE; {initailize result}
- IF evt.what = mouseDown THEN BEGIN {Only care about mouseDown in our list}
- clickPos:= evt.where;
- GlobalToLocal(clickPos);
- GetDialogItem(dlgPtr, kFileListItem, iKind, iHandle, iRect);
- IF PtInRect(clickPos, iRect) THEN BEGIN
- ignore:= LClick(clickPos, evt.modifiers, gListHandle);
- itemHit:= kFileListItem;
- FListDlgFilter:= TRUE;
- END;
- END;
- END;
-
-
- FUNCTION ListGetDlgHook(item: INTEGER; dlgPtr: DialogPtr): INTEGER;
-
- VAR
- iRect: Rect;
- iHandle: Handle;
- iKind: INTEGER;
- listCell: Cell; {File list cell}
-
- BEGIN
- ListGetDlgHook:= item; {default, except in special cases below}
- CASE item OF
-
- kFirstTime: BEGIN
- GetDialogItem(dlgPtr, kMultiOpenItem, iKind, iHandle, iRect);
- HiliteControl(ControlHandle(iHandle), kCntlDeactivate);
- GetDialogItem(dlgPtr, kRemoveItem, iKind, iHandle, iRect);
- HiliteControl(ControlHandle(iHandle), kCntlDeactivate);
- GetDialogItem(dlgPtr, kFileListItem, iKind, iHandle, iRect);
- CreateFGetList(dlgPtr, iRect, gListHandle);
- SetDialogItem(dlgPtr, kFileListItem, iKind, Handle(@UIPFileList), iRect);
- END; {kFirstTime}
-
- getOpen: BEGIN {old Open button means add a file}
- AddFItem(gReply.fName,GetSFCurDir, GetSFVRefNum, gListHandle);
- ListGetDlgHook:= kNoItem; {replace item with no item}
- END;
-
- kMultiOpenItem: BEGIN {new Open button means get all files}
- GetFItems(gListHandle);
- ListGetDlgHook:= getOpen; {replace item with real Open item}
- END;
-
- kRemoveItem:
- RemoveFItems(gListHandle); {remove the selected item from list}
-
- END; {CASE}
-
- IF item <> kNullModalEvt THEN BEGIN
- GetDialogItem(dlgPtr, kMultiOpenItem, iKind, iHandle, iRect);
- WITH gListHandle^^ DO
- IF dataBounds.top <> dataBounds.bottom THEN
- HiliteControl(ControlHandle(iHandle), kCntlActivate)
- ELSE
- HiliteControl(ControlHandle(iHandle), kCntlDeactivate);
- listCell:= Point(0);
- GetDialogItem(dlgPtr, kRemoveItem, iKind, iHandle, iRect);
- IF LGetSelect(TRUE, listCell, gListHandle) THEN
- HiliteControl(ControlHandle(iHandle), kCntlActivate)
- ELSE
- HiliteControl(ControlHandle(iHandle), kCntlDeactivate);
- END;
-
- IF (item = kMultiOpenItem) | (item = getCancel) THEN
- LDispose (gListHandle);
- END;
-
-
- PROCEDURE DoMultiFile;
-
- VAR
- msg1: Str255;
- typeList: SFTypeList;
- index: INTEGER;
- numOfFiles: INTEGER;
- fHPBRec: HParamBlockRec;
- err: OSErr;
- theItem: INTEGER;
- dhUPP: DlgHookUPP;
- mfUPP: ModalFilterUPP;
-
- BEGIN
- dhUPP := NewDlgHookProc(@ListGetDlgHook);
- mfUPP := NewModalFilterProc(@FListDlgFilter);
- SFPGetFile(Point(kSFTopLeft), {Location of SFDialog}
- '', {Here’s that String thing.}
- NIL, {File filter}
- kShowAllFiles, {Number of file types to display}
- typeList, {Array of file types to show}
- dhUPP, {Std File dialog hook}
- gReply, {Record for returned SF info}
- rListGetDLOG, {Resource ID of dialog}
- mfUPP); {ModalDialog filter proc}
- DisposeRoutineDescriptor(dhUPP);
- DisposeRoutineDescriptor(mfUPP);
-
- IF gReply.good THEN BEGIN
- numOfFiles:= GetHandleSize(Handle(gMultiFiles)) DIV SizeOf(FileDesc);
-
- HLock(Handle(gMultiFiles));
- FOR index:= 1 TO numOfFiles DO BEGIN
- WITH fHPBRec DO BEGIN {prepare a hParamBlock}
- ioCompletion:= NIL;
- ioNamePtr:= @gMultiFiles^^[index].name;
- ioVRefNum:= gMultiFiles^^[index].vRefNum;
- ioFDirIndex:= 0;
- ioDirID:= gMultiFiles^^[index].parID;
- END;
- err:= PBHGetFInfo(@fHPBRec, NOT kFSAsynch); {fPBRec on stack, synch only}
-
- IF err = noErr THEN BEGIN
- IUDateString(fHPBRec.ioFlMdDat, longDate, msg1);
- ParamText(gMultiFiles^^[index].name, msg1, '', '');
- theItem:= CenteredAlert(rFInfoAlert);
- END
- ELSE
- AlertUser(err, sFileSystem);
- END;
- DisposeHandle(Handle(gMultiFiles));
- END
- ELSE
- ShowCancelled;
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {Typical SFPutFile example}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {Simplest form of SFPutFile. This routine puts up a PutFile dialog with a
- prompt and suggested file name.}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE DoNormalPut;
-
- VAR
- msg: Str255; {prompt String}
- reply: SFReply;
-
- BEGIN
- GetIndString(msg, rStrMisc, sSaveAsMsg);
- SFPutFile(Point(kSFTopLeft), {location}
- msg, {prompt String}
- 'Doug', {original name}
- NIL, {dialog hook}
- reply); {record for returned values}
- IF reply.good THEN
- ShowSelection(reply.fName, '')
- ELSE
- ShowCancelled;
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {SFPPutFile with a Dialog Hook}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {This demonstration is a custom PutFile routine which adds a few items to
- the dialog box. There are two radio buttons that determine what format
- the user wants to save the information in. And there is a Static Text
- item that displays the amount of free space left on the disk. This text
- item is updated whenever we detect that the user has switched to another
- volume.
-
- DoNormalPPut
- ------------
- This routine calls SFPPutFile. Note the extra 'P' in the name. This
- Toolbox call allows us to specify a customized dialog box, and dialog
- hook that handles hits on special items. There is a global string used
- to show what the file format will be to the user.
-
- MySFPutDlgHook
- --------------
- A routine that is called to handle the non-standard items in our dialog
- box. These items are:
-
- * The control of two radio buttons that affect the setting of a global
- variable used to determine what format the file will be saved in
- * UserItem that displays the amount of free space on the current volume.
-
- The dialog hook is also used to perform some special initialization on
- the items in the dialog box. We initialize our radio buttons, change the
- text in the save button, and prepare the user item by initializing the
- routine that will draw it and getting the text that will appear in it.
-
- Finally, this routine will force the user item to be drawn once at
- initialization time, and when ever the user clicks the Drive button. By
- invalidating the user item rectangle, it causes the drawing procedure to
- update according to the current volume which is set by Standard File.
-
- DrawFreeSpace
- -------------
- This is a user item's drawing procedure. It is used to display the
- amount of free space left on the disk. All we need to do is update the
- text in our userItem.}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE DrawFreeSpace(theWindow: WindowPtr; itemNo: INTEGER);
-
- VAR
- convert: TwoIntsMakesALong;
- myStr1, myStr2: Str255;
- iHandle: Handle;
- iKind: INTEGER;
- iRect: Rect;
- err: OSErr;
- space: LONGINT;
-
- BEGIN
- space:= GetKFreeSpace(GetSFVRefNum);
- IF space < 0 THEN
- myStr1:= '??' {we couldn't get the free space size!}
- ELSE
- NumToString(space, myStr1);
- GetIndString(myStr2, rStrMisc, sKFree); {Text to be added in our user item.}
- myStr1:= CONCAT(myStr1, myStr2);
- GetDialogItem(DialogPtr(theWindow), kSFPPutFreeSpace, iKind, iHandle, iRect);
- TETextBox(@myStr1[1], LENGTH(myStr1), iRect, teJustCenter);
- END;
-
-
- FUNCTION MySFPutDlgHook(item: INTEGER; dlgPtr: DialogPtr): INTEGER;
-
-
- VAR
- saveStr: Str255; {replacement name for Save button}
- iRect: Rect;
- iHandle: Handle;
- iKind: INTEGER;
-
- BEGIN
- MySFPutDlgHook:= item;
- CASE item OF
-
- kFirstTime: BEGIN
- SetRadioButton(dlgPtr, kSFPPutTextBtn, kCntlOff);
- GetDialogItem(dlgPtr, kSFPPutNormalBtn, iKind, iHandle, iRect);
- SetControlValue(ControlHandle(iHandle), kCntlOn);
- GetControlTitle(ControlHandle(iHandle), gPutFormat);
- GetDialogItem(dlgPtr, kSFPPutFreeSpace, iKind, iHandle, iRect);
- SetDialogItem(dlgPtr, kSFPPutFreeSpace, iKind, Handle(@DrawFreeSpace), iRect);
- InvalRect(iRect); {force item to update}
- END;
-
- getDrive: BEGIN
- GetDialogItem(dlgPtr, kSFPPutFreeSpace, iKind, iHandle, iRect);
- InvalRect(iRect);
- END;
-
- kSFPPutNormalBtn: BEGIN
- IF NOT gNormal THEN BEGIN
- SetRadioButton(dlgPtr, kSFPPutTextBtn, kCntlOff);
- GetDialogItem(dlgPtr, kSFPPutNormalBtn, iKind, iHandle, iRect);
- SetControlValue(ControlHandle(iHandle), kCntlOn);
- GetControlTitle(ControlHandle(iHandle), gPutFormat);
- gNormal:= TRUE; {change our flag accordingly}
- END;
- END;
-
- kSFPPutTextBtn: BEGIN
- IF gNormal THEN BEGIN
- SetRadioButton(dlgPtr, kSFPPutNormalBtn, kCntlOff);
- GetDialogItem(dlgPtr, kSFPPutTextBtn, iKind, iHandle, iRect);
- SetControlValue(ControlHandle(iHandle), kCntlOn);
- GetControlTitle(ControlHandle(iHandle), gPutFormat);
- gNormal:= FALSE; {change our flag accordingly}
- END;
- END;
-
- END; {CASE}
- END;
-
-
- PROCEDURE DoNormalPPut;
-
- VAR
- msg: Str255; {prompt String}
- reply: SFReply;
- dhUPP: DlgHookUPP;
-
- BEGIN
- gNormal:= TRUE;
- GetIndString(msg, rStrMisc, sSaveAsMsg);
-
- dhUPP := NewDlgHookProc(@MySFPutDlgHook);
- SFPPutFile(Point(kSFTopLeft), {location}
- msg, {prompt String}
- 'Doug', {original name}
- dhUPP, {dialog hook}
- reply, {record for returned values}
- rSFPPutFileDLOG, {ID of custom Dialog}
- NIL); {ModalDialog filterProc}
- DisposeRoutineDescriptor(dhUPP);
-
- IF reply.good THEN
- ShowSelection(reply.fName, gPutFormat)
- ELSE
- ShowCancelled;
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {DoForceDirectory}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {This is a quick sample that shows how to set the initial directory that
- Standard File comes up with. Basically, this is done by storing
- appropriate values into SFSaveDisk and CurDirStore. In this example, I
- force the directory to be the System Folder of the boot volume as
- specified by SysEnvirons. Since I like to be user friendly, I save and
- restore the current folder. So the next time Standard File is called it
- will be exactly where the user left it.}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE DoForceDirectory;
-
- VAR
- msg: Str255; {prompt String}
- typeList: SFTypeList;
- oldDirStore: LONGINT;
- dirID: LONGINT;
- procID: LONGINT;
- vRefNum: INTEGER;
- oldVRefNum: INTEGER;
- reply: SFReply;
- err: OSErr;
-
- BEGIN
- oldDirStore:= GetSFCurDir; {save old folder and volume}
- oldVRefNum:= GetSFVRefNum;
- err:= GetWDInfo(gMac.sysVRefNum, vRefNum, dirID, procID);
- IF err = noErr THEN BEGIN
- SetSFCurDir(dirID);
- SetSFVRefNum(vRefNum);
- GetIndString(msg, rStrMisc, sSaveAsMsg);
- SFPutFile(Point(kSFTopLeft), {location}
- msg, {prompt String}
- 'Doug', {original name}
- NIL, {dialog hook}
- reply); {record for returned values}
- IF reply.good THEN
- ShowSelection(reply.fName, '')
- ELSE
- ShowCancelled;
- SetSFCurDir(oldDirStore); {restore old folder and volume}
- SetSFVRefNum(oldVRefNum);
- END
- ELSE
- AlertUser(err, sStandardErr);
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {DoPutListsFile}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {This sample demonstrates putting a List Manager list into an SFPPutFile
- dialog, describes a problem that can occur by doing so, and shows one
- solution to the problem. Putting another list into a Standard File
- dialog can be useful for a number of reasons. While *I* can't think of a
- reason I would want to put a second list into a PutFile dialog, it is
- possible that you may wish to. The best example that I could think of is
- a list of file types for the user to choose. However, there is a subtle
- problem that arises if you do: in the following example, if were to
- dispose of the list when the user clicked on Save, we may hit a snag. It
- is possible for the user to specify the name of a file that already
- exists. When that happens, s/he is presented with another dialog that
- asks if they are sure of what they are doing. At that point, the user
- could press Cancel, and return us to the PutFile dialog, SANS our second
- list! Neither can we defer disposing of list until Standard File is done
- and returns to our application, as the Dialog box we were using is gone,
- and the List Manager tries to perform an InvalRect on a non-existent
- window when it erases its list and that will crash.
-
- There are two solutions to this problem:
-
- * Implement the list as a Custom Control. Then, when it comes time to
- close the dialog box, the custom control will get called with a dispCntl
- message. It can take that opportunity to call LDispose. This technique
- is not presented here, as it is more appropriate for a Custom Control
- Sample program. However, it is the suggested way to proceed.
-
- * Ensure that the confirmation dialog box never comes up under Standard
- File's control. By calling it ourself, we know what the user chooses. If
- the user presses OK, we delete the offending file, dispose of our list,
- and return to Standard File. If the user presses Cancel, we change the
- click on the Save button into a NULL event. This is the algorithm we use
- below.
-
- There is a major disadvantage with this last approach, however. With it,
- we cannot implement a safe saving procedure. Normally, the best way to
- save a file (disk space permitting), is to save the data to a temporary
- file, delete the original, and then rename the temporary file to that of
- the original. However, with the second approach, the file is deleted well
- before it is safe to do so.
-
- This example *could* be reworked to avoid this problem. For instance,
- instead of being deleted, the offending file could be renamed to
- something else or moved to another directory.
-
- DoPutListsFile
- --------------
- We force the current folder of Standard File to be the location of the
- temporary file that we've created. This is to attempt to make the
- Existing File? dialog show up. Being a user friendly application, we
- save and restore the user's current folder. If the user did happen to
- select to replace the file, we create a new one for the next time.
-
- PutListsDlglFilter
- ------------------
- The Dialog Filter is used to handle clicks in the List Manager list.
-
- PutListsDlgHook
- ---------------
- The initialization case is used to create a list of file types. It also
- jams in the name of the temporary file that we are using. This is to try
- and force the "Replace file?" dialog to show up. This is only part of
- the demonstration, not something that programs would really be doing. If
- the user attempts to save the file with the name of an existing file,
- then the Existing File alert needs to be shown. This is centered within
- the main dialog alert properly according to the Human Interface Guidelines.
-
- LNPFDrawList
- ------------
- Dialog Manager's user item drawing procedure to update the List Manager
- list.}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE LNPFDrawList(theWindow: WindowPtr; item: INTEGER);
-
- BEGIN
- IF item = kListItem THEN BEGIN
- LUpdate(theWindow^.visRgn, gListHandle);
- DrawItemFrame(theWindow, kListItem);
- END;
- END;
-
-
- FUNCTION PutListsDlgHook(item: INTEGER; dlgPtr: DialogPtr): INTEGER;
-
- VAR
- name: Str63;
- fndrInfo: FInfo;
- dLoc: Point;
- iRect: Rect;
- alertRect: Rect;
- iHandle: Handle;
- alertHandle: AlertTHndl;
- parID: LONGINT;
- vRefNum: INTEGER;
- replaceItem: INTEGER;
- iKind: INTEGER;
- choice: INTEGER;
- err: OSErr;
-
- BEGIN
- replaceItem:= item;
- vRefNum:= GetSFVRefNum;
- parID:= GetSFCurDir;
- CASE replaceItem OF
-
- kFirstTime: BEGIN
- GetDialogItem(dlgPtr, kListItem, iKind, iHandle, iRect);
- CreateFPutList(dlgPtr, iRect, gListHandle, gPutFormat);
- SetDialogItem(dlgPtr, kListItem, iKind, Handle(@LNPFDrawList), iRect);
- name:= GetTempFileName;
- GetDialogItem(dlgPtr, putName, iKind, iHandle, iRect);
- SetDialogItemText(iHandle, name);
- TESetSelect(0, LENGTH(name), DialogPeek(dlgPtr)^.textH);
- END;
-
- putSave: BEGIN
- err:= HGetFInfo(vRefNum, parID, gReply.fName, fndrInfo);
- IF err = noErr THEN BEGIN {if GetFInfo = noErr, then}
- {this file already exists.}
-
- alertHandle:= AlertTHndl(Get1Resource('ALRT', kExistingFileALRT));
- HNoPurge(Handle(alertHandle)); {keep is around}
- alertRect:= alertHandle^^.boundsRect;
- GlobalToLocal(alertRect.topLeft);
- GlobalToLocal(alertRect.botRight);
- PositionTwoRects(dlgPtr^.portRect, alertRect, FixRatio(1,2), FixRatio(1,3));
- LocalToGlobal(alertRect.topLeft);
- LocalToGlobal(alertRect.botRight);
- alertHandle^^.boundsRect:= alertRect;
- ParamText(gReply.fName, '', '', '');
- choice:= Alert(kExistingFileALRT, NIL);
- HPurge(Handle(alertHandle)); {let it go}
- IF choice = kReplaceItem THEN
- err:= HDelete(vRefNum, parID, gReply.fName)
- ELSE {Change "Save" into null event}
- replaceItem:= kNullModalEvt;
- END;
- END;
-
- END; {CASE}
- IF (replaceItem = putSave) | (replaceItem = putCancel) THEN
- DisposeFPutList(gListHandle, gPutFormat);
- PutListsDlgHook:= replaceItem;
- END;
-
-
- FUNCTION PutListsDlglFilter(dlgPtr: DialogPtr; VAR evt: EventRecord;
- VAR itemHit: INTEGER): BOOLEAN;
-
- VAR
- localPt: Point;
- ignore: BOOLEAN;
-
- BEGIN
- IF evt.what = mouseDown THEN BEGIN
- localPt:= evt.where;
- GlobalToLocal(localPt);
- ignore:= LClick(localPt, evt.modifiers, gListHandle);
- END;
- PutListsDlglFilter:= FALSE;
- END;
-
-
- PROCEDURE DoPutListsFile;
-
- VAR
- msg: Str255; {prompt String}
- fName: Str63;
- oldDirStore: LONGINT;
- oldVRefNum: INTEGER;
- err: INTEGER;
- dhUPP: DlgHookUPP;
- mfUPP: ModalFilterUPP;
-
- BEGIN
- oldDirStore:= GetSFCurDir; {save old folder and volume}
- oldVRefNum:= GetSFVRefNum;
- SetSFCurDir(gAppParID); {force to our app folder}
- SetSFVRefNum(gAppVRefNum);
-
- GetIndString(msg, rStrMisc, sSaveAsMsg);
-
- dhUPP := NewDlgHookProc(@PutListsDlgHook);
- mfUPP := NewModalFilterProc(@PutListsDlglFilter);
- SFPPutFile(Point(kSFTopLeft), {location}
- msg, {prompt String}
- '', {original name}
- dhUPP, {dialog hook}
- gReply, {record for returned values}
- rPutListsFileDLOG, {ID of custom Dialog}
- mfUPP); {ModalDialog filterProc}
- DisposeRoutineDescriptor(dhUPP);
- DisposeRoutineDescriptor(mfUPP);
-
- IF gReply.good THEN BEGIN
- ShowSelection(gReply.fName, gPutFormat);
- fName:= GetTempFileName;
- IF gReply.fName = fName THEN
- err:= HCreate(gAppVRefNum, gAppParID, fName, rAppSignature, 'TEMP');
- END
- ELSE
- ShowCancelled;
-
- SetSFCurDir(oldDirStore); {restore old folder and volume}
- SetSFVRefNum(oldVRefNum);
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {DoPutOptions with a Dialog Hook}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {This demonstration add a new button to the SFPutFile dialog. This button
- is used to bring up another dialog used to show a set of radio buttons.
- The options dialog allows the user to select a file type, which will be
- used to as the file format when saving the file.
-
- DoPutOptions
- ------------
- Very simple version of creating a SFPPutFile dialog with a Dialog Hook
- that checks for the addition button being selected.
-
- PutOptionsDlgHook
- -----------------
- If the Options button is selected, then we bring up the options dialog.
- The default button is outlined and we wait for the OK or Cancel item.
- The new format is the item that was hit within the options dialog. The
- title of this item is displayed to the user in the SFPutFile dialog
- showing the new file format.}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION PutOptionsDlgHook(item: INTEGER; dlgPtr: DialogPtr): INTEGER;
-
- VAR
- title: Str255;
- dlogTemplate: DialogTHndl;
- subDlgPtr: DialogPtr;
- dlogRect: Rect;
- iRect: Rect;
- iHandle: Handle;
- itemHit: INTEGER;
- iKind: INTEGER;
- newItem: INTEGER;
- oldPort: GrafPtr;
-
- BEGIN
- PutOptionsDlgHook:= item;
- CASE item OF
-
- kOptionsButton: BEGIN
- GetPort(oldPort);
-
- dlogTemplate := DialogTHndl(Get1Resource('DLOG', rOptionsSubDLOG));
- dlogRect:= dlogTemplate^^.boundsRect;
- GlobalToLocal(dlogRect.topLeft);
- GlobalToLocal(dlogRect.botRight);
- PositionTwoRects(dlgPtr^.portRect, dlogRect, FixRatio(1,2), FixRatio(1,3));
- LocalToGlobal(dlogRect.topLeft);
- LocalToGlobal(dlogRect.botRight);
- dlogTemplate^^.boundsRect:= dlogRect;
-
- subDlgPtr:= GetNewDialog(rOptionsSubDLOG, NIL, WindowPtr(-1));
- GetDialogItem(subDlgPtr, OK, iKind, iHandle, iRect);
- OutlineButton(ControlHandle(iHandle));
- SetRadioButton(subDlgPtr, kDefaultFormat, kCntlOn);
- GetDialogItem(subDlgPtr, kFrameItem, iKind, iHandle, iRect);
- SetDialogItem(subDlgPtr, kFrameItem, iKind, Handle(@DrawItemFrame), iRect);
- newItem:= kDefaultFormat;
- REPEAT
- ModalDialog(NIL, itemHit);
- IF (itemHit <> newItem) & (itemHit > cancel) THEN BEGIN
- SetRadioButton(subDlgPtr, newItem, kCntlOff);
- SetRadioButton(subDlgPtr, itemHit, kCntlOn);
- newItem:= itemHit;
- END;
- UNTIL (itemHit = OK) | (itemHit = Cancel);
-
- IF itemHit = OK THEN BEGIN
- GetDialogItem(subDlgPtr, newItem, iKind, iHandle, iRect);
- GetControlTitle(ControlHandle(iHandle), gPutFormat);
- GetDialogItem(dlgPtr, kFormatString, iKind, iHandle, iRect);
- SetDialogItemText(iHandle, gPutFormat);
- END;
- DisposeDialog(subDlgPtr);
- SetPort(oldPort);
- END;
-
- END; {CASE}
- END;
-
-
- PROCEDURE DoPutOptions;
-
- VAR
- msg: Str255; {prompt String}
- reply: SFReply;
- dhUPP: DlgHookUPP;
-
- BEGIN
- GetIndString(msg, rStrMisc, sSaveAsMsg);
-
- dhUPP := NewDlgHookProc(@PutOptionsDlgHook);
- SFPPutFile(Point(kSFTopLeft), {location}
- msg, {prompt String}
- 'Doug', {original name}
- dhUPP, {dialog hook}
- reply, {record for returned values}
- rOptionsDLOG, {ID of custom Dialog}
- NIL); {ModalDialog filterProc}
- DisposeRoutineDescriptor(dhUPP);
-
- IF reply.good THEN
- ShowSelection(reply.fName, gPutFormat)
- ELSE
- ShowCancelled;
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {DoIdleUpdates}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {This demonstrates two things: how to handle idle events and how to
- determine the currently selected file. There is a problem that Standard
- File has when updates are pending in background windows of the current
- application partition. Standard File calls ModalDialog at the heart of
- its main loop. ModalDialog calls GetNextEvent, and then calls a
- filterProc internal to Standard File. This filterProc performs some
- processing on the event, (like handling hits on the filename list, the
- Current Directory button, and the Disk Name Icon), and then calls the
- user filterProc specified in SFPPutFile or SFPGetFile calls. Another one
- of the things that the internal filterProc does is look for a nullEvent.
- When one is found, the filterProc returns 100 as the "itemHit". This
- forces ModalDialog to return to Standard File, which in turn can now call
- the Dialog Hook with the manufactured item number 100, indicating that
- idle time processing can be performed.
-
- The problem occurs when there are updates pending in windows open in the
- current application's partition. These updates will 'clog' the event
- queue. When ModalDialog calls GetNextEvent, it will get the update event.
- However, there is no way for ModalDialog to respond to them. It therefore
- passes the event off to the filterProc, which is Standard File's internal
- one. This filterProc doesn't know how to handle it either. Normally, the
- event would be ignored at this point, and the update event would be
- unresolved. Control returns back to ModalDialog, which calls GetNextEvent
- again, and gets the another update event since it wasn't handled. Null
- events will not get returned by GetNextEvent and, hence, the dialog hook
- will never get called with itemHit=100.
-
- This situation can be solved by providing your own filterProc procedure
- to be called after Standard File's's internal Dialog Filter. It will be
- this routine's responsibility to check for update events, and handle them
- appropriately. Usually, this would mean that the update procedure within
- the application would be called, and the update could be cleared by
- BeginUpdate/EndUpdate. However, the filterProc could also just handle
- update events in the same way that Standard File's filterProc handles
- null events. This is done by returning 100 in the ItemHit parameter and
- TRUE as the function result. This is the approach taken by the sample
- below.
-
- DoIdleUpdates
- -------------
- Create a window with a non-empty update region then call up SFPGetFile
- with a Dialog Hook and Dialog Filter.
-
- IdleDlgFilter
- -------------
- If we get an update event for a window other than the dialog box, change
- it to a null event, and tell ModalDialog that we handled it. This allows
- our Dialog Hook idle time.
-
- IdleDlgHook
- -----------
- All it does is wait around for null events and draws the current time
- when it gets one. Note that this routine will NOT get called with
- item=100 if we did not have the IdleDlgFilter below. Determining if
- there is an item selected is straight forward, once you know the secret.
- During the idle event the SFReply record will contain the file name if a
- file is selected. (It's also worth noting here that if the fName field
- is NIL, this could mean a folder is selected. If this were the case then
- fType will be the DirID of that folder! Pretty cool huh?)
-
- Given that we can determine the name of the file, it's easy to find out
- the size. So, just for fun we'll show the file's size on the disk to the
- user. ioFlPyLen is the physical size of the file on the device. Logical
- size would be the actual size of the file in memory. In other words,
- GetEOF returns the logical size. The data and resource fork's physical
- sizes are added together to give the true size of the entire file on the
- volume.}
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- FUNCTION IdleDlgHook(item: INTEGER; dlgPtr: DialogPtr): INTEGER;
-
- VAR
- newStr: Str255;
- itemStr: Str255;
- conCatStr: Str255;
- hPBRec: HParamBlockRec;
- dateTime: LONGINT;
- totalSize: LONGINT; {size of file in k bytes}
- iRect: Rect;
- iHandle: Handle;
- iKind: INTEGER;
- err: OSErr;
-
- BEGIN
- IdleDlgHook:= item;
- IF item = kNullModalEvt THEN BEGIN
- GetDateTime(dateTime);
- IUTimeString(dateTime, kWantSeconds, newStr);
- GetDialogItem(dlgPtr, kTimeItem, iKind, iHandle, iRect);
- GetDialogItemText(iHandle, itemStr);
- IF newStr <> itemStr THEN
- SetDialogItemText(iHandle, newStr);
-
- GetDialogItem(dlgPtr, kSizeItem, iKind, iHandle, iRect);
- GetDialogItemText(iHandle, itemStr);
- IF gReply.fName <> '' THEN BEGIN
- WITH hPBRec DO BEGIN
- ioCompletion:= NIL;
- ioNamePtr:= @gReply.fName;
- ioVRefNum:= GetSFVRefNum;
- ioFDirIndex:= 0;
- ioDirID:= GetSFCurDir;
- END;
- err:= PBHGetFInfo(@hPBRec, NOT kFSAsynch);
- IF err = noErr THEN BEGIN
- newStr:= gReply.fName;
- newStr[0]:= CHR(INTEGER(newStr[0]) + 1);
- newStr[LENGTH(newStr)]:= ' ';
-
- totalSize:= (hPBRec.ioFlPyLen + hPBRec.ioFlRPyLen) DIV 1024;
- NumToString(totalSize, conCatStr);
- newStr:= CONCAT(newStr, conCatStr);
-
- GetIndString(conCatStr, rStrMisc, sFileSize);
- newStr:= CONCAT(newStr, conCatStr);
-
- IF newStr <> itemStr THEN
- SetDialogItemText(iHandle, newStr);
- END
- ELSE
- SetDialogItemText(iHandle, '??');
- END
- ELSE
- SetDialogItemText(iHandle, '');
- END;
- END;
-
-
- FUNCTION IdleDlgFilter(dlgPtr: DialogPtr; VAR evt: EventRecord;
- VAR itemHit: INTEGER): BOOLEAN;
-
- BEGIN
- IdleDlgFilter:= FALSE;
- IF (evt.what = updateEvt) & (DialogPtr(evt.message) <> dlgPtr) THEN BEGIN
- itemHit:= kNullModalEvt;
- IdleDlgFilter:= TRUE;
- END;
- END;
-
-
- PROCEDURE DoIdleUpdates;
-
- VAR
- typeList: SFTypeList;
- window: WindowPtr;
- dhUPP: DlgHookUPP;
- mfUPP: ModalFilterUPP;
-
- BEGIN
- window:= GetNewWindow(rUpdateWindow, NIL, Pointer(-1));
-
- dhUPP := NewDlgHookProc(@IdleDlgHook);
- mfUPP := NewModalFilterProc(@IdleDlgFilter);
- SFPGetFile(Point(kSFTopLeft), {location}
- '', {vestigial String}
- NIL, {file filter}
- kShowAllFiles, {numtypes}
- typeList, {array to types to show}
- dhUPP, {Dialog Hook}
- gReply, {record for returned values}
- rGetIdleUpdates, {ID of Normal Dialog}
- mfUPP); {ModalDialog filterProc}
- DisposeRoutineDescriptor(dhUPP);
- DisposeRoutineDescriptor(mfUPP);
-
- CloseWindow(window);
- IF gReply.good THEN
- ShowSelection(gReply.fName, '')
- ELSE
- ShowCancelled;
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {DoRememberFile}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-
- {This demonstrates how to ask the user to locate a file and then how the
- application should remember it. The proper way to do this is use the
- volume name, directory ID and file name. Do not use the vRefNum. It
- could be a working directory which will change, probably as soon as your
- application quits. It could also be a real vRefNum but that could change
- as soon as the user re-boots. Directory ID do not change unless the user
- deletes the folder. No matter where the user places that folder or if
- they rename it, the DirID will be the same.
-
- Real applications should have a preference file. This would store the
- users options, such as the location of a file. For the purposes of this
- demonstration, we're using the application's resource fork to hold the
- file's location. Some potential problem with the method is if the file
- is locked, the volume is lock, or the application is on a shared volume
- by many different users.
-
- RecallFile
- ----------
- Get the application defined resource and see if the file it describes can
- be used to locate a file. Start by changing the volume name into a
- vRefNum. If this works, then a volume with that name is mounted. Then
- we attempt to show a dialog that displays some information to the user
- proving we've located their file. If we could not locate the file, then
- we ask them to locate another one and we'll try again next time.
-
- DoRememberFile
- --------------
- Call upon SFGetFile to ask the user to select a file. Then change the
- working directory number returned in vRefNum into a real vRefNum and
- DirID. The real vRefNum is then used to option the volume name. This
- information along with the file name is saved into an application defined
- resource and saved.}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE DoRememberFile;
-
- VAR
- volName: Str27;
- typeList: SFTypeList;
- reply: SFReply;
- myWDPBRec: WDPBRec;
- rememberFile: FileDescHdl;
- err: OSErr;
-
- BEGIN
- SFGetFile(Point(kSFTopLeft), {location}
- '', {prompt String}
- NIL, {file filter}
- kShowAllFiles, {numtypes}
- typeList, {array to types to show}
- NIL, {dialog hook}
- reply); {record for returned values}
- IF reply.good THEN BEGIN
- ShowSelection(reply.fName, '');
- volName:= ''; {best to empty volName}
- WITH myWDPBRec DO BEGIN {set up WDPBRec}
- ioNamePtr:= @volName;
- ioVRefNum:= reply.vRefnum;
- ioWDIndex:= 0;
- ioWDProcID:= 0;
- END;
- err:= PBGetWDInfo(@myWDPBRec, NOT kFSAsynch);
- IF err = noErr THEN BEGIN
- rememberFile:= FileDescHdl(Get1Resource('FILE', rMemorizedFile));
- IF rememberFile <> NIL THEN BEGIN
- rememberFile^^.name:= reply.fName;
- rememberFile^^.parID:= myWDPBRec.ioWDDirID;
- rememberFile^^.volName:= volName;
- ChangedResource(Handle(rememberFile));
- WriteResource(Handle(rememberFile));
- END
- ELSE
- AlertUser(ResError, sResErr);
- END
- ELSE
- AlertUser(err, sFileSystem);
- END
- ELSE
- ShowCancelled;
- END;
-
-
- PROCEDURE RecallFile;
-
- VAR
- rememberFile: FileDescHdl;
- vRefNum: INTEGER;
- err: OSErr;
-
- BEGIN
- rememberFile:= FileDescHdl(Get1Resource('FILE', rMemorizedFile));
- IF rememberFile <> NIL THEN BEGIN
- HLock(Handle(rememberFile));
- WITH rememberFile^^ DO BEGIN
- err:= GetVol(@volName, vRefNum);
- IF err = noErr THEN
- err:= ShowFoundFile(name, parID, vRefNum);
- END;
- HUnlock(Handle(rememberFile));
- IF err <> noErr THEN BEGIN {catch any file errors}
- AlertUser(noErr, sLostFile);
- DoRememberFile;
- END;
- END
- ELSE
- AlertUser(ResError, sResErr);
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE DoCommand(mResult: LONGINT);
-
- {Performed the selected menu command}
-
- VAR
- name: Str255;
- oldPort: GrafPtr;
- theItem: INTEGER;
- theMenu: INTEGER;
- daRefNum: INTEGER;
- ignore: BOOLEAN;
-
- BEGIN
- theItem:= LoWord(mResult);
- theMenu:= HiWord(mResult);
-
- CASE theMenu OF
-
- mApple:
- IF theItem = iAboutMe THEN
- DoAbout
- ELSE BEGIN
- GetMenuItemText(GetMenuHandle(mApple), theItem, name);
- GetPort(oldPort);
- daRefNum:= OpenDeskAcc(name);
- SetPort(oldPort);
- END;
-
- mFile:
- CASE theItem OF
- iQuit:
- Terminate;
- END;
-
- mEdit:
- ignore:= SystemEdit(theItem - 1);
-
- mSFExamples: BEGIN
- CASE theItem OF
- iNormalGet:
- ShowPath;
- iNormalPGet:
- DoNormalPGet;
- iFileFilter:
- DoFFilter;
- iGetDirectory:
- DoGetDirectory;
- iMultiFile:
- DoMultiFile;
- iNormalPut:
- DoNormalPut;
- iNormalPPut:
- DoNormalPPut;
- iForceDirectory:
- DoForceDirectory;
- iPutListsFile:
- DoPutListsFile;
- iPutOptions:
- DoPutOptions;
- iIdleUpdates:
- DoIdleUpdates;
- iRememberFile:
- DoRememberFile;
- END; {CASE}
- END;
- END;
- HiliteMenu(0); {unhighlight all menus}
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- PROCEDURE EventLoop;
-
- {Chances are, you've seen an event loop before so I'm not going to
- comment about it.}
-
- CONST
- osEvt = app4Evt; {defined in post MPW 3.0 Events.p}
- suspendResumeMessage = 1; {defined in post MPW 3.0 Events.p}
- kGetHighByte = 24; {24 bits (three bytes) to shift right}
- kResumeMask = 1; {resume event is low bit of message}
-
- VAR
- evt: EventRecord;
- whichWindow: WindowPtr; {used for FindWindow}
- deskPart: INTEGER; {result from FindWindow}
- err: OSErr;
- haveEvent: BOOLEAN; {TRUE if interesting event}
-
- BEGIN
- REPEAT
- IF gHasWaitNextEvent THEN
- haveEvent:= WaitNextEvent(everyEvent, evt, MAXLONGINT, NIL)
- ELSE BEGIN
- SystemTask;
- haveEvent:= GetNextEvent(everyEvent, evt);
- END;
-
- deskPart:= FindWindow(evt.where, whichWindow);
- IF deskPart <> inSysWindow THEN
- SetCursor(qd.arrow);
-
- IF haveEvent THEN BEGIN
- CASE evt.what OF
-
- mouseDown:
- CASE deskPart OF
- inSysWindow:
- SystemClick(evt, whichWindow);
- inMenuBar:
- DoCommand(MenuSelect(evt.where));
- END;
-
- keyDown, autoKey:
- IF BAND(evt.modifiers, cmdKey) <> 0 THEN
- DoCommand(MenuKey(CHR(BAND(evt.message, charCodeMask))));
-
- diskEvt: {Call DIBadMount in response to a diskEvt}
- IF HiWord(evt.message) <> noErr THEN
- err:= DIBadMount(Point(kSFTopLeft), evt.message);
-
- osEvt: BEGIN
- CASE BSR(evt.message, kGetHighByte) OF
- suspendResumeMessage: BEGIN
- IF BAND(evt.message, kResumeMask) <> 0 THEN BEGIN
- gInBackground:= FALSE;
- END
- ELSE gInBackground:= TRUE; {suspend event}
- END; {suspendResumeMessage}
- END;
- END;
-
- END; {CASE}
- END;
- UNTIL FALSE; {loop forever, quit through Terminate}
- END;
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Initialize}
-
- PROCEDURE Initialize;
-
- {Due to some bug in older versions of A/UX regarding working directories,
- I check which verison we might be running if any. If A/UX isn't running,
- then the version will be zero. Other wise it will be set to the major
- version number, such as 2 for 2.0. I want to make sure that enough
- memory is free for my application to run. It is possible that user may
- have adjusted the SIZE resource to too small a setting or for some other
- reason the application started up in a very small memory partition. I
- preform this check after initializing all the Toolbox and the basic
- features of this application. The pentultimate thing is to save the
- default directory and volume that the application is launched from in two
- globals and create a temporary file. This file is only used for
- demonstration purposes. Finally, we check if the file we last remembered
- can be found.}
-
- VAR
- fName: Str63;
- event: EventRecord;
- menuBar: Handle;
- apParam: Handle;
- ignoreError: OSErr;
- count: INTEGER;
- auxVersion: INTEGER;
- appResRef: INTEGER;
- err: OSErr;
- ignoreResult: Boolean;
-
- BEGIN
- gInBackground:= FALSE; {we’ll be in the foreground soon}
- gHasWaitNextEvent := TrapExists(_WaitNextEvent);
-
- FOR count:= 1 TO kNumberOfMasters DO {allocate master pointer blocks}
- MoreMasters;
- InitGraf(@qd.thePort); {init managers, yawn...}
- InitFonts;
- InitWindows;
- InitMenus;
- TEInit;
- InitDialogs(NIL);
- InitCursor;
-
- {This code is necessary to pull the application into the foreground. I use
- EventAvail because I don’t want to remove any events the user may have
- done, such as typing ahead. Until the application has made a few calls (3
- seems to be the magic number) to the Event Manager, MultiFinder keeps me
- in the background. Splashscreens and Alerts will remain in a background
- layer until we get a few events. This is documented in Tech Note #180.}
-
- FOR count:= 1 TO kBroughtToFront DO
- ignoreResult:= EventAvail(everyEvent, event);
-
- auxVersion:= GetAUXVersion;
- IF (auxVersion < kMinAUXVersion) & (auxVersion > 0) THEN
- EmergencyExit(sOldAUX);
-
- ignoreError:= SysEnvirons(kSysEnvironsVersion, gMac);
- IF (gMac.machineType < envMachUnknown) THEN {we use HFS}
- EmergencyExit(sNoHFS);
-
- menuBar := GetNewMBar(rMenuBar); {read menus into menu bar}
- IF menuBar = NIL THEN
- EmergencyExit(sNoMenus);
- SetMenuBar(menuBar); {install menus}
- DisposeHandle(menuBar);
- AppendResMenu(GetMenuHandle(mApple), 'DRVR'); {add DA names to Apple menu}
- DrawMenuBar;
-
- IF FailLowMemory(kMinSpace) THEN
- EmergencyExit(sLowMemory);
-
- GetAppParms(gAppName, appResRef, apParam);
- err:= MyHGetVol(NIL, gAppVRefNum, gAppParID);
- err:= HCreate(gAppVRefNum, gAppParID, GetTempFileName, rAppSignature, 'TEMP');
-
- RecallFile; {what was that file?}
- END;
-
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- PROCEDURE _DataInit; EXTERNAL;
-
- {This routine is contained in the MPW runtime library. It will be placed
- into the code segment used to initialize the A5 globals. This external
- reference to it is done so that we can unload that segment, named %A5Init.}
-
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MAIN PROGRAM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {$S Main}
-
- BEGIN
- UnLoadSeg(@_DataInit); {note: _DataInit must not be in Main!}
-
- {If you have stack requirements that differ from the default, then you
- could use SetApplLimit to increase StackSpace at this point, before
- calling MaxApplZone.}
-
- MaxApplZone; {expand the heap so code segments load at
- the top}
- Initialize; {initialize the program}
- UnLoadSeg(@Initialize); {note: Initialize must not be in Main!}
- EventLoop; {call the main event loop}
- END.
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MAIN PROGRAM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
- {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}
-