{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} { GENERIC DIALOGS `````````````````````````` A Generic Utilities unit, for use by any program. Copyright İ by David Sinclair, 1990 ­ 2001. I am releasing these units to the Pascal community. Feel free to use them in whole or part in your Pascal programs. You are also welcome to modify these units to suit your needs. If you wish to re-distribute the sources with your changes, please clearly indicate that you have changed them. In all cases, you must leave these comments and the copyright notice intact. If you use a significant portion of these units, I would appreciate acknowledgement in your About dialog and/or documentation, e.g. ³Dejal Generic Utilities copyright İ by David Sinclair, 1990 - 2001.² Iıd appreciate it if you also e-mail me at if you find these units useful. If you have any questions about these units, you can e-mail me at that address and I will do my best to help, time permitting. However, these units are provided ³as is² and I do not guarantee their reliablity or suitability for any particular purpose. These units have been used extensively in my Dejal shareware and freeware products over the years. Most of the code was written many years ago, and the code and style may not be optimal in all cases, but unless otherwise noted all routines have been used in released software, so should work as described. Please visit and try out Dejal QuickEncrypt and/or my other shareware products. If you want to show your appreciation for these units financially, registrations for my shareware are always welcome! Or you can make a donation to me via my online order form: . I hope you find these units useful, and good luck in your Pascal endeavors! - David Sinclair, Dejal * * * UNIT HISTORY: (Reverse chronology) Start ­ finish dates: Comments / changes: 27 October 2001 Public release as source code. 14 May 1999 Added the DlogInitialise routine. 19 April 1999 Added the DlogItemIsButton routine. 29 March 1998 Modified DlogFindKind to add a starting dialog parameter. 29 June 1997 Modified DlogCoerceEditable to allow dynamically changing editTexts to statTexts or vice versa, even from a hit handler. 31 May 1997 Completed PowerMac-native support. 27 April 1997 Converted to CodeWarrior, and modified for PowerMac-native support. 23 March 1997 Added support for default-size offsets for modeless dialogs. 1­2, 9, 15, 22 Feb 1997 Added support for modeless dialogs. 16 February 1996 Added the dlogResizeDItems and dlogExpandingGeneva9 routines. 3 February 1996 Added dlogGetCurrentEditSelection. 30 January 1996 Added dlogSearchPrevDItems and support for Shift-Tabbing through edit items. Also added the dlogDontManageOK routine. 29 January 1996 Added support for handling menu items while in a dialog [which I then disabled!]. 20 January 1996 Added the dlogMoveDItems routine. 16 January 1996 Added the dlogLastEvent routine. 15 January 1996 Added the dlogUseFontSize routine. * * * } { OLD UNIT HISTORY: } { } { Version: Start - finish dates: Comments / changes: } { } { 1.0: 11 August 1990 Created and checked entire unit. } { 1.0.1: 14-15 Aug 1990 Revised routines and added dlogOSError, etc. } { 1.1: 19-21 Sep 1990 Added sub-filter proc ability & changed key } { equiv method to inteligent selection (cmd } { symbol or initial). } { 1.1.1: 22 September 1990 Fixed a few bugs here and there. } { 1.1.2: 30 September 1990 Changed the unit name to a Œgenı prefix and } { routines to a Œdlogı prefix. } { 1.2: 2 October 1990 Added dlogStandardFile. } { 1.2.1: 17 November 90 Changed so it will only exitToShell if } { application on error. } { 1.2.2: 13 January 1991 Added the defaultID constant. } { 1.2.3: 14 March 1991 Fixed probs with dlogStandardFile by } { making it use globals & a dedicated } { filterProc & added the compile-time } { variable ŒstandardFileı to exclude if } { not needed. } { 1.3: 21 March 1991 Added the working message routines. } { 1.4: 22 March 1991 Moved dlogReallyBadError to genDebug. } { 1.4.1: 12 April 1991 Allowed dismissal of buttonless dialog with } { a keypress. } { 1.5: 22 September 1991 Added support for strings below the } { progress thingie. } { 1.6: 26 October 1991 Added dlogSFHook patch to allow } { dlogHookProcs. } { 1.6.1: 27 October 1991 Added more SystemTasks to the progress } { dialogs. } { 1.7: 28 October 1991 Added dlogSFFileFilter to allow file filter } { procs. } { 1.7.1: 2 November 1991 Added option of passing ignoreProgessBar } { to the dlogChangeTextMessage etc routines. } { 1.8: 8 November 1991 Moved the progress dialog routines into a } { new unit. } { 1.9: 12 November 1991 Added support for the menubar under } { System 7. } { 1.10: 11 December 1991 Added the dlogExpandingText procedure } { and changed dlogOSError to use a dialog } { rather than an alert. Also added the } { dlogDoErrorSound procedure. } { 1.10.1: 12­13 Dec 1991 Debugging of dlogExpandingText. } { 1.10.2: 13 December 1991 Now only shows an error number in } { dlogOSError if a text message is unavailable. } { 1.10.3: 15 December 1991 Stopped dlogDialog from Œclickingı on } { inactive buttons with the keyboard. } { 2.0: 20 December 1991 First public release, in library form. } { 2.1: 12­13 Jan 1992 Added support for automatic plural } { handling. } { 2.1.1: 21 January 1992 Added the userMsg constant for use with } { dlogOSError. } { 2.2: 20 April 1992 Added the dlogUserError procedure. } { 2.3: 26 April 1992 Changed the way buttons hilight from the } { keyboard to the standard method (i.e. } { highlights then unhilites, rather than just } { hilighting and removing the dialog). } { 2.4: 26 June 1992 Made the bold outline routine publically } { available, and increased the speed of } { drawing dialog contents. } { 2.4.1: 2 July 1992 Fixed NIL deref bug in findEquivButton } { if no match is found. } { 2.5: 10 July 1992 Added auto-centring of dialogs if building } { as an application. } { 2.6: 16 Sep 1992 Added the dlogUserWarning procedure } { 2.7: 22 May 1993 Added hookFirstCall in dlogDialog. } { 2.8: 28 August 1993 Added dlogNoError and dlogIgnoreError. } { 2.9: 4 June 1994 Added dlogItemIsCtrl, dlogSetControl, } { dlogSetDrawingProc & dlogChangeCtrlTitle. } { 2.10: 6 June 1994 Added dlogInitAnimatedIcons, } { dlogNextIconID, and dlogInitialIconID. } { 2.11: 14­16 June 1994 Added the dlogDrawGreyLinesItem, } { dlogDrawBlackLinesItem, dlogGetItemText, } { dlogSetItemText, dlogInColour and } { dlogHiliteButton routines. } { 2.11.1: 25 June 1994 Modified dlogDialog to save and restore the } { previous ParamText strings, to fix problems } { when nesting dialogs, and dlogOSError to } { only append a period if there isnıt already a } { valid sentence terminator. } { 2.12: 2­3 July 1994 Added dlogProcDialog, dlogSimpleDialog, } { dlogInfoDialog, dlogGetCurrentEditItem, } { and dlogSetDefaultButton; we now have } { default button flipping at last! Also added } { the dlogResetItemStatus, dlogSetItemStatus, } { and dlogKeepOKDimmed routines and } { related support code to allow automatic } { management of editText length and OK } { button dimming when certain editText } { items are null. } { 2.12.1 5 July 1994 Added erasing to dlogDrawBoldOutline. } { 2.13: 6 July 1994 Added the dlogOldError procedure. } { 2.14: 6 July 1994 Added the itemColonsIllegal option to } { dlogSetItemStatus to filter out colons from } { filenames etc. } { 2.15: 7 July 1994 Added the dlogParamDialog routine. } { 2.16: 8 July 1994 Added the itemDigitsOnly option to } { dlogSetItemStatus to only accept entry of } { digits in editText fields. } { 2.16.1: 12 July 1994 dlogSetItemText now only changes the text } { if it is different from what is already there. } { 2.17: 13 July 1994 Added the dlogOKIsActive, } { dlogToggleCheckbox, dlogSetupRadioGroup, } { and dlogHandleRadioGroup routines. } { 2.18: 18 July 1994 Added the dlogEllipsisText routine. } { 2.19: 20 July 1994 Added the ability to have no default button } { (by passing zero to dlogSetDefaultButton). } { 2.19.1: 21 July 1994 Added menuEquivs compile-time variable } { to prevent bus errors when the app doesnıt } { have any menus. } { 2.20: 24 July 1994 Added the dlogEllipsisCtrlTitle routine, } { and modified dlogChangeCtrlTitle so that } { it only changes the title if the new one is } { different from the existing one. } { 2.21: 25 July 1994 Added the dlogCoerceEditable and } { dlogDrawGreyoutItem routines. } { 2.21.1: 28 July 1994 Fixed dlogResetItemStatus; it wasnıt initing } { the digitsOnly array. Also made sure that } { the fore colour is set to black in } { dlogDrawBoldOutline, so that I can use a } { different fore colour for statText etc items. } { 2.22: 2 August 1994 Moved drawing-related routines to the } { genGraphics unit, in an attempt to cut down } { the size of this unit (previously 120k!). } { 2.23: 11 August 1994 Added dlogUserErrStr & dlogUserWarnStr. } { 2.23.1: 13 October 1994 Added more sentence terminators. } { 2.24: 10­11 Nov 1994 Tweaked a number of routines, including } { dlogSetItemStatus, dlogDrawBoldOutline, } { added dlogBoldOutlineMethod, and moved } { the old standard file routines into a separate } { unit, at long last! } { 2.25: 11 November 94 Changed filter infomation storage scheme } { to piggyback on the DialogRecord instead of } { storing it in a handle, as there was no real } { way to find out whether a given dialog was } { created with or without filter info (e.g. } { standard file dialogs). } { 2.26: 15 November 94 Added the dlogAutoStatus routine and } { associated support. } { 2.27: 23­24 Nov 1994 Added the itemMenuMetasIllegal and } { itemBlankShownZero item status options. } { 2.28: 29 November 94 Added the itemDecimalsOnly status option. } { 2.29: 12 December 1994 Modified the dlogKeepOKDimmed routine } { to allow enabling or disabling of the feature. } { 2.29.1: 18 December 1994 The call to the DABeeper routine was } { crashing some Macs and not working on } { others, so it has been replaced with a } { simple SysBeep call. } { 2.29.2: 22 December 1994 Fixed a problem with period detection in } { the itemDecimalsOnly status option. } { 2.30: 12 January 1995 Added the dlogGetCtrlItem function. } { 2.31: 17 January 1995 Added the itemZeroIllegal status option. } { 2.32: 20 January 1995 Added the automatic notification features } { to dlogDialog, and added the } { dlogSetNotificationSnds, } { dlogNotifyForAttention, } { dlogNotifyOfCompletion and } { dlogHandleBoolRadioGroup routines. } { 2.33: 2 October 1995 Added dlogDontExportClipboard and fixed } { inconsistency in dlogHiliteButton. } { 2.34: 14 October 1995 Modified dlogHiliteButton so the bold } { outline is dimmed instead of toggled if } { Cancel is missing or already dimmed. } { * * * } { N.B: Most of my units require the compile-time variables Œapplicationı } { and Œdebugı, both of which are booleans. } { Iff your program has menus, you should also define the compile- } { time variable ŒmenuEquivsı (the value isnıt important). } {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} UNIT genDialogs; INTERFACE USES genToolbox, genResources, genNumerics, genStrings; CONST itemNotFound = 0; { Returned by dlogSearchDItems to indicate just that } zerothItem = 0; { Used with dlogSearchDItems when assigning result to the same } { variable as is used in the parameters (which is incremented) } firstItem = 1; { Used with dlogSearchDItems to start at the first dialog item } defaultID = 0; { Used to specify the default dialog ID for dlogStandardFile } errorStringsID = 600; { ID numbers of the STR# resources containing the error strings } moreErrStrsID = 601; { used by the genericErrorHandler } miscDlogStrsID = 610; { Miscelaneous strings (in a STR#) used by the handlers } unknownErrorIndex = 1; { Index number of the string for when we canıt find a } { specific error message; used by dlogOSError } accessingIndex = 2; { Index number of the string Œaccessingı; used by dlogOSError } openingIndex = 3; { Index number of the string Œopeningı; used by dlogOSError } savingIndex = 4; { Index number of the string Œsavingı; used by dlogOSError } readingIndex = 5; { Index number of the string Œreadingı; used by dlogOSError } writingIndex = 6; { Index number of the string Œwritingı; used by dlogOSError } whileDoingIndex = 9; { Index number of the string Œwhileı; used by dlogOSError } idNoIndex = 10; { Index number of the string ŒID # = ı; used by dlogOSError } quittingStrIndex = 12; { Index number of the string Œquittingı; used by dlogSaveChanges } closingStrIndex = 13; { Index number of the string Œclosingı; used by dlogSaveChanges } osErrorDialogID = 600; { ID number of the DLOG resource used by dlogOSError } userMsg = 1; { Pass this to dlogOSError when using your own messages } osWarningDialogID = 610; { ID number of the user warning DLOG resource } hookNothing = 0; {Matches no event; use to coerce an item hit to do nothing } { Note: hookNull is defined in the CTBUtilities unit; it equals 100 } hookSetupInvisible = -1; { Passed to the dialog hook routine before the dialog has been shown } hookSetupVisible = -2; { Passed to the dialog hook routine after the dialog has been shown and drawn } hookFirstCall = hookSetupVisible; { Old constant, for compatiblity } { The following item hit hooks are only passed to modeless dialogs: } hookQuit = -3; { The user chose File:Quit; only used for dlogModelessMenu to send a } { hookClose item hit to each open window } hookClose = -4; { The user clicked in the Close box, chose File:Close, or File:Quit } hookSave = -10; { The user chose File:Save } hookSaveAs = -11; { The user chose File:Save As } hookRevert = -12; { The user chose File:Revert to Saved } hookCut = -20; { The user chose Edit:Cut } hookCopy = -21; { The user chose Edit:Copy } hookPaste = -22; { The user chose Edit:Paste} hookClear = -23; { The user chose Edit:Clear } hookSelectAll = -24; { The user chose Edit:Select All } hookKeyReturn = -100; { The user pressed the Return key } hookKeyEnter = -101; { The user pressed the Enter key } hookKeyTab = -102; { The user pressed the Tab key } hookKeyShiftTab = -103; { The user pressed the Shift-Tab key } hookKeyHelp = -104; { The user pressed the Help key } hookKeyFwdDel = -105; { The user pressed the Forward Delete key } hookKeyHome = -106; { The user pressed the Home key } hookKeyEnd = -107; { The user pressed the End key } hookKeyPageUp = -108; { The user pressed the Page Up key } hookKeyPageDown = -109; { The user pressed the Page Down key } hookPosition = -200; { Not passed by the dlog routines; pass to your resize routine to position the wind } hookGrow = -201; { The window was resized via grow box } hookZoomIn = -202; { The window was zoomed in } hookZoomOut = -203; { The window was zoomed out } hookActivate = -300; { The window has just been activated } hookDeactivate = -301; { The window has just been deactivated } foreignKind = 0; { Returned by dlogGetKind if the window wasnıt created by this unit } defaultKind = -1; { Returned if the window kind hasnıt been changed from the default } modalKind = -99; { Returned if the window is in fact a modal dialog } maxStatusItems = 63; { Maximum number of dialog items supported by } { dlogSetItemStatus (must be a multiple of 32 minus 1) } maxStatusAsLongs = (maxStatusItems + 1) DIV 32; itemCurrentLength = -1; { Used in dlogSetItemStatus if you want to set the options } { without changing the text length value } itemDefaultLength = 255; { Used in dlogSetItemStatus for the default text length } itemCurrentOptions = -1; { Used in dlogSetItemStatus if you want to set the text } { length value without changing the options } itemDefaultOptions = 0; { Used in dlogSetItemStatus for the default options } { The following options can be added to have a combination of features for the item, } { e.g. itemDimOKIfNull + itemMultiLineEdit: } itemDimOKIfNull = 1; { Dim the OK button if the editText item contents is null } itemMultiLineEdit = 2; { Return will go to a new line instead of OKing the dialog } itemColonsIllegal = 4; { Colons (³:²) will be filtered out; useful for filename entry } itemMenuMetasIllegal = 8; { Meta-characters for menu items will be filtered out } itemDecimalsOnly = 16; { Only digits (0..9) and one decimal point (.) will be allowed } itemDigitsOnly = 32; { Only digits (0..9) will be allowed } itemBlankShownZero = 64; { If the text is null, zero will be substituted instead } itemZeroIllegal = 128; { Zero and null values will be replaced with the value 1 } itemFilename = itemDimOKIfNull + itemColonsIllegal; { The standard options for filenames } activate = true; { Used in dlogSetControl for the controlıs value } deactivate = false; toggleDefaults = true; { Used in dlogHiliteButton to specify whether to toggle the } dimInSynch = false; { bold outline between the OK and Cancel buttons or dim it } letMeHandleEdit = true; { Used in dlogInstallMenuHandler to specify whether to override the } autoHandleEdit = false; { built-in Edit menu handling or not } notificationIconID = 128; { ID of the small icon family used during notification } notifyWithSilence = Chr(1); { Used in dlogSetNotificationSnds and dlogNotifyOfCompletion } notifyWithBeep = ''; { to indicate a silence or a beep, respectively } quitAppErr = 9702; { Returned by dlogEventLoop when the application should quit } uppSetupDialogProcInfo = $000000C0; { PROCEDURE (4 byte param); } uppItemHitProcInfo = $000003C0; { PROCEDURE (4 byte param, 4 byte param); (4-byte second param as it is a VAR) } TYPE AppearanceVersionType = (AppearanceNA,Appearance10,Appearance11); {Appearance not available, version 1.0 - 1.0.2, or version 1.1} stdPats = (whitePat, ltGrayPat, grayPat, dkGrayPat, blackPat); boldOutlineType = (boldActive, boldDim, boldErase); ItemRange = SET OF 1..99; SetupDialogProcPtr = ProcPtr; { PROCEDURE MySetupDialog(theDialog: DialogPtr); } ItemHitProcPtr = ProcPtr; { PROCEDURE MyItemHit(theDialog: DialogPtr; VAR itemHit: Integer); } SetupDialogUPP = UniversalProcPtr; ItemHitUPP = UniversalProcPtr; {$IFC application} VAR gAppearanceVersion: AppearanceVersionType; inBackground: boolean; { If true, the application is in the background } notifyErrorSound: str63; { Sound to play when an error dialog is displayed } notifyAttentionSound: str63; { Sound to play when any other kind of dialog is displayed, } { iff the application is in the background } {$ENDC} {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE DlogInitialise; FUNCTION DlogGetKind (theDialog: DialogPtr): Integer; PROCEDURE DlogSetKind (theDialog: DialogPtr; kind: Integer); FUNCTION DlogFindKind (theDialog: DialogPtr; kind: Integer): DialogPtr; FUNCTION DlogKindAlreadyOpen (kind: Integer; select: Boolean): Boolean; PROCEDURE DlogSetFirstList(theDialog: DialogPtr; firstListHdl: ListHandle); FUNCTION DlogGetFirstList(theDialog: DialogPtr): ListHandle; PROCEDURE DlogSetCurrentList(theDialog: DialogPtr; currentListHdl: ListHandle); FUNCTION DlogGetCurrentList(theDialog: DialogPtr): ListHandle; FUNCTION dlogCountDItems (theDialog: dialogPtr): integer; FUNCTION dlogSearchDItems (theDialog: dialogPtr; startItem, theType: integer; {} VAR itemHandle: handle; VAR itemBox: rect): integer; FUNCTION dlogSearchPrevDItems (theDialog: DialogPtr; startItem, theType: Integer; {} VAR itemHandle: Handle; VAR itemBox: Rect): Integer; PROCEDURE dlogMoveDItems (theDialog: DialogPtr; theItems: ItemRange; deltaH, deltaV: Integer); PROCEDURE dlogResizeDItems (theDialog: DialogPtr; theItems: ItemRange; deltaH, deltaV: Integer); PROCEDURE dlogResetItemStatus (theDialog: dialogPtr; fromItem: integer); PROCEDURE dlogSetItemStatus (theDialog: dialogPtr; theItem, {} maximumLength, options: integer); PROCEDURE dlogAutoStatus (theDialog: dialogPtr; auto: boolean); PROCEDURE dlogDontManageOK (theDialog: DialogPtr); PROCEDURE dlogKeepOKDimmed (theDialog: dialogPtr; keepDimmed: boolean); FUNCTION dlogOKIsActive (theDialog: dialogPtr; ignoreKeepDimmed: boolean): boolean; FUNCTION dlogItemIsCtrl (itemType: integer; itemHandle: handle): boolean; FUNCTION DlogItemIsButton (theDialog: DialogPtr; theItem: Integer): Boolean; FUNCTION dlogGetCtrlItem (theDialog: dialogPtr; theItem: integer; {} VAR itemHandle: controlHandle): boolean; PROCEDURE dlogSetControl (theDialog: dialogPtr; theItem: integer; {} value, activateIt: boolean); FUNCTION dlogToggleCheckbox (theDialog: dialogPtr; theItem: integer): boolean; PROCEDURE dlogSetupRadioGroup (theDialog: dialogPtr; firstItem, {} noOfItems: integer; currentValue: UNIV signedByte); PROCEDURE dlogHandleRadioGroup (theDialog: dialogPtr; itemHit, firstItem, {} noOfItems: integer; VAR results: UNIV signedByte); FUNCTION dlogHandleBoolRadioGroup (theDialog: dialogPtr; itemHit, firstItem: integer): boolean; PROCEDURE dlogBoldOutlineMethod (theDialog: dialogPtr; {} toggleDefaultButtons: boolean); PROCEDURE dlogDrawBoldOutline (theDialog: dialogPtr; theItem: integer; {} draw: boldOutlineType); PROCEDURE dlogSetDefaultButton (theDialog: dialogPtr; theItem: integer); PROCEDURE dlogHiliteButton (theDialog: dialogPtr; theItem: integer; {} activateIt: boolean); FUNCTION dlogGetCurrentEditItem (theDialog: dialogPtr): integer; PROCEDURE dlogGetCurrentEditSelection (theDialog: DialogPtr; VAR selStart, selEnd: Integer); PROCEDURE DlogCoerceEditable (theDialog: DialogPtr; theItem: Integer; toEditText, allowStatHits: Boolean); FUNCTION dlogGetItemText (theDialog: dialogPtr; theItem: integer): str255; PROCEDURE dlogSetItemText (theDialog: dialogPtr; theItem: integer; theText: str255); PROCEDURE dlogEllipsisText (theDialog: dialogPtr; theItem: integer; {} leftText, rightText: str255); PROCEDURE DlogSetDrawingProc (theDialog: DialogPtr; theItem: Integer; theProcedure: UserItemProcPtr); PROCEDURE dlogChangeCtrlTitle (theDialog: dialogPtr; theItem: integer; {} newTitle: str255); PROCEDURE dlogEllipsisCtrlTitle (theDialog: dialogPtr; theItem: integer; {} leftTitle, rightTitle: str255); PROCEDURE dlogDontExportClipboard (theDialog: dialogPtr); PROCEDURE dlogUseFontSize (theDialog: DialogPtr; theFont, theSize: Integer); PROCEDURE dlogSetNotificationSnds (soundForErrors, soundForAttention: str255); PROCEDURE dlogNotifyForAttention; PROCEDURE dlogNotifyOfCompletion (soundForCompletion: str255); FUNCTION dlogLastEvent (theDialog: DialogPtr): EventRecord; PROCEDURE dlogTrackCursor (theDialog: DialogPtr); PROCEDURE dlogSetTrackCursor (theDialog: DialogPtr; trackCursor: Boolean); {€ PROCEDURE dlogInstallMenuHandler (theDialog: dialogPtr; menuHandlerProc: ProcPtr; {€]} {€ overrideEdit: Boolean);€} PROCEDURE dlogCentreWindow (theDialog: dialogPtr); FUNCTION DlogDialog (dialogID: Integer; dlogSetupProc: SetupDialogProcPtr; eventFilterProc: ModalFilterProcPtr; itemHitFilterProc: ItemHitProcPtr; str0, str1, str2, str3: Str255): Integer; FUNCTION DlogProcDialog (dialogID: Integer; dlogSetupProc: SetupDialogProcPtr; eventFilterProc: ModalFilterProcPtr; itemHitFilterProc: ItemHitProcPtr): Integer; PROCEDURE dlogParamDialog (dialogID: integer; str0, str1, str2, str3: str255); FUNCTION dlogSimpleDialog (dialogID: integer): integer; PROCEDURE dlogInfoDialog (dialogID: integer); PROCEDURE dlogInit; FUNCTION dlogGetDesktopSize: Point; PROCEDURE dlogGetPosAndSize (theDialog: DialogPtr; VAR thePos: Point; {} VAR theWidth, theHeight: Integer); FUNCTION dlogGetPosition (theDialog: DialogPtr): Point; FUNCTION DlogModelessNew (dialogID, kind: Integer; eventFilterProc: ModalFilterProcPtr; itemHitFilterProc: ItemHitProcPtr; sizeOfVars: Size; VAR vars: UNIV Ptr): DialogPtr; PROCEDURE DlogModelessShow (theDialog: DialogPtr); PROCEDURE dlogModelessVars (theDialog: DialogPtr; VAR vars: UNIV Ptr); PROCEDURE dlogModelessAction (theDialog: DialogPtr; itemHit: Integer); PROCEDURE dlogModelessMenu (itemHit: Integer); PROCEDURE dlogModelessPosition (theDialog: DialogPtr; thePosition: Point); FUNCTION dlogModelessStartResize (theDialog: DialogPtr; operation: Integer; {} constrainH, constrainV: Boolean; thePosition: Point; VAR deltaH, deltaV: Integer): Boolean; PROCEDURE dlogModelessFinishResize (theDialog: DialogPtr); PROCEDURE HandleModelessActivate (theDialog: DialogPtr; activating: Boolean); FUNCTION HandleModelessEvent (VAR theEvent: EventRecord): Boolean; FUNCTION dlogEventLoop (VAR menuID, menuItem: Integer): OSErr; { FUNCTION dlogAlert (alertID: integer; str0, str1, str2, str3: str255): integer; } PROCEDURE dlogExpandingText (theDialog: dialogPtr); PROCEDURE dlogExpandingGeneva9 (theDialog: DialogPtr); FUNCTION dlogOSError (errorNo: osErr; message, process, filename: str255): boolean; FUNCTION dlogNoError (error: osErr): boolean; PROCEDURE dlogReportError (error: osErr); PROCEDURE dlogIgnoreError (error: osErr); PROCEDURE dlogOldError (result: osErr; VAR error: osErr); PROCEDURE dlogUserError (message: str255); PROCEDURE dlogUserErrStr (stringID, stringIndex: integer); PROCEDURE dlogUserWarning (message: str255); PROCEDURE dlogUserWarnStr (stringID, stringIndex: integer); {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} IMPLEMENTATION CONST DAStringsGlob = $AA0; TYPE ItemBoolArray = RECORD CASE Boolean OF false: ( asBool: PACKED ARRAY[0..maxStatusItems] OF Boolean { Bit 0 is represents all } ); true: ( asLong: PACKED ARRAY[1..maxStatusAsLongs] OF Longint ) END; ItemByteArray = PACKED ARRAY[0..maxStatusItems] OF Byte; { Yes, bytes are evil 2-byte variables for some unknown reason, but } { this is a packed array, so itıs okay. It has to be a byte to avoid } { problems in the dlogSetItemStatus routine. } FilterDialogRec = RECORD dialog: DialogRecord; validity: OSType; kind: Integer; eventFilterUPP: ModalFilterUPP; itemHitFilterUPP: ItemHitUPP; sizeOfVars: Size; vars: Ptr; minWindowSize: Point; firstListHdl: ListHandle; currentListHdl: ListHandle; lastEvent: EventRecord; boldOutline: BoldOutlineType; toggleOutline: Boolean; buttonless: Boolean; exportTEScrap: Boolean; overrideEditMenu: Boolean; statusInited: Boolean; autoStatus: Boolean; OKActive: Boolean; keepOKDimmed: Boolean; dontManageOK: Boolean; trackCursor: Boolean; growable: Boolean; unused: Boolean; dimOKIfEditNull: ItemBoolArray; dimOK: ItemBoolArray; multiLineEdit: ItemBoolArray; colonsIllegal: ItemBoolArray; menuMetasIllegal: ItemBoolArray; digitsOnly: ItemBoolArray; decimalsOnly: ItemBoolArray; blankShownZero: ItemBoolArray; zeroIllegal: ItemBoolArray; maxLength: ItemByteArray; END; FilterPeek = ^FilterDialogRec; DAStringsArray = ARRAY[0..3] OF StringHandle; DAStringsPtr = ^DAStringsArray; CONST normalDialogSize = SizeOf(DialogRecord); filterDialogSize = SizeOf(FilterDialogRec); validFilterInfo = 'Filt'; invalidFilterInfo = 'Err!'; VAR gOffsetPos: Point; gQuittingApp: Boolean; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION NewItemHitProc(userRoutine: ItemHitProcPtr): ItemHitUPP; {Returns a new UPP for a dialog item hit routine.} {Written by David Sinclair, 27 April 1997.} BEGIN NewItemHitProc:= NewRoutineDescriptor(userRoutine,uppItemHitProcInfo,GetCurrentISA) END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE CallSetupDialogProc(theDialog: DialogPtr; userRoutine: ProcPtr); {Calls the dialog setup routine; a ProcPtr is used as this is only called once, so thereıs no need to store the UPP.} {Written by David Sinclair, 31 May 1997.} {$IFC GENERATINGPOWERPC} VAR theUPP: UniversalProcPtr; ignored: Longint; BEGIN theUPP:= NewRoutineDescriptor(userRoutine,uppSetupDialogProcInfo,GetCurrentISA); ignored:= CallUniversalProc(theUPP,uppSetupDialogProcInfo,theDialog); DisposeRoutineDescriptor(theUPP); END; {$ELSEC} INLINE $205f, { movea.l (a7)+,a0 ; (a0) is a ptr to string, 4(a0) is mode } $4e90; {$ENDC} {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE CallItemHitProc(theDialog: DialogPtr; VAR itemHit: Integer; userRoutineUPP: ItemHitUPP); {Calls the dialog item hit routine.} {Written by David Sinclair, 31 May 1997.} {$IFC GENERATINGPOWERPC} VAR ignored: Longint; BEGIN ignored:= CallUniversalProc(UniversalProcPtr(userRoutineUPP),uppItemHitProcInfo,theDialog,@itemHit); END; {$ELSEC} INLINE $205f, { movea.l (a7)+,a0 ; (a0) is a ptr to string, 4(a0) is mode } $4e90; {$ENDC} {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE DlogInitialise; {Initialises the genDialog globals. Only call this if you want to use the Appearance Manager. Sets gAppearanceVersion to AppearanceNA if it is not available, Appearance10 if version 1.0 - 1.0.2 is available, or Appearance11 if version 1.1 is available.} {Written by David Sinclair, 14 May 1999.} VAR response: Longint; BEGIN (* gAppearanceVersion:= AppearanceVersionType(Ord(Gestalt(gestaltAppearanceAttr,response)=noErr)); IF (gAppearanceVersion > AppearanceNA) & (Gestalt(gestaltAppearanceVersion,response)=noErr) THEN BEGIN IF response>=$0110 THEN gAppearanceVersion:= Appearance11; END; *) END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE InitFilterInfo (theDialog: dialogPtr; theEventFilterProc: ModalFilterProcPtr; theItemHitFilterProc: ItemHitProcPtr; varsSize: Size); { Procedure to initialise the filter information for the specified dialog. } { Written by David Sinclair, 21 September 1990; modifed to use the piggyback } { filter info instead of a record accessed via a handle, 11 November 1994; } { added itemHitFilterProc, 2 February 1997; added varsSize, 9 February 1997; { added UPP support, 31 May 1997; added first and current lists, 13 September 1997. } VAR itemHandle: handle; itemBox: rect; BEGIN IF theDialog <> NIL THEN IF GetPtrSize(ptr(theDialog)) = filterDialogSize THEN WITH FilterPeek(theDialog)^ DO BEGIN validity:= validFilterInfo; { Fill in the filter info record } kind:= modalKind; IF theEventFilterProc<>Nil THEN eventFilterUPP:= NewModalFilterProc(theEventFilterProc) ELSE eventFilterUPP:= Nil; IF theItemHitFilterProc<>Nil THEN itemHitFilterUPP:= NewItemHitProc(theItemHitFilterProc) ELSE itemHitFilterUPP:= Nil; sizeOfVars:= varsSize; IF varsSize > 0 THEN vars:= NewPtr(varsSize) ELSE vars:= NIL; IF (vars = NIL) | (GetPtrSize(vars) = 0) THEN sizeOfVars:= 0 ELSE numZeroBlock(vars, varsSize); minWindowSize:= theDialog^.portRect.botRight; firstListHdl:= Nil; currentListHdl:= Nil; lastEvent.what:= nullEvent; boldOutline:= boldActive; toggleOutline:= true; buttonless:= dlogSearchDItems(theDialog, firstItem, ctrlItem + btnCtrl, itemHandle, itemBox) = itemNotFound; exportTEScrap:= true; statusInited:= false; autoStatus:= true; trackCursor:= true; growable:= false; END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE resetItemStatus (theDialog: dialogPtr; fromItem: integer); { Resets the item status and max length information for all dialog items from } { the specified one onwards; pass zero to reset them all. Only called from } { validFilterDialog. } { Written by David Sinclair, 2­3 July 1994; modifed to use the piggyback } { filter info instead of a record accessed via a handle, 11 November 1994. } VAR index: integer; BEGIN WITH filterPeek(theDialog)^ DO BEGIN IF NOT statusInited | (fromItem < 0) THEN BEGIN fromItem:= 0; OKActive:= true; keepOKDimmed:= false; dontManageOK:= false; END; IF fromItem <= maxStatusItems THEN FOR index:= fromItem TO maxStatusItems DO BEGIN dimOKIfEditNull.asBool[index]:= false; dimOK.asBool[index]:= false; multiLineEdit.asBool[index]:= false; colonsIllegal.asBool[index]:= false; menuMetasIllegal.asBool[index]:= false; digitsOnly.asBool[index]:= false; decimalsOnly.asBool[index]:= false; blankShownZero.asBool[index]:= false; zeroIllegal.asBool[index]:= false; maxLength[index]:= 255; END; statusInited:= true END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION ValidFilterDialog (theDialog: DialogPtr; needStatusInfo: Boolean): Boolean; { Returns true if the dialog contains valid filter information; if it returns false, you } { must not attempt to modify any filter info as it is just a normal dialog (this could } { happen for standard file dialogs or progress dialogs, for example). If needStatusInfo } { is true, the status information is also checked, and initialised if not already. If true is } { returned, the port is guaranteed to be set to theDialog. } { Written by David Sinclair, 11 November 1994. } VAR valid: Boolean; BEGIN valid:= false; IF theDialog <> NIL THEN IF GetPtrSize(ptr(theDialog)) = filterDialogSize THEN IF FilterPeek(theDialog)^.validity = validFilterInfo THEN BEGIN valid:= true; SetPort(theDialog); IF needStatusInfo & NOT FilterPeek(theDialog)^.statusInited THEN ResetItemStatus(theDialog, 0) END; ValidFilterDialog:= valid END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION DlogGetKind (theDialog: DialogPtr): Integer; {Returns the kind of dialog; foreignKind for windows not created by the this unit, modalKind for modal dialogs or defaultKind for modeless dialogs that havenıt previously called dlogSetKind, or whatever value was passed to dlogSetKind.} {Written by David Sinclair, 2 February 1997.} VAR kind: Integer; BEGIN IF ValidFilterDialog(theDialog, false) THEN kind:= FilterPeek(theDialog)^.kind ELSE kind:= foreignKind; DlogGetKind:= kind END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE DlogSetKind (theDialog: DialogPtr; kind: Integer); {Sets the kind value for the dialog. This can be used to identify different kinds of windows when activating menus, etc. Pass a value in the range 1..MaxInt.} {Written by David Sinclair, 2 February 1997.} BEGIN IF ValidFilterDialog(theDialog, false) THEN FilterPeek(theDialog)^.kind:= kind; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION DlogFindKind (theDialog: DialogPtr; kind: Integer): DialogPtr; {Finds the next dialog of the specified kind after the given dialog (or from the first if theDialog is nil). Returns the dialog pointer or nil if there are no more windows of that kind open.} {Written by David Sinclair, 15 February 1997; added theDialog parameter, 29 March 1998.} VAR found: Boolean; BEGIN found:= false; IF theDialog<>nil THEN theDialog:= DialogPtr(WindowPeek(theDialog)^.nextWindow) ELSE theDialog:= FrontWindow; WHILE (theDialog <> nil) & NOT found DO BEGIN IF ValidFilterDialog(theDialog, false) & (FilterPeek(theDialog)^.kind = kind) THEN found:= true ELSE theDialog:= DialogPtr(WindowPeek(theDialog)^.nextWindow) END; DlogFindKind:= theDialog END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION DlogKindAlreadyOpen (kind: Integer; select: Boolean): Boolean; {Call this to determine whether there is a dialog of the specified kind open already, or not. If there is one, and select is true, it is selected. Useful when only one instance of a modeless dialog is allowed; pass true for select, and only open a new one if this returns false; itıll then open a new one if one isnıt already open, otherwise itıll select the old one.} {Written by David Sinclair, 22 February 1997.} VAR theDialog: DialogPtr; found: Boolean; BEGIN theDialog:= DlogFindKind(nil, kind); found:= (theDialog <> nil); IF found & select THEN BEGIN SelectWindow(theDialog); HiliteMenu(0); END; DlogKindAlreadyOpen:= found END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE DlogSetFirstList(theDialog: DialogPtr; firstListHdl: ListHandle); {Stores a handle to the dialogıs first list in the dialogıs info. Only the List Suite should call this routine.} {Written by David Sinclair, 13 September 1997.} BEGIN IF ValidFilterDialog(theDialog,false) THEN FilterPeek(theDialog)^.firstListHdl:= firstListHdl END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION DlogGetFirstList(theDialog: DialogPtr): ListHandle; {Returns a handle to the dialogıs first list, from the specified dialogıs info, or Nil if the dialog doesnıt contain valid data, or no list has been previously added. Only the List Suite should call this routine.} {Written by David Sinclair, 13 September 1997.} BEGIN IF ValidFilterDialog(theDialog,false) THEN DlogGetFirstList:= FilterPeek(theDialog)^.firstListHdl ELSE DlogGetFirstList:= Nil END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE DlogSetCurrentList(theDialog: DialogPtr; currentListHdl: ListHandle); {Stores a handle to the dialogıs currently active list in the dialogıs info. Only the List Suite should call this routine.} {Written by David Sinclair, 13 September 1997.} BEGIN IF ValidFilterDialog(theDialog,false) THEN FilterPeek(theDialog)^.currentListHdl:= currentListHdl END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION DlogGetCurrentList(theDialog: DialogPtr): ListHandle; {Returns a handle to the dialogıs currently active list, from the specified dialogıs info, or Nil if the dialog doesnıt contain valid data, or no list is currently active. Only the List Suite should call this routine.} {Written by David Sinclair, 13 September 1997.} BEGIN IF ValidFilterDialog(theDialog,false) THEN DlogGetCurrentList:= FilterPeek(theDialog)^.currentListHdl ELSE DlogGetCurrentList:= Nil END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogCountDItems (theDialog: dialogPtr): integer; { Returns the number of items in the item list for the specified dialog. } { Written by David Sinclair, 15 August 1990; fixed -1 bug 13 December 1991 (amazing } { that it could lurk this long!!). } TYPE countPtr = ^integer; countHandle = ^countPtr; BEGIN dlogCountDItems:= countHandle(dialogPeek(theDialog)^.items)^^ + 1 END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogSearchDItems (theDialog: dialogPtr; startItem, theType: integer; {} VAR itemHandle: handle; VAR itemBox: rect): integer; { Routine to find a specified item type within a dialog and return its item number } { and box. } { Written by David Sinclair, 11 August 1990. } VAR done: boolean; noOfItems, itemType: integer; BEGIN done:= false; IF startItem < firstItem THEN startItem:= firstItem; noOfItems:= dlogCountDItems(theDialog); REPEAT IF startItem > noOfItems THEN BEGIN startItem:= itemNotFound; done:= true END ELSE BEGIN GetDialogItem(theDialog, startItem, itemType, itemHandle, itemBox); IF itemType = theType THEN done:= true ELSE startItem:= startItem + 1 END UNTIL done; dlogSearchDItems:= startItem END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogSearchPrevDItems (theDialog: DialogPtr; startItem, theType: Integer; {} VAR itemHandle: Handle; VAR itemBox: Rect): Integer; { The same as dlogSearchDItems, above, but searches backwards. } { Written by David Sinclair, 30 January 1996. } VAR done: Boolean; noOfItems, itemType: Integer; BEGIN done:= false; noOfItems:= dlogCountDItems(theDialog); IF startItem > noOfItems THEN startItem:= noOfItems; REPEAT IF startItem < firstItem THEN BEGIN startItem:= itemNotFound; done:= true END ELSE BEGIN GetDialogItem(theDialog, startItem, itemType, itemHandle, itemBox); IF itemType = theType THEN done:= true ELSE startItem:= startItem - 1 END UNTIL done; dlogSearchPrevDItems:= startItem END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogMoveDItems (theDialog: DialogPtr; theItems: ItemRange; deltaH, deltaV: Integer); { Moves the specified items by deltaH and deltaV. Pass a range of items to move, e.g. } { [OK..Cancel, excitingEdit]. } { Written by David Sinclair, 20 January 1996. } VAR itemType: Integer; itemHandle: Handle; itemBox: Rect; index: Integer; count: Integer; BEGIN count:= dlogCountDItems(theDialog); FOR index:= 1 TO count DO IF index IN theItems THEN BEGIN GetDialogItem(theDialog, index, itemType, itemHandle, itemBox); OffsetRect(itemBox, deltaH, deltaV); SetDialogItem(theDialog, index, itemType, itemHandle, itemBox); IF dlogItemIsCtrl(itemType, itemHandle) THEN MoveControl(ControlHandle(itemHandle), itemBox.left, itemBox.top); END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogResizeDItems (theDialog: DialogPtr; theItems: ItemRange; deltaH, deltaV: Integer); { Resizes the specified items by deltaH and deltaV. Pass a range of items to move, e.g. } { [OK..Cancel, excitingEdit]. } { Written by David Sinclair, 16 February 1996. } VAR itemType: Integer; itemHandle: Handle; itemBox: Rect; index: Integer; count: Integer; BEGIN count:= dlogCountDItems(theDialog); FOR index:= 1 TO count DO IF index IN theItems THEN BEGIN GetDialogItem(theDialog, index, itemType, itemHandle, itemBox); itemBox.bottom:= itemBox.bottom + deltaV; itemBox.right:= itemBox.right + deltaH; SetDialogItem(theDialog, index, itemType, itemHandle, itemBox); IF dlogItemIsCtrl(itemType, itemHandle) THEN WITH itemBox DO SizeControl(ControlHandle(itemHandle), right - left, bottom - top); END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogResetItemStatus (theDialog: dialogPtr; fromItem: integer); { Resets the item status and max length information for all dialog items from the } { specified one onwards; pass zero to reset them all. Useful with multi-DITL dialogs. } { Written by David Sinclair, 2­3 July 1994; modifed to use the piggyback } { filter info instead of a record accessed via a handle, 11 November 1994. } BEGIN IF validFilterDialog(theDialog, false) THEN resetItemStatus(theDialog, fromItem) END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogSetItemStatus (theDialog: dialogPtr; theItem, {} maximumLength, options: integer); { Sets the item status information for the specified item. Pass the maximum edit } { item length in maximumLength (or itemDefaultLength for 255 characters, or } { itemCurrentLength to leave it the same as before), and one or more of the item } { status constants for options; you can add them to set multiple options at once (the } { default is itemDefaultOptions, or itemCurrentOptions to leave them the same and } { only alter the text length). NOTE: You should call this routine BEFORE calling } { dlogSetItemText if you are using the itemDimOKIfNull option, just in case the } { initial value is null (otherwise the OK button wonıt be dimmed until the user } { interacts with the field). } { Written by David Sinclair, 2­3 July 1994; modifed to use the piggyback } { filter info instead of a record accessed via a handle, 11 November 1994. } BEGIN IF validFilterDialog(theDialog, true) & (theItem IN [0..maxStatusItems]) THEN WITH filterPeek(theDialog)^ DO BEGIN IF options <> itemCurrentOptions THEN BEGIN dimOKIfEditNull.asBool[theItem]:= bitAnd(options, itemDimOKIfNull) <> 0; dimOK.asBool[theItem]:= (theItem = 0) & dimOKIfEditNull.asBool[theItem]; multiLineEdit.asBool[theItem]:= bitAnd(options, itemMultiLineEdit) <> 0; colonsIllegal.asBool[theItem]:= bitAnd(options, itemColonsIllegal) <> 0; menuMetasIllegal.asBool[theItem]:= bitAnd(options, itemMenuMetasIllegal) <> 0; digitsOnly.asBool[theItem]:= bitAnd(options, itemDigitsOnly) <> 0; decimalsOnly.asBool[theItem]:= bitAnd(options, itemDecimalsOnly) <> 0; blankShownZero.asBool[theItem]:= bitAnd(options, itemBlankShownZero) <> 0; zeroIllegal.asBool[theItem]:= bitAnd(options, itemZeroIllegal) <> 0; END; IF (maximumLength <> itemCurrentLength) & (maximumLength IN [1..255]) THEN maxLength[theItem]:= maximumLength; END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogAutoStatus (theDialog: dialogPtr; auto: boolean); { If auto is true, the item status filtering is done automatically, otherwise itıs } { left up to the client program. If you call the dlogGetItemText routine in your } { dialog hit handler, you should call this routine to deactivate the AutoStatus } { feature, to avoid filtering the hit items twice. There is no harm in filtering them } { twice, but itıs less efficient. You can use the constants activate and deactivate. } { AutoStatus is on by default. } { Written by David Sinclair, 15 November 1994. } BEGIN IF validFilterDialog(theDialog, false) THEN filterPeek(theDialog)^.autoStatus:= auto END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogDontManageOK (theDialog: DialogPtr); { Call this routine to leave the OK button (i.e. item 1) alone, e.g. if it isnıt being } { used as an OK button. } { Written by David Sinclair, 30 January 1996. } BEGIN IF ValidFilterDialog(theDialog, true) THEN filterPeek(theDialog)^.dontManageOK:= true END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogKeepOKDimmed (theDialog: dialogPtr; keepDimmed: boolean); { Call this routine to always keep the OK button dimmed, no matter what goes on } { with edit fields. } { Written by David Sinclair, 3 July 1994; modifed to use the piggyback } { filter info instead of a record accessed via a handle, 11 November 1994; } { modified to allow turning off the keep dimmed feature, 12 December 1994. } BEGIN IF validFilterDialog(theDialog, true) THEN filterPeek(theDialog)^.keepOKDimmed:= keepDimmed END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogOKIsActive (theDialog: dialogPtr; ignoreKeepDimmed: boolean): boolean; { Returns true if the OK button isnıt dimmed, due to one or more editTexts being } { null when they shouldnıt be or whatever. Pass true for ignoreKeepDimmed if you } { want to know what the state of the button would be if it werenıt being kept dimmed } { by the previous routine, or false if you just want to find out itıs actual state. Note } { that passing true assumes that the state of the button is controlled automatically, i.e. } { you havenıt explicitly dimmed it yourself. } { Written by David Sinclair, 3 July 1994; modifed to use the piggyback } { filter info instead of a record accessed via a handle, 11 November 1994. } VAR itemType: integer; itemHandle: handle; itemBox: rect; result: boolean; BEGIN result:= true; IF ignoreKeepDimmed THEN { Report what the OK button would be, ignoring } BEGIN { whether keepOKDimmed is true or not } IF validFilterDialog(theDialog, false) & filterPeek(theDialog)^.statusInited THEN result:= filterPeek(theDialog)^.OKActive ELSE ignoreKeepDimmed:= false END; IF NOT ignoreKeepDimmed THEN { Report the actual visual state of the OK button } BEGIN GetDialogItem(theDialog, ok, itemType, itemHandle, itemBox); IF dlogItemIsCtrl(itemType, itemHandle) THEN result:= controlHandle(itemHandle)^^.contrlHilite <> 255 END; { 0 = active, 255 = dimmed } dlogOKIsActive:= result END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogItemIsCtrl (itemType: integer; itemHandle: handle): boolean; { Returns true if the specified itemType (as returned by GetDialogItem) is a control item, } { and the associated handle is not nil (this is worth checking since one normally } { coerces the handle into a ControlHandle and uses it; being nil would be Bad). } { Written by David Sinclair, 4 June 1994. } CONST ctrlBit = 2; BEGIN dlogItemIsCtrl:= BTST(itemType, ctrlBit) & (itemHandle <> NIL) END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION DlogItemIsButton (theDialog: DialogPtr; theItem: Integer): Boolean; {Returns true if the specified item is a button, otherwise false.} {Written by David Sinclair, 19 April 1999.} VAR itemType: Integer; itemHndl: Handle; itemBox: Rect; BEGIN GetDialogItem(theDialog, theItem, itemType, itemHndl, itemBox); DlogItemIsButton:= dlogItemIsCtrl(itemType, itemHndl) & (itemType IN [ctrlItem + btnCtrl]) END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogGetCtrlItem (theDialog: dialogPtr; theItem: integer; {} VAR itemHandle: controlHandle): boolean; { Call this if you need a controlHandle for the specified item, but donıt need its itemType and } { itemBox. This routine returns true if the item is a control item and the associated handle is } { not nil. The handle is returned as a controlHandle. } { Written by David Sinclair, 4 June 1994. } CONST ctrlBit = 2; VAR itemType: integer; itemBox: rect; valid: boolean; BEGIN valid:= false; IF theDialog <> NIL THEN BEGIN GetDialogItem(theDialog, theItem, itemType, handle(itemHandle), itemBox); valid:= BTST(itemType, ctrlBit) & (itemHandle <> NIL) END; dlogGetCtrlItem:= valid END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogSetControl (theDialog: dialogPtr; theItem: integer; {} value, activateIt: boolean); { Turns off or on the specified radio button or checkbox as appropriate, and activates } { or deactivates it also. Use the constants activate and deactivate, or an expression. } { Written by David Sinclair, 5 December 1991; made into a genDialogs routine } { and added itemType checking, 4 June 1994. } VAR itemType: integer; itemHandle: handle; itemBox: rect; BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF dlogItemIsCtrl(itemType, itemHandle) THEN BEGIN SetControlValue(controlHandle(itemHandle), ord(value)); hiliteControl(controlHandle(itemHandle), ord(NOT activateIt) * 255) END { 0 = active, 255 = dimmed } END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogToggleCheckbox (theDialog: dialogPtr; theItem: integer): boolean; { Similar to the previous routine, except that it toggles the checkbox value, and } { doesnıt alter the activation (dimming) state. The new value is returned. } { Written by David Sinclair, 13 July 1994. } VAR itemType: integer; itemHandle: handle; itemBox: rect; result: boolean; BEGIN result:= false; GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF dlogItemIsCtrl(itemType, itemHandle) THEN BEGIN result:= GetControlValue(controlHandle(itemHandle)) = 0; SetControlValue(controlHandle(itemHandle), ord(result)); END; dlogToggleCheckbox:= result END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogSetupRadioGroup (theDialog: dialogPtr; firstItem, {} noOfItems: integer; currentValue: UNIV signedByte); { Sets up a radio group. The radio button items are presumed to be sequential. Pass } { the first item in the group, the number of items in the group, and the current value } { of the controlling variable; currentValue is suitable for variables of set type or } { boolean; in the latter case, the second of the two radio buttons is considered true. } { Since the currentValue is a univ parameter, you donıt need to coerce it. } { Written by David Sinclair, 18 June 1994; made into a genDialogs routine, } { 13 July 1994. } VAR itemType, index: integer; itemHandle: handle; itemBox: rect; BEGIN IF (firstItem > 0) & (noOfItems > 0) THEN FOR index:= 0 TO noOfItems - 1 DO BEGIN GetDialogItem(theDialog, firstItem + index, itemType, itemHandle, itemBox); IF dlogItemIsCtrl(itemType, itemHandle) THEN SetControlValue(controlHandle(itemHandle), ord(currentValue = index)); END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogHandleRadioGroup (theDialog: dialogPtr; itemHit, firstItem, {} noOfItems: integer; VAR results: UNIV signedByte); { Handles a radio group. The radio button items are presumed to be sequential. Pass } { the item hit, the first item in the group, and the number of items in the group. The } { results are suitable for variables of set type or boolean; in the latter case, the second } { of the two radio buttons is considered true. Since results is a univ parameter, you } { donıt need to coerce it. } { Written by David Sinclair, 17 June 1994; made into a genDialogs routine, } { 13 July 1994. } VAR index: integer; itemHandle: controlHandle; BEGIN IF (itemHit IN [firstItem..(firstItem + noOfItems - 1)]) & (firstItem > 0) & (noOfItems > 0) THEN BEGIN results:= itemHit - firstItem; FOR index:= 0 TO noOfItems - 1 DO BEGIN IF dlogGetCtrlItem(theDialog, firstItem + index, itemHandle) THEN SetControlValue(itemHandle, ord(results = index)); END END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogHandleBoolRadioGroup (theDialog: dialogPtr; itemHit, firstItem: integer): boolean; { Handles a boolean radio group, i.e. one where there are only two radio buttons, the first } { representing the false case, the second representing the true. These are a hassle to pass } { to the above routine if the value is stored in a packed array of boolean, so this routine will } { instead return the correct value as a function result. } { Written by David Sinclair, 20 January 1995. } VAR itemHandle: controlHandle; secondSelected: boolean; BEGIN IF (firstItem > 0) & dlogGetCtrlItem(theDialog, firstItem, itemHandle) THEN IF NOT (itemHit IN [firstItem, firstItem + 1]) THEN { Didnıt click on one of these, so just get value } secondSelected:= GetControlValue(itemHandle) = 0 ELSE BEGIN { Get the new value and update the radio buttons } secondSelected:= itemHit <> firstItem; SetControlValue(itemHandle, ord(NOT secondSelected)); IF dlogGetCtrlItem(theDialog, firstItem + 1, itemHandle) THEN SetControlValue(itemHandle, ord(secondSelected)); END; dlogHandleBoolRadioGroup:= secondSelected END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogBoldOutlineMethod (theDialog: dialogPtr; {} toggleDefaultButtons: boolean); { Changes the method used to draw the bold outline around the default button. By } { default, the default button is toggled between OK and Cancel when the OK button is } { activated or dimmed respectively. If you pass dimInSynch, the bold outline is } { dimmed or activated in synch with the button, or if you pass toggleDefaults the } { default behavior is restored. You only need to call this routine if you prefer the } { dimming behaviour instead of the normal toggling behaviour. } { Written by David Sinclair, 10 November 1994; modifed to use the piggyback } { filter info instead of a record accessed via a handle, 11 November 1994. } BEGIN IF validFilterDialog(theDialog, false) THEN filterPeek(theDialog)^.toggleOutline:= toggleDefaultButtons; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogDrawBoldOutline (theDialog: dialogPtr; theItem: integer; {} draw: boldOutlineType); { Draws a bold round rect around the specified item. Used to draw the bold outline } { around the default button in a dialog. Pass boldActive for draw to draw it black (i.e. } { in an active state, boldDim to draw it greyed, or boldErase to erase it. NOTE: The } { dialog routines in this unit automatically draw and update this outline (without } { using a userItem), so you donıt need to worry about it. Note, though, that if you use } { it together with a userItem, you will need to first make sure you set the userItemıs } { rect to the rect of the button you want outlined. } { Made into a publically available routine 26 June 1992; added erasing capability, } { 5 July 1994; added greying out capability, 10 September 1994; modifed to use the } { piggyback filter info instead of a record accessed via a handle, 11 November 1994. } CONST edgeCurve = 16; gapBetween = -4; lineSize = 3; colourQD = $100; { QD vers 1.00 is first with colour } VAR itemType: integer; itemHandle: handle; itemBox: rect; oldPen: penState; oldPort: grafPtr; response: longint; oldColour, greyColour: RGBColor; BEGIN IF (theDialog <> NIL) & (theItem > 0) THEN BEGIN getPort(oldPort); setPort(theDialog); GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); getPenState(oldPen); {$IFC application} getForeColor(oldColour); IF draw = boldDim THEN BEGIN IF gestalt(gestaltQuickDrawVersion, response) = noErr THEN IF loWord(response) >= colourQD THEN BEGIN { Drawing grey in colour } greyColour.red:= $8000; greyColour.green:= $8000; greyColour.blue:= $8000; RGBForeColor(greyColour) END END ELSE foreColor(blackColor); { Drawing black or erasing } {$ENDC} penNormal; penSize(lineSize, lineSize); insetRect(itemBox, gapBetween, gapBetween); {$IFC application} IF draw = boldErase THEN penMode(patBic); {$ENDC} frameRoundRect(itemBox, edgeCurve, edgeCurve); {$IFC application} RGBForeColor(oldColour); {$ENDC} setPenState(oldPen); IF validFilterDialog(theDialog, false) THEN filterPeek(theDialog)^.boldOutline:= draw; { Update the current state of the outline } setPort(oldPort) END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogSetDefaultButton (theDialog: dialogPtr; theItem: integer); { Makes the specified button the default, redrawing the bold outline as appropriate. } { Note: if you call dlogHiliteButton to dim the OK button (or whatever button is item } { number 1), there is no need to call this routine; it is called automatically in that } { situation. Note that you can pass an item number of zero to make no default button } { (i.e. forcing the user to click the button with the mouse). } { Written by David Sinclair, 2 July 1994; passing zero support added 20 July 1994; modified to } { use a dimmed outline if the button is dimmed, and always draw the outline, 14 October 1995. } VAR itemType, oldDefault: integer; itemHandle: handle; itemBox: rect; newOutline, oldOutline: boldOutlineType; BEGIN IF theItem > 0 THEN BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF dlogItemIsCtrl(itemType, itemHandle) THEN BEGIN IF validFilterDialog(theDialog, false) THEN oldOutline:= filterPeek(theDialog)^.boldOutline ELSE oldOutline:= boldActive; IF (controlHandle(itemHandle)^^.contrlHilite = 0) THEN newOutline:= boldActive ELSE newOutline:= boldDim; oldDefault:= dialogPeek(theDialog)^.aDefItem; dialogPeek(theDialog)^.aDefItem:= theItem; IF (theItem <> oldDefault) | (newOutline <> oldOutline) THEN BEGIN IF theItem <> oldDefault THEN dlogDrawBoldOutline(theDialog, oldDefault, boldErase); dlogDrawBoldOutline(theDialog, theItem, newOutline); END END END ELSE { No default button } BEGIN oldDefault:= dialogPeek(theDialog)^.aDefItem; dialogPeek(theDialog)^.aDefItem:= theItem; IF oldDefault > 0 THEN dlogDrawBoldOutline(theDialog, oldDefault, boldErase); END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogHiliteButton (theDialog: dialogPtr; theItem: integer; {} activateIt: boolean); { Dims or activates the specified button as requested (but only if the button isnıt } { already in that state). If youıre dealing with the OK button (i.e. the button at item } { number 1), the bold outline is altered in accordance with the current method ‹ see } { the dlogBoldOutlineMethod routine. Use the constants activate and deactivate, or a } { boolean expression (e.g. theString <> null). } { Written by David Sinclair, 15 June 1994; added support for the dimming bold outline } { method, 10 November 1994; modifed to use the piggyback filter info instead of a } { record accessed via a handle, 11 November 1994; modified so that the bold outline is } { toggled even if the hilite state hasnıt changed, in case some external factor has } { influenced it, 2 October 1995; modified so that if the OK button is dimmed when there } { is no Cancel or Cancel is already dimmed, the bold outline is dimmed instead of } { toggled, 14 October 1995. } VAR newHilite: byte; itemType: integer; itemHandle: handle; cancelHandle: controlHandle; itemBox: rect; BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF dlogItemIsCtrl(itemType, itemHandle) THEN BEGIN newHilite:= ord(NOT activateIt) * 255; { 0 = active, 255 = dimmed } IF newHilite <> controlHandle(itemHandle)^^.contrlHilite THEN hiliteControl(controlHandle(itemHandle), newHilite); IF validFilterDialog(theDialog, false) & NOT filterPeek(theDialog)^.dontManageOK THEN IF filterPeek(theDialog)^.toggleOutline & (theItem = ok) & (dialogPeek(theDialog)^.aDefItem IN [ok, cancel]) THEN IF activateIt | NOT dlogGetCtrlItem(theDialog, cancel, cancelHandle) | (cancelHandle^^.contrlHilite <> 0) THEN dlogSetDefaultButton(theDialog, ok) ELSE dlogSetDefaultButton(theDialog, cancel) ELSE IF theItem = dialogPeek(theDialog)^.aDefItem THEN IF activateIt THEN dlogDrawBoldOutline(theDialog, theItem, boldActive) ELSE dlogDrawBoldOutline(theDialog, theItem, boldDim) END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogGetCurrentEditItem (theDialog: dialogPtr): integer; { Returns the item number of the currently active edit item. If there are no edit } { items in the dialog, zero is returned. } { Written by David Sinclair, 2 July 1994. } BEGIN dlogGetCurrentEditItem:= dialogPeek(theDialog)^.editField + 1 END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogGetCurrentEditSelection (theDialog: DialogPtr; VAR selStart, selEnd: Integer); { Returns the selection range of the currently active edit item. If there are no edit items in the } { dialog, zeroes are returned. } { Written by David Sinclair, 3 February 1996. } BEGIN IF DialogPeek(theDialog)^.editField >= 0 THEN BEGIN selStart:= DialogPeek(theDialog)^.textH^^.selStart; selEnd:= DialogPeek(theDialog)^.textH^^.selEnd; END ELSE BEGIN selStart:= 0; selEnd:= 0; END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE DlogCoerceEditable (theDialog: DialogPtr; theItem: Integer; toEditText, allowStatHits: Boolean); { Changes a statText item into an editText item, or vice versa. For allowStatHits, } { youıd normally pass false (or the constant deactivate), unless you want the new } { statText item to report hits. Hits are always allowed for editText items. NOTE: } { This routine will now work from a hit handler to dynamically change between the } { types. } { Written by David Sinclair, 25 July 1994; modified to dynamically adjust, 29 June 1997. } VAR itemType: Integer; itemHandle: Handle; itemBox: Rect; i: Integer; anyEdits: Boolean; BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF itemType IN [editText, itemDisable + editText, statText, itemDisable + statText] THEN BEGIN IF toEditText THEN itemType:= editText ELSE IF allowStatHits THEN itemType:= statText ELSE itemType:= itemDisable + statText; SetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); InsetRect(itemBox,-2,-2); {Remove or redraw the edit outline} EraseRect(itemBox); InsetRect(itemBox,-1,-1); InvalRect(itemBox); anyEdits:= False; i:= CountDITL(theDialog); REPEAT {Look for editText items} GetDialogItem(theDialog,i,itemType,itemHandle,itemBox); IF (itemType=editText) | (itemType=editText+itemDisable) THEN anyEdits:= True; i:= i - 1; UNTIL anyEdits | (i<=0); IF NOT anyEdits THEN DialogPeek(theDialog)^.editField:= -1; {There are no editTexts left} END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE handleItemStatusStuff (theDialog: dialogPtr; theItem: integer; {} VAR theText: str255); { Updates the item status information, ensures the text isnıt too long, and dims or } { undims the OK button as appropriate, etc. } { Written by David Sinclair, 3 July 1994; modifed to use the piggyback } { filter info instead of a record accessed via a handle, 11 November 1994. } VAR itemType, index, oldLength: integer; itemHandle: handle; itemBox: rect; updateText, selectText, dotSighted: boolean; BEGIN updateText:= false; selectText:= false; IF validFilterDialog(theDialog, false) THEN WITH filterPeek(theDialog)^ DO IF statusInited THEN BEGIN IF theItem IN [1..maxStatusItems] THEN { theItem will be zero if calling while } { setting up in dlogDialog } BEGIN oldLength:= length(theText); IF colonsIllegal.asBool[theItem] & (pos(':', theText) <> 0) THEN theText:= strReplace(theText, ':', null); IF menuMetasIllegal.asBool[theItem] THEN BEGIN theText:= strReplace(strReplace(strReplace(theText, ';', null), '^', null), '!', null); theText:= strReplace(strReplace(strReplace(theText, '<', null), '/', null), '(', null); END; IF digitsOnly.asBool[theItem] | decimalsOnly.asBool[theItem] THEN BEGIN index:= 1; dotSighted:= digitsOnly.asBool[theItem]; IF theText <> null THEN REPEAT IF NOT (theText[index] IN [period, '0'..'9']) | ((theText[index] = period) & dotSighted) THEN delete(theText, index, 1) ELSE BEGIN IF (theText[index] = period) THEN dotSighted:= true; index:= index + 1 END UNTIL index > length(theText); END; IF blankShownZero.asBool[theItem] & (theText = null) THEN BEGIN theText:= '0'; updateText:= true; selectText:= true; END; IF zeroIllegal.asBool[theItem] & (strToNum(theText) = 0) THEN BEGIN theText:= '1'; updateText:= true; selectText:= true; END; IF length(theText) = 255 THEN { It might actually be longer, so truncate } updateText:= true; IF length(theText) > maxLength[theItem] THEN theText:= copy(theText, 1, maxLength[theItem]); { Truncate if too long } updateText:= (oldLength <> length(theText)) | updateText; IF updateText THEN BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); SetDialogItemText(itemHandle, theText) END; IF selectText THEN SelectDialogItemText(theDialog, theItem, 0, maxInt); dimOK.asBool[theItem]:= (theText = null) & dimOKIfEditNull.asBool[theItem]; END; { Is this item null, and do we care? } index:= maxStatusAsLongs; WHILE (index > 0) & (dimOK.asLong[index] = 0) DO { Are there any (significant) } index:= index - 1; { null edit items? } OKActive:= index = 0; IF NOT dontManageOK THEN dlogHiliteButton(theDialog, ok, OKActive & NOT keepOKDimmed); END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogGetItemText (theDialog: dialogPtr; theItem: integer): str255; { Returns the current text of the specified statText or editText item, and handles } { options set via dlogSetItemStatus, if any. } { Written by David Sinclair, 14 June 1994. } VAR itemType: integer; itemHandle: handle; itemBox: rect; theText: str255; BEGIN IF (theDialog <> NIL) & (theItem > 0) THEN BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF itemType IN [statText, itemDisable + statText, editText, itemDisable + editText] THEN GetDialogItemText(itemHandle, theText) ELSE theText:= null; IF itemType IN [editText, itemDisable + editText] THEN handleItemStatusStuff(theDialog, theItem, theText); END ELSE theText:= null; dlogGetItemText:= theText END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogSetItemText (theDialog: dialogPtr; theItem: integer; theText: str255); { Changes the contents of the specified statText or editText item, and handles } { options set via dlogSetItemStatus, if any. The text is only changed if it is different } { from what is already there. } { Written by David Sinclair, 14 June 1994. } VAR itemType: integer; itemHandle: handle; itemBox: rect; oldText: str255; BEGIN IF (theDialog <> NIL) & (theItem > 0) THEN BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF itemType IN [statText, itemDisable + statText, editText, itemDisable + editText] THEN BEGIN GetDialogItemText(itemHandle, oldText); IF (theText = null) | (theText <> oldText) THEN BEGIN IF itemType IN [editText, itemDisable + editText] THEN handleItemStatusStuff(theDialog, theItem, theText); SetDialogItemText(itemHandle, theText); END END END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogGetTextPattern (theDialog: DialogPtr; theItem: Integer; textPattern: Str255; {} options: Integer): Str255; { Similar to dlogGetItemText, above, but also formats the text using the specified pattern. } { . } { Written by David Sinclair, 3 February 1996. } { ĥĥĥ This routine is not yet implemented; I was going to use it in Dejal Register, but decided that it } { wasnıt useful enough for the overhead. Design thoughts, in case I decide to implement it in the } { future: } { The Get routine can either return the text in a formatted or unformatted state ‹ in either case the } { text is formatted in the edit field, but the return value is formatted or not. The Set routine always } { formats the text. For both you might be able to specify whether to format the text with or without } { sample text ‹ e.g. ³mm/yy² would be displayed as ³11/yy² or ³11² if ³11² was entered ‹ in which } { case Iıd have to select the first ³y² so that subsequent typing changes that, and Iıd adjust the selection } { range each time the user types. Tabbing wouldnıt be a problem, since the user could replace all the } { text, leaving the sample text. For Get, get the text from the edit field, call dlogSetTextPattern to format } { and set the text, then set the selection if necessary. } BEGIN dlogGetTextPattern:= dlogGetItemText(theDialog, theItem); END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogEllipsisText (theDialog: dialogPtr; theItem: integer; {} leftText, rightText: str255); { Changes the contents of the specified statText item such that it is ellipsised if } { necessary. The text is only changed if it is different from what is already there. } { Written by David Sinclair, 18 July 1994. } VAR itemType: integer; itemHandle: handle; itemBox: rect; oldText: str255; BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF itemType IN [statText, itemDisable + statText] THEN BEGIN GetDialogItemText(itemHandle, oldText); leftText:= strEllipsis(leftText, rightText, itemBox.right - itemBox.left); IF (leftText = null) | (leftText <> oldText) THEN SetDialogItemText(itemHandle, leftText); END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE DlogSetDrawingProc (theDialog: DialogPtr; theItem: Integer; theProcedure: UserItemProcPtr); { Installs the specified routine as the drawing procedure for the userItem. } { Pass @routineName for theProcedure. Note: you must call DlogDisposeDrawingProc } { before closing the dialog, to avoid fragmenting memory. } { Written by David Sinclair, 1 June 1994; made into a genDialogs routine, } { 4 June 1994; added UPP support, 31 May 1997. } VAR itemType: Integer; itemHandle: Handle; itemBox: Rect; theUPP: UserItemUPP; BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF (theProcedure <> NIL) & (itemType IN [userItem, itemDisable + userItem]) THEN BEGIN theUPP:= NewUserItemProc(theProcedure); SetDialogItem(theDialog, theItem, itemType, Pointer(theUPP), itemBox); END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE DisposeAllDrawingProcs(theDialog: DialogPtr); { Removes the previously assigned routines as the drawing procedures for all userItems. Called by the dialog routines when a dialog is closed. } { Written by David Sinclair, 31 May 1997. } VAR itemType: Integer; itemHandle: Handle; itemBox: Rect; count: Integer; index: Integer; BEGIN count:= DlogCountDItems(theDialog); FOR index:= 1 TO count DO BEGIN GetDialogItem(theDialog, index, itemType, itemHandle, itemBox); IF (itemHandle <> Nil) & (itemType IN [userItem, itemDisable + userItem]) THEN BEGIN DisposeRoutineDescriptor(UniversalProcPtr(itemHandle)); SetDialogItem(theDialog, index, itemType, Nil, itemBox); END; END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogChangeCtrlTitle (theDialog: dialogPtr; theItem: integer; {} newTitle: str255); { Changes the title of the specified control to the specified string, but only if the } { title has changed. } { Written by David Sinclair, 28 September 1992; made into a genDialogs routine } { and added itemType checking, 4 June 1994; added check that the text has changed, } { 24 July 1994. } VAR itemType: integer; itemHandle: handle; itemBox: rect; oldTitle: str255; BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF dlogItemIsCtrl(itemType, itemHandle) THEN BEGIN GetControlTitle(controlHandle(itemHandle), oldTitle); IF newTitle <> oldTitle THEN SetControlTitle(controlHandle(itemHandle), newTitle) END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogEllipsisCtrlTitle (theDialog: dialogPtr; theItem: integer; {} leftTitle, rightTitle: str255); { Changes the title of the specified control to the title made by adding the specified left } { and right parts such that the leftTitle is ellipsed if itıs too long to fit in the control. } { Especially useful for buttons with dynamic titles. The title is only changed if the } { new title is different from the previous one. } { Written by David Sinclair, 24 July 1994. } VAR itemType, allowance: integer; itemHandle: handle; itemBox: rect; oldTitle: str255; BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF dlogItemIsCtrl(itemType, itemHandle) THEN BEGIN IF itemType IN [ctrlItem + btnCtrl, itemDisable + ctrlItem + btnCtrl] THEN allowance:= 6 ELSE allowance:= 10; leftTitle:= strEllipsis(leftTitle, rightTitle, itemBox.right - itemBox.left - allowance); GetControlTitle(controlHandle(itemHandle), oldTitle); IF leftTitle <> oldTitle THEN SetControlTitle(controlHandle(itemHandle), leftTitle) END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogDontExportClipboard (theDialog: dialogPtr); { Normally, dlogDialog will export the TE scrap contents to the Clipboard when the dialog is } { closed. Call this routine if you manipulate the Clipboard yourself, to avoid reverting the } { contents. } { Written by David Sinclair, 2 October 1995. } BEGIN IF validFilterDialog(theDialog, false) THEN filterPeek(theDialog)^.exportTEScrap:= false END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogUseFontSize (theDialog: DialogPtr; theFont, theSize: Integer); { Makes the dialog use the specified font and size instead of the usual 12-point system font. } { Written by David Sinclair, 15 January 1996. } VAR info: FontInfo; BEGIN IF theDialog <> NIL THEN BEGIN TextFont(theFont); TextSize(theSize); GetFontInfo(info); DialogPeek(theDialog)^.textH^^.txFont:= theFont; DialogPeek(theDialog)^.textH^^.txSize:= theSize; DialogPeek(theDialog)^.textH^^.lineHeight:= info.ascent + info.descent + info.leading; DialogPeek(theDialog)^.textH^^.fontAscent:= info.ascent; IF DialogPeek(theDialog)^.textH^^.teLength > 0 THEN TECalText(DialogPeek(theDialog)^.textH); END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE flashDialogItem (theDialog: dialogPtr; itemNo: integer); { Flashes the selected button a bit more than modelDialog does, for added effect. } { Taken from Paul Pottsı program. Called by handleEventFilter. } CONST delayTicks = 10; VAR finalTicks: Longint; itemType: integer; itemHandle: handle; itemBox: rect; BEGIN IF itemNo <> itemNotFound THEN BEGIN GetDialogItem(theDialog, itemNo, itemType, itemHandle, itemBox); IF dlogItemIsCtrl(itemType, itemHandle) THEN BEGIN hiliteControl(controlHandle(itemHandle), 1); delay(delayTicks, finalTicks); hiliteControl(controlHandle(itemHandle), 0) END END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE findDefaultButton (theDialog: dialogPtr; VAR theItem: integer; {} VAR theBox: rect; ignoreHiliting: boolean); { Finds which button is the default, and returns its item number and rectangle. Used by } { both handleDialogUpdate & handleEventFilter. } { Written by David Sinclair, 21 September 1990. } VAR theType: integer; itemHandle: handle; BEGIN theItem:= dialogPeek(theDialog)^.aDefItem; IF theItem > 0 THEN GetDialogItem(theDialog, theItem, theType, itemHandle, theBox) ELSE theType:= 0; IF theType <> ctrlItem + btnCtrl THEN theItem:= itemNotFound ELSE IF (controlHandle(itemHandle)^^.contrlHilite = 255) & NOT ignoreHiliting THEN theItem:= itemNotFound END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION findEquivButton (theDialog: dialogPtr; theKey: Char): integer; { Function to determine what, if any, item in the dialog (i.e. a button) corresponds to the } { keyboard equivalent the user typed. Works out what the key equivs are by looking for } { the character after a command-symbol in the button title, or failing that the initial } { character of the buttonıs title. Used by handleEventFilter. } { Written by David Sinclair, 11 August 1990; fixed bug with NIL deref if no match is } { found, 2 July 1992. } VAR foundIt: boolean; buttonTitle: str255; keyEquiv: Char; keyStr: Str1; theItem, itemType: integer; itemHandle: handle; itemBox: rect; BEGIN {€ findDefaultButton(theDialog, defaultButton, itemBox, true);€} foundIt:= false; theItem:= zerothItem; IF theKey IN [escChar, period] THEN theKey:= 'C'; REPEAT theItem:= dlogSearchDItems(theDialog, theItem + 1, ctrlItem + btnCtrl, itemHandle, itemBox); IF theItem = itemNotFound THEN foundIt:= true ELSE {€if theItem <> defaultButton then€} BEGIN GetControlTitle(controlHandle(itemHandle), buttonTitle); keyStr:= Copy(buttonTitle, pos(commandSymbol, buttonTitle) + 1, 1); keyEquiv:= keyStr[1]; { This will give the character after the command } { symbol if there is one, or the initial char if not } IF keyEquiv = theKey THEN foundIt:= true ELSE BEGIN strSwapCharCase(keyEquiv); IF keyEquiv = theKey THEN foundIt:= true END END UNTIL foundIt; IF theItem <> itemNotFound THEN BEGIN GetDialogItem(theDialog, theItem, itemType, itemHandle, itemBox); IF controlHandle(itemHandle)^^.contrlHilite = 255 THEN theItem:= itemNotFound END; findEquivButton:= theItem END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} {$IFC application} FUNCTION getNMIconHandle: handle; { Returns a handle to an icon with the appropriate bit depth. } { Written by David Sinclair, 25 January 1995. } VAR returnHndl: handle; err: osErr; PROCEDURE addIcon (iconType: resType); { Subroutine to get and add the specified bit depth of icon to the icon suite. } VAR iconHndl: handle; BEGIN err:= numGetResource(iconHndl, iconType, notificationIconID); IF err = noErr THEN BEGIN HNoPurge(iconHndl); err:= AddIconToSuite(iconHndl, returnHndl, iconType) END; END; BEGIN err:= NewIconSuite(returnHndl); IF err <> noErr THEN returnHndl:= NIL ELSE BEGIN addIcon(small1BitMask); addIcon(small4BitData); addIcon(small8BitData); END; getNMIconHandle:= returnHndl END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION getNMSoundHandle (eventString: str255; VAR releaseWhenDone: boolean): handle; { Returns the appropriate sound handle to pass to the notification request, based on the } { event code. True is passed back in releaseWhenDone if you should call releaseResource } { for the returned sound handle before disposing of the NM record. If it is false, donıt } { touch it. } { Written by David Sinclair, 8 July 1992; moved to genDialogs, 20 January 1995. } VAR returnHndl: handle; BEGIN releaseWhenDone:= false; returnHndl:= NIL; IF eventString = notifyWithBeep THEN { Play the system beep } returnHndl:= handle(-1) ELSE IF eventString <> notifyWithSilence THEN { Play a user-specified sound } BEGIN returnHndl:= getNamedResource('snd ', eventString); IF returnHndl = NIL THEN returnHndl:= handle(-1) ELSE BEGIN hNoPurge(returnHndl); releaseWhenDone:= true END END; getNMSoundHandle:= returnHndl END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE notifyForForeground (eventString: str255; iconInForeground, soundInForeground: boolean); { If we are in the background, queues a notification request then waits until the user brings us into } { the foreground. Handles update events in the meantime. In either case, this routine plays any } { appropriate sound as specified by the user. Pass a valid sound name or one of the other } { constants (see dlogSetNotificationSnd, below), and pass true for iconInForeground and } { soundInForeground if the icon and sound are allowed to be used in the foreground as well as } { in the background. } { Written by David Sinclair, 14 November 1991, with the osEvt handler based on the } { code in SoundApp, an excellent DTS sample program; check for background and playing } { the prefs sounds added 8 July 1992; moved to genDialogs, 20 January 1995. } CONST getHighByte = 24; { 24 bits (three bytes) to shift right } theSleepTime = 60; VAR myNotification: NMRec; myErr: osErr; theEvent: eventRecord; oldPort: grafPtr; theWindow: windowPtr; index: integer; releaseWhenDone: boolean; BEGIN IF iconInForeground | soundInForeground | inBackground THEN WITH myNotification DO BEGIN qType:= ord(nmType); { Set queue type } nmMark:= ord(inBackground); { Put mark in Application menu if in background, } { or in the Apple menu if in the foreground } IF iconInForeground | inBackground THEN nmIcon:= getNMIconHandle { Get the icon to alternate in the menubar } ELSE nmIcon:= NIL; IF soundInForeground | inBackground THEN nmSound:= getNMSoundHandle(eventString, releaseWhenDone) ELSE nmSound:= NIL; nmStr:= NIL; { Donıt put up an alert } nmResp:= NIL; { No response procedure } nmRefCon:= 0 { Donıt need a refCon } END; myErr:= NMInstall(@myNotification); { Install the notification request } IF myErr = noErr THEN BEGIN FOR index:= 1 TO 10 DO { Get the notification started } systemTask; IF myNotification.nmIcon <> NIL THEN REPEAT IF waitNextEvent(osMask + updateMask, theEvent, theSleepTime, NIL) THEN CASE theEvent.what OF osEvt: { osEvt code based on SoundApp } BEGIN IF BSR(theEvent.message, getHighByte) = suspendResumeMessage THEN IF BAnd(theEvent.message, resumeFlag) <> 0 THEN inBackground:= FALSE {it was a resume event} ELSE inBackground:= TRUE {it was a suspend event} END; updateEvt: IF windowPeek(theEvent.message)^.windowKind = dialogKind THEN BEGIN { Repaint a dialog } getPort(oldPort); theWindow:= windowPtr(theEvent.message); setPort(theWindow); beginUpdate(theWindow); drawDialog(theWindow); endUpdate(theWindow); setPort(oldPort) END; OTHERWISE ; { Ignore other events } END UNTIL NOT inBackground; myErr:= NMRemove(@myNotification); { Remove the notification request } WITH myNotification DO BEGIN IF nmIcon <> NIL THEN myErr:= DisposeIconSuite(nmIcon, true); IF releaseWhenDone & (nmSound <> NIL) & (nmSound <> handle(-1)) THEN releaseResource(nmSound); END END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogSetNotificationSnds (soundForErrors, soundForAttention: str255); { Sets the sounds to play when displaying error dialogs and other dialogs (while in the background). } { Pass either a valid sound name (which shouldnıt be longer than 63 characters), or the constant } { notifyWithSilence to not play any sound, or notifyWithBeep to play a system beep. } { Written by David Sinclair, 20 January 1995. } BEGIN notifyErrorSound:= copy(soundForErrors, 1, 63); notifyAttentionSound:= copy(soundForAttention, 1, 63); END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogNotifyForAttention; { Call this if you display a dialog through some other method than calling a genDialogs routine } { to inherit the normal notification behaviour. (The genProgressDialogs routines call this routine } { for you.) } { Written by David Sinclair, 20 January 1995. } BEGIN notifyForForeground(notifyAttentionSound, false, false); END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogNotifyOfCompletion (soundForCompletion: str255); { Call this when an operation is complete, to play the specified sound and blink the applicationıs } { icon in the menubar. Pass either a valid sound name or one of the constants notifyWithSilence } { or notifyWithBeep. } { Written by David Sinclair, 20 January 1995. } BEGIN notifyForForeground(soundForCompletion, true, true); END; {$ENDC} {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogLastEvent (theDialog: DialogPtr): EventRecord; { Returns the most recent event received for this dialog. } { Written by David Sinclair, 16 January 1996. } VAR theEvent: EventRecord; BEGIN IF ValidFilterDialog(theDialog, false) THEN theEvent:= FilterPeek(theDialog)^.lastEvent ELSE theEvent.what:= nullEvent; dlogLastEvent:= theEvent END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogTrackCursor (theDialog: DialogPtr); { Changes the cursor shape to whatever is appropriate at the momemt. You only need to call this } { if you are handling cursor tracking manually, and you donıt currently need a custom cursor. By } { default, cursor tracking is automatic. } { Written by David Sinclair, 11 August 1990; made publically available, 22 February 1997. } VAR mouseLoc: point; count, itemNo: integer; itemHandle: handle; itemBox: rect; useIBeam: boolean; BEGIN GetMouse(mouseLoc); useIBeam:= false; count:= firstItem; REPEAT itemNo:= dlogSearchDItems(theDialog, count, editText, itemHandle, itemBox); IF itemNo <> itemNotFound THEN IF PtInRect(mouseLoc, itemBox) THEN useIBeam:= true ELSE count:= itemNo + 1 UNTIL (itemNo = itemNotFound) | useIBeam; IF useIBeam THEN SetCursor(GetCursor(iBeamCursor)^^) ELSE {$IFC application} SetCursor(globals.qd.arrow); {$ELSEC} SetCursor(GetCursor(arrowCursor)^^); {$ENDC} END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogSetTrackCursor (theDialog: DialogPtr; trackCursor: Boolean); { Call this routine to enable or disable automatic cursor tracking. If you disable automatic cursor } { tracking, call dlogTrackCursor, above, if after checking for a custom cursor, one isnıt required. } { Written by David Sinclair, 22 February 1997. } BEGIN IF ValidFilterDialog(theDialog, false) THEN FilterPeek(theDialog)^.trackCursor:= trackCursor END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE HandleDrawGrow (theDialog: DialogPtr); { Draws the grow icon, without drawing the scrollbar lines too. } { Written by David Sinclair, 15 February 1997. } VAR growRect: Rect; oldClip: RgnHandle; newClip: RgnHandle; BEGIN oldClip:= NewRgn; IF oldClip <> NIL THEN GetClip(oldClip); growRect:= theDialog^.portRect; growRect.left:= growRect.right - 15; growRect.top:= growRect.bottom - 15; newClip:= NewRgn; IF newClip <> NIL THEN BEGIN RectRgn(newClip, growRect); SetClip(newClip); DisposeRgn(newClip); END; DrawGrowIcon(theDialog); IF oldClip <> NIL THEN BEGIN SetClip(oldClip); DisposeRgn(oldClip); END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE HandleDialogUpdate (theDialog: dialogPtr); { This is used for dialog drawing such as the default button: used by the } { HandleEventFilter and dlogModelessEvent. } { Written by David Sinclair, 11 August 1990. Based on Kirk Chaseıs code; modifed to } { use the piggyback filter info instead of a record accessed via a handle, 11 November } { 1994; added call to HandleDrawGrow, 15 February 1997. } VAR itemNo: integer; itemBox: rect; BEGIN IF ValidFilterDialog(theDialog, false) THEN BEGIN IF FilterPeek(theDialog)^.growable THEN HandleDrawGrow(theDialog); FindDefaultButton(theDialog, itemNo, itemBox, true); IF itemNo <> itemNotFound THEN dlogDrawBoldOutline(theDialog, itemNo, FilterPeek(theDialog)^.boldOutline); END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION HandleTab (theDialog: DialogPtr; VAR itemHit: Integer): Boolean; { Supports Tabbing through edit items. Implemented as a routine so I can support keyboard targetting } { one day. } VAR itemHandle: Handle; itemBox: Rect; count: Integer; BEGIN count:= dlogCountDItems(theDialog); itemHit:= dlogGetCurrentEditItem(theDialog); IF itemHit > 0 THEN BEGIN itemHit:= dlogSearchDItems(theDialog, itemHit + 1, editText, itemHandle, itemBox); IF itemHit = itemNotFound THEN itemHit:= dlogSearchDItems(theDialog, 1, editText, itemHandle, itemBox); SelectDialogItemText(theDialog, itemHit, 0, maxInt); END; HandleTab:= (itemHit <> itemNotFound) END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION HandleShiftTab (theDialog: DialogPtr; VAR itemHit: Integer): Boolean; { Supports Shift-Tabbing through edit items. } VAR itemHandle: Handle; itemBox: Rect; BEGIN itemHit:= dlogGetCurrentEditItem(theDialog); IF itemHit > 0 THEN BEGIN itemHit:= dlogSearchPrevDItems(theDialog, itemHit - 1, editText, itemHandle, itemBox); IF itemHit = itemNotFound THEN itemHit:= dlogSearchPrevDItems(theDialog, dlogCountDItems(theDialog), editText, itemHandle, itemBox); SelectDialogItemText(theDialog, itemHit, 0, maxInt); END; HandleShiftTab:= (itemHit <> itemNotFound) END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE HandleMenuCommand (theDialog: DialogPtr; menuSelection: Longint; VAR itemHit: Integer); { Handles menu commands for the dialog, if and as appropriate. } { Written by David Sinclair, 6 July 1994; added support for the Select All command and the menu } { handler proc, 29 January 1996. } CONST normalEditMenuID = 130; cutItem = 3; copyItem = 4; pasteItem = 5; clearItem = 6; selectAllItem = 8; VAR menuID: Integer; menuItem: Integer; currentEdit: Integer; BEGIN menuID:= HiWord(menuSelection); menuItem:= LoWord(menuSelection); itemHit:= 999; IF menuID <> 0 THEN BEGIN IF (menuID = normalEditMenuID) & (menuItem <= selectAllItem) THEN BEGIN CASE menuItem OF cutItem: DialogCut(theDialog); copyItem: DialogCopy(theDialog); pasteItem: DialogPaste(theDialog); clearItem: DialogDelete(theDialog); selectAllItem: BEGIN currentEdit:= dlogGetCurrentEditItem(theDialog); IF currentEdit > 0 THEN SelectDialogItemText(theDialog, currentEdit, 0, maxInt); END; OTHERWISE ; END; { of case } itemHit:= dlogGetCurrentEditItem(theDialog); END; END; HiliteMenu(0); END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE HandleMenuBarClick (theDialog: DialogPtr; theEvent: EventRecord; VAR itemHit: Integer); { Checks for a mouse down in the menu bar under System 7.0 & handles it by tracking the } { menus if there is one‹only the Show Balloons Help menu item should be enabled. } { Written by David Sinclair, 12 November 1991; modified to support a menu handler proc, } { 29 January 1996. } VAR response: Longint; theWindow: WindowPtr; menuResult: Longint; BEGIN IF Gestalt(gestaltHelpMgrAttr, response) = noErr THEN IF BitTst(@response, 31 - gestaltHelpMgrPresent) THEN IF FindWindow(theEvent.where, theWindow) = inMenuBar THEN BEGIN InitCursor; menuResult:= MenuSelect(theEvent.where); HandleMenuCommand(theDialog, menuResult, itemHit); SetCursor(GetCursor(watchCursor)^^); END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION handleEventFilter (theDialog: dialogPtr; VAR theEvent: eventRecord; VAR itemHit: integer): boolean; { Filter proc for modal dialogs. Handles disk inserts, key equivs, updating & cursor changing. } { Written by David Sinclair, 11 August 1990, with code from Paul Potts & Kirk Chase. } { Major changes 19-22 September 1990; fixed illegal keyboard clicking bug, 15 December } { 1991; modifed to use the piggyback filter info instead of a record accessed via a } { handle, 11 November 1994. } CONST selectAllCmd = $00820008; { i.e. 130 in the high byte, 8 in the low byte } VAR key: Char; { Holds the key pressed } itemBox: rect; currentEditItem: integer; diResult: integer; { Returned by diBadMount } diskEvent: eventRecord; { Returned by getNextEvent, below } return, noEditText, cmdKeyDown: boolean; theUPP: ModalFilterUPP; menuSelection: longint; BEGIN return:= false; itemHit:= 0; SetPort(theDialog); { Make sure the dialogıs port is active } IF validFilterDialog(theDialog, false) THEN BEGIN {€ HandleMenuAvailablity(theDialog, false);€} theUPP:= FilterPeek(theDialog)^.eventFilterUPP; IF theUPP <> NIL THEN return:= CallModalFilterProc(theDialog, theEvent, itemHit, theUPP); IF return = false THEN BEGIN { Handle disk mounts } IF getNextEvent(diskMask, diskEvent) = true THEN IF hiWord(diskEvent.message) <> noErr THEN BEGIN {$IFC application} diskEvent.where.h:= ((globals.qd.screenbits.bounds.right - globals.qd.screenbits.bounds.left) DIV 2) - (304 DIV 2); diskEvent.where.v:= ((globals.qd.screenbits.bounds.bottom - globals.qd.screenbits.bounds.top) DIV 3) - (104 DIV 2); {$ELSEC} diskEvent.where.h:= 104; { Default to compact Mac values when screenBits } diskEvent.where.v:= 62; { is unavailable (if running as INIT, FKEY etc) } {$ENDC} initCursor; diResult:= diBadMount(diskEvent.where, diskEvent.message) END; { Disk events } setPort(theDialog); { Make sure the dialogıs port is active } { Change the cursor shape to whatever is appropriate at the moment } IF FilterPeek(theDialog)^.trackCursor THEN dlogTrackCursor(theDialog); { Handle other event types } CASE theEvent.what OF updateEvt: BEGIN handleDialogUpdate(theDialog); return:= false END; autoKey: IF (BitAnd(theEvent.message, charCodeMask) = tabKey) & (dlogGetCurrentEditItem(theDialog) > 0) THEN IF BitAnd(theEvent.modifiers, shiftKey) <> 0 THEN return:= HandleShiftTab(theDialog, itemHit) ELSE return:= HandleTab(theDialog, itemHit); keyDown: IF filterPeek(theDialog)^.buttonless THEN BEGIN itemHit:= 1; return:= true { Exit modalDialog } END ELSE BEGIN key:= Chr(bitAnd(theEvent.message, charCodeMask)); currentEditItem:= dlogGetCurrentEditItem(theDialog); CASE ord(key) OF { If a key has been pressed, we want to interpret it properly } returnKey, enterKey: IF (ord(key) = enterKey) | (currentEditItem < 1) | NOT filterPeek(theDialog)^.statusInited | NOT filterPeek(theDialog)^.multiLineEdit.asBool[currentEditItem] THEN BEGIN findDefaultButton(theDialog, itemHit, itemBox, false); IF itemHit <> itemNotFound THEN BEGIN flashDialogItem(theDialog, itemHit); return:= true { Exit modalDialog } END ELSE theEvent.what:= nullEvent { Ignore the key } END; tabKey: IF (BitAnd(theEvent.message, charCodeMask) = tabKey) & (currentEditItem > 0) THEN IF BitAnd(theEvent.modifiers, shiftKey) <> 0 THEN return:= HandleShiftTab(theDialog, itemHit) ELSE return:= HandleTab(theDialog, itemHit); OTHERWISE { Maybe a key equivŠ } BEGIN noEditText:= currentEditItem < 1; cmdKeyDown:= bitAnd(theEvent.modifiers, cmdKey) <> 0; IF noEditText | (NOT noEditText & cmdKeyDown) | (key = escChar) THEN { If there is an editText item in the dialog, we need to } { worry about whether the Command key is down } { or not, otherwise sure ya worry! } BEGIN menuSelection:= 0; IF cmdKeyDown THEN { Look for a menu equiv first } BEGIN {$IFC NOT UNDEFINED menuEquivs} menuSelection:= menuKey(key); { A bus error will result if this is } { called in an app with no menus } {$ENDC} IF key IN ['A', 'a'] THEN menuSelection:= selectAllCmd; HandleMenuCommand(theDialog, menuSelection, itemHit); IF itemHit <> 0 THEN return:= true ELSE IF NOT noEditText THEN BEGIN itemHit:= currentEditItem; return:= true END END; IF hiWord(menuSelection) = 0 THEN { Then look for a button equiv } BEGIN itemHit:= findEquivButton(theDialog, key); IF itemHit <> itemNotFound THEN BEGIN flashDialogItem(theDialog, itemHit); return:= true END END END END END { Key code case } END; { KeyDown } mouseDown: BEGIN HandleMenuBarClick(theDialog, theEvent, itemHit); { Check for & handle menu bar clicks } IF itemHit <> 0 THEN return:= true ELSE { If there are no buttons, dismiss dialog with a mouse click } IF filterPeek(theDialog)^.buttonless THEN BEGIN itemHit:= 1; return:= true END ELSE return:= false END; OTHERWISE return:= false; { We donıt handle any other types of events, } { so these get sent to modalDialog unchanged } END { Event case } END; { If } filterPeek(theDialog)^.lastEvent:= theEvent; END; handleEventFilter:= return END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE HandleItemHit (theDialog: DialogPtr; VAR itemHit: Integer); { Handles built-in item hit filtering, then calls the userıs hit handling routine, if any. } { If autoStatus is true, the item status filtering is done automatically, otherwise itıs } { left up to the client program (which would probably do it anyway by calling the } { dlogGetItemText routine). } { Written by David Sinclair, 2 July 1994; added UPP support, 31 May 1997. } VAR itemType: Integer; itemHandle: Handle; itemBox: Rect; itemText: Str255; BEGIN SetPort(theDialog); { Make sure the dialogıs port is active } IF ValidFilterDialog(theDialog, false) THEN BEGIN IF itemHit > 0 THEN IF FilterPeek(theDialog)^.autoStatus THEN BEGIN GetDialogItem(theDialog, itemHit, itemType, itemHandle, itemBox); IF itemType IN [editText, itemDisable + editText] THEN itemText:= DlogGetItemText(theDialog, itemHit); { All the filtering is done in here } END ELSE ELSE HandleItemStatusStuff(theDialog, 0, itemText); { Hilite the OK button correctly } IF FilterPeek(theDialog)^.itemHitFilterUPP <> NIL THEN CallItemHitProc(theDialog, itemHit, FilterPeek(theDialog)^.itemHitFilterUPP); END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} {$IFC application} PROCEDURE dlogCentreWindow (theDialog: dialogPtr); { Centres the dialog (or window) on the screen. Currently only looks at the main screen. } { Written by David Sinclair, 10 July 1992; removed check for System 7, 16 February 1997. } CONST heightOfMBar = 20; VAR oldPort: grafPtr; position: point; BEGIN IF theDialog <> NIL THEN WITH theDialog^, position, globals.qd.screenbits DO BEGIN getPort(oldPort); setPort(theDialog); h:= (bounds.right - portRect.right) DIV 2; { Remember: portRect is local } v:= (bounds.bottom - heightOfMBar - portRect.bottom) DIV 2 + heightOfMBar; moveWindow(theDialog, h, v, false); setPort(oldPort) END END; {$ENDC} {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION DlogDialog (dialogID: Integer; dlogSetupProc: SetupDialogProcPtr; eventFilterProc: ModalFilterProcPtr; itemHitFilterProc: ItemHitProcPtr; str0, str1, str2, str3: Str255): Integer; { Routine to handle dialogs completely. If you want to include anything fancy not } { already handled, e.g. special interpreting of keypresses or whatever, specify your } { own event filter proc, which will be called before my custom filter proc or the standard } { one does anything. For even more flexablity and power, specify a routine to call } { straight after setting up the dialog but before displaying it or calling ModalDialog, } { and / or a routine to call just after ModalDialog returns. If you wish to use } { expanding / contracting statText items like System 7 does for its alerts, and support } { automatic plural handling, either pass @dlogExpandingText as the setup proc (if } { you donıt want your own setup proc), or call dlogExpandingText from your setup } { proc. If you specify an itemHitFilterProc procedure, it will be called with the special } { itemHit value of hookFirstCall once the dialog has been displayed and drawn but } { before any events are handled. } { Procedure declarations: } { procedure myDlogSetupProc (theDialog: dialogPtr); } { function myEventFilterProc (theDialog: dialogPtr; var theEvent: eventRecord; } { var itemHit: integer): boolean; } { procedure myItemHitProc (theDialog: dialogPtr; var itemHit: integer); } { Written by David Sinclair, 11 August 1990, with code by Kirk Chase. } { Modified by David Sinclair, 19 September 1990, to handle filterProcs & eventFilters; } { hookFirstCall added 22 May 1993; modified to save and restore ParamText strings, } { 25 June 1994; modifed to use the piggyback filter info instead of a record accessed via } { a handle, 11 November 1994; exports scrap at start if a sub-dialog, 4 February 1996; { added UPP support, 27 April 1997. } VAR oldPort: GrafPtr; theDialog: DialogPtr; dlogEventFilterUPP: ModalFilterUPP; index, itemHit, itemType: Integer; itemHandle: Handle; itemBox: Rect; buttonless: Boolean; DAStrings: DAStringsArray; oldParams: ARRAY[0..3] OF Str255; ignored: OSErr; BEGIN GetPort(oldPort); SetCursor(getCursor(watchCursor)^^); IF ValidFilterDialog(oldPort, false) THEN { If this is a sub-dialog, export the scrap to preserve it } BEGIN ignored:= ZeroScrap; ignored:= TEToScrap; END; DAStrings:= DAStringsPtr(ptr(DAStringsGlob))^; FOR index:= 0 TO 3 DO { Save the previous ParamText strings, JIC } IF DAStrings[index] <> NIL THEN oldParams[index]:= DAStrings[index]^^ ELSE oldParams[index]:= null; ParamText(str0, str1, str2, str3); theDialog:= DialogPtr(NewPtr(SizeOf(FilterDialogRec))); { Allocate the space for the dialog and filter info } IF (resError = noErr) & (theDialog <> NIL) THEN theDialog:= GetNewDialog(dialogID, Ptr(theDialog), Pointer(-1)); { Read the dialog resource } IF (resError <> noErr) | (theDialog = NIL) THEN { Major problem! } BEGIN ResReallyBadError; DlogDialog:= 0 END ELSE BEGIN {$IFC application} DlogCentreWindow(theDialog); { Centre the dialog on the screen } {$ENDC} dlogEventFilterUPP:= NewModalFilterProc(@HandleEventFilter); InitFilterInfo(theDialog, eventFilterProc, itemHitFilterProc, 0); buttonless:= FilterPeek(theDialog)^.buttonless; SetPort(theDialog); IF dlogSetupProc <> NIL THEN BEGIN CallSetupDialogProc(theDialog,dlogSetupProc); { Allow setting up radio buttons etc } HandleItemStatusStuff(theDialog, 0, str3); { Dim OK button if necessary } END; {€ HandleMenuAvailablity(theDialog, false);€} itemHit:= hookSetupInvisible; HandleItemHit(theDialog, itemHit); IF (dialogID = osErrorDialogID) | (dialogID = osWarningDialogID) THEN NotifyForForeground(notifyErrorSound, false, true) ELSE NotifyForForeground(notifyAttentionSound, false, false); ShowWindow(theDialog); SetPort(theDialog); BeginUpdate(theDialog); HandleDialogUpdate(theDialog); DrawDialog(theDialog); EndUpdate(theDialog); ignored:= TEFromScrap; InitCursor; itemHit:= hookSetupVisible; HandleItemHit(theDialog, itemHit); REPEAT ModalDialog(dlogEventFilterUPP, itemHit); { Process all events and then some! } HandleItemHit(theDialog, itemHit); { Allow user checking of result } IF itemHit > 0 THEN GetDialogItem(theDialog, itemHit, itemType, itemHandle, itemBox) ELSE itemType:= 0 UNTIL (itemType = ctrlItem + btnCtrl) | buttonless; { Keep on going until a button is } { clicked, provided there are buttons } IF FilterPeek(theDialog)^.exportTEScrap THEN BEGIN ignored:= ZeroScrap; ignored:= TEToScrap; END; DisposeAllDrawingProcs(theDialog); CloseDialog(theDialog); NumDisposeHandle(dialogPeek(theDialog)^.items); IF dlogEventFilterUPP<>Nil THEN DisposeRoutineDescriptor(UniversalProcPtr(dlogEventFilterUPP)); IF FilterPeek(theDialog)^.eventFilterUPP<>Nil THEN DisposeRoutineDescriptor(UniversalProcPtr(FilterPeek(theDialog)^.eventFilterUPP)); IF FilterPeek(theDialog)^.itemHitFilterUPP<>Nil THEN DisposeRoutineDescriptor(UniversalProcPtr(FilterPeek(theDialog)^.itemHitFilterUPP)); DisposePtr(ptr(theDialog)); SetPort(oldPort); FOR index:= 0 TO 3 DO { Restore the previous ParamText strings } IF DAStrings[index] <> NIL THEN SetString(DAStrings[index], oldParams[index]) ELSE DAStrings[index]:= NewString(oldParams[index]); DlogDialog:= itemHit END END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION DlogProcDialog (dialogID: Integer; dlogSetupProc: SetupDialogProcPtr; eventFilterProc: ModalFilterProcPtr; itemHitFilterProc: ItemHitProcPtr): Integer; { Similar to calling dlogDialog, but without the hassle of the four string parameters, } { if you donıt need to param anything. } { Written by David Sinclair, 2 June 1994. } BEGIN DlogProcDialog:= DlogDialog(dialogID, dlogSetupProc, eventFilterProc, itemHitFilterProc, null, null, null, null) END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogParamDialog (dialogID: integer; str0, str1, str2, str3: str255); { Similar to calling dlogDialog, but without the hassle of the three procedure } { parameters, if you donıt need them, and without returning which button was } { clicked (if you donıt care, or there is only one button (or none)). Useful for } { information-only dialogs. } { Written by David Sinclair, 7 June 1994. } VAR ignored: integer; BEGIN ignored:= dlogDialog(dialogID, NIL, NIL, NIL, str0, str1, str2, str3) END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogSimpleDialog (dialogID: integer): integer; { Similar to calling dlogDialog, but without the hassle of the three procPtrs or the } { four string parameters, if you donıt need them. } { Written by David Sinclair, 2 June 1994. } BEGIN dlogSimpleDialog:= dlogDialog(dialogID, NIL, NIL, NIL, null, null, null, null) END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogInfoDialog (dialogID: integer); { Similar to calling dlogDialog, but without the hassle of the three procPtrs or the } { four string parameters, if you donıt need them, and without returning which } { button was clicked (if you donıt care, or there is only one button (or none)). Useful } { for information-only dialogs. } { Written by David Sinclair, 2 June 1994. } VAR ignored: integer; BEGIN ignored:= dlogDialog(dialogID, NIL, NIL, NIL, null, null, null, null) END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogInit; { If you want to use any modeless dialogs, or call dlogEventLoop, you must call this routine first to } { initialise the dialog globals. } { Written by David Sinclair, 9 February 1997. } BEGIN gOffsetPos.h:= 0; gOffsetPos.v:= 0; gQuittingApp:= false; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogGetDesktopSize: Point; { Returns the size of the total desktop, i.e. including all screens. } { Written by David Sinclair, 15 February 1997. } BEGIN dlogGetDesktopSize:= GetGrayRgn^^.rgnBBox.botRight END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} PROCEDURE dlogGetPosAndSize (theDialog: DialogPtr; VAR thePos: Point; {} VAR theWidth, theHeight: Integer); { Returns the current position and size of the dialog. } { Written by David Sinclair, 16 February 1997. } BEGIN IF theDialog <> NIL THEN BEGIN SetPort(theDialog); thePos:= Point(0); LocalToGlobal(thePos); theWidth:= theDialog^.portRect.right; theHeight:= theDialog^.portRect.bottom; END ELSE BEGIN thePos:= Point(0); theWidth:= 0; theHeight:= 0; END; END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION dlogGetPosition (theDialog: DialogPtr): Point; { Returns the current position of the dialog. } { Written by David Sinclair, 16 February 1997. } VAR thePos: Point; BEGIN thePos:= Point(0); IF theDialog <> NIL THEN BEGIN SetPort(theDialog); LocalToGlobal(thePos); END; dlogGetPosition:= thePos END; {--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------} FUNCTION DlogModelessNew (dialogID, kind: Integer; eventFilterProc: ModalFilterProcPtr; itemHitFilterProc: ItemHitProcPtr; sizeOfVars: Size; VAR vars: UNIV Ptr): DialogPtr; {Similar to DlogDialog, but opens a modeless dialog instead of a modal one. Call this routine to open the window, then DlogModelessShow to show it. If an error occurs, the user is alerted. Pass the dialog ID and a value for the kind (see DlogSetKind, above); pass defaultKind if you donıt need to differentiate between window kinds. Pass a procedure to call to handle any filtering of the events, and one to handle hits on dialog items. After calling this routine, check that the returned DialogPtr is not Nil, then do setup of the dialog, then call DlogModelessShow to show and draw it. Note: unlike DlogDialog, the Clipboard is never touched, and the ParamText call is not supported. Use the same routine descriptions as for DlogDialog. Unlike modal dialogs, modeless dialogs can open several instances of each window, so the variables for the window should be stored in a block allocated by this routine. Pass the size of this record in the sizeOfVars parameter, above, and call DlogModelessVars, below, to retrieve a pointer to the block for the current window. The vars are also returned by this routine; theyıre guaranteed to be valid if returned DialogPtr is valid, and if you passed a sizeOfVars > 0. If you donıt need vars (e.g. when converting old single-instance dialogs that use globals), pass zero. If you only want one instance of the window, call DlogKindAlreadyOpen first.} {Written by David Sinclair, 1­2 & 9 February 1997; removed setup routine call and split showing code to separate routine, 1 June 1997.} VAR theDialog: DialogPtr; itemBox: Rect; desktopSize: Point; ignored: Boolean; BEGIN SetCursor(GetCursor(watchCursor)^^); gQuittingApp:= false; vars:= Nil; theDialog:= DialogPtr(NewPtr(SizeOf(FilterDialogRec))); { Allocate the space for the dialog and filter info } IF (ResError = noErr) & (theDialog <> NIL) THEN theDialog:= GetNewDialog(dialogID, Ptr(theDialog), Pointer(-1)); { Read the dialog resource } IF (ResError <> noErr) | (theDialog = NIL) THEN BEGIN { Major problem! } ignored:= dlogOSError(memFullErr, null, null, null); theDialog:= Nil; HiliteMenu(0); END ELSE BEGIN {$IFC application} dlogCentreWindow(theDialog); { Centre the window on the screen } {$ENDC} desktopSize:= DlogGetDesktopSize; itemBox.top:= 15; itemBox.left:= 5; itemBox.bottom:= desktopSize.v