{ File: DITL.p Contains: DITL Management unit Written by: Byron Han Copyright: © 1987-1990 by Apple Computer, Inc., all rights reserved. This file is used in these builds: BigBang Change History (most recent first): <7> 12/13/90 kaz Stuff the new rect before appending resCtrls. <6> 5/9/90 kaz Adjusted the rects of resCtrl items on Append <5> 3/29/90 BBH fix handling of resControl's (was using uninitialized control ID variable) <4> 3/22/90 BBH Implement (latest) Help interface stuff thrashed out with Randy (new constant HMScanAppendhdlg with the hdlg ID and the dialog item list offset stored in the ditl itself) <3> 3/21/90 BBH Take out SetClip's Use WITH where possible Use ShowDItem/HideDItem to handle text edit item appending/removal <2> 3/15/90 BBH renamed some routines <1> 3/14/90 BBH first checked in To Do: Pre-BBS History 1/30/90 BBH Changed handling of helpitems to reflect reality. 8/22/89 BBH In the Save/Restore of the control template rectangle, we assumed that GetNewControl does not release the CNTL resource. This is, unfortunately, only true on the Mac II. On SE and Plus the CNTL resource is released by the control manager. 8/21/89 BBH Eliminate call to ReleaseResource when handling resControl's 8/19/89 BBH In fixing things, forgot to lock down theNewDITL when appending to end of old one.... Revised handling of resource controls to truly allow use of cctb's in resource controls. Also there was a screw up in the handling of the rectangles for resource controls which is now fixed. The strategy is the load the CNTL template and offset the template rectangle appropriately. Then after control creation via GetNewControl (which automagically handles cctb's), the control is moved to the topLeft of the itemRect (as per dmgr.a) 8/18/89 BBH Changed BitOr to BOR's Use WITH's statements In processing helpItem, we now stuff helpInvalidID into the ID field of the dialog item list if we are unable to load the hdlg resource. Added error recovery from failing to append the new item list to the old item list. Moved calls to InvalRect in Append outside of CASE structure to cut down on codesize. Changed constants 5, 3, 8192 to named constants 8/7/89 BBH Added balloon help support per conversations with Randy Carr 'hdlg' resource has first two bytes version, second two being the item number (0 based) of the first dialog item managed by the 'hdlg' resource. Renamed some data structure elements to be clearer and renamed some local variables to be clearer. 8/4/89 BBH Removed "fix" to resControl handling. Now explicitly read in the CNTL template and handle it manually. 8/2/89 BBH Added support for HelpItems ? Handle resControl items with call to GetNewControl instead of manually ditling with the CNTL template (no pun intended) 7/28/89 MPR Added support for color icons 4/25/89 BBH Changed BitAnd to BANDs Added some memory error checking 4/9/89 BBH Added sanity check to AppendDITL to see if theDITL is NIL. Added sanity check to ShortenDITL to see if numberItems is <= 0 } UNIT DITL; INTERFACE USES MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, AppleTalk, CTBUtilities, CommToolboxPriv; FUNCTION DITLEntry(theDialog: DialogPtr; theDITL: Handle; method: INTEGER; numberItems: INTEGER; msg: INTEGER): LONGINT; IMPLEMENTATION CONST { INTERNAL CONSTANTS } dialogSlop = 5; { after growing dialog, this is whitespace at bottom/right } teSlop = 3; { slop around text edit boxes } hiddenThreshold = 8192; low7Bits = $0000007F; buttonMask = $03; { to get var code for buttons } growNothing = 0; growRight = 1; { internal msgs for dialgo growing } growBottom = 2; { to the right or to the bottom } { new item types to be included someday with Dialogs.p } helpItem = 1; reservedItem = 2; { more help stuff } helpResource = 'hdlg'; helpInvalidID = -1; HMScanAppendhdlg = 8; TYPE { DITL data structures } IntPtr = ^INTEGER; IntHandle = ^IntPtr; hHelpItem = ^pHelpItem; { per discussions with Randy Carr } pHelpItem = ^rHelpItem; rHelpItem = RECORD hVersion : INTEGER; hFirstItem : INTEGER; hItemData : ARRAY[0..0] OF SignedByte; END; hDITLItem = ^pDITLItem; pDITLItem = ^DITLItem; DITLItem = RECORD dItemHandle : Handle; dItemRect : Rect; dItemType : SignedByte; dItemData : Array[0..0] OF SignedByte; END; hItemList = ^pItemList; pItemList = ^ItemList; ItemList = RECORD iNumberItems: INTEGER; iDITLItems : ARRAY[0..0] OF DITLItem; END; { forward declaration of routines } { These routines are the externally visible routines which are accessed via the single entry point and selector } PROCEDURE DoAppendDITL( theDialog:DialogPtr; theDITL:Handle; method:INTEGER); FORWARD; FUNCTION DoCountDITL( theDialog:DialogPtr ) : INTEGER; FORWARD; PROCEDURE DoShortenDITL( theDialog:DialogPtr; numberItems:INTEGER); FORWARD; { utility routines } FUNCTION IsCGrafPort(theCPort: CGrafPtr): BOOLEAN; FORWARD; FUNCTION GetFirstTEItem(theDialog: DialogPtr) : INTEGER; FORWARD; FUNCTION GetUSB(a : SignedByte) : INTEGER; FORWARD; FUNCTION EvenInt(a : INTEGER) : INTEGER; FORWARD; { Main Entry Point } {------------------------------------------------------------------------------------------} FUNCTION DITLEntry(theDialog: DialogPtr; theDITL: Handle; method: INTEGER; numberItems: INTEGER; msg: INTEGER): LONGINT; BEGIN DITLEntry := 0; CASE msg OF AppendMSG: DoAppendDITL(theDialog, theDITL, method); CountMSG: DITLEntry := DoCountDITL(theDialog); ShortenMSG: DoShortenDITL(theDialog, numberItems); OTHERWISE DITLEntry := -1; { out of range } END; {case} END; PROCEDURE DoAppendDITL( theDialog:DialogPtr; theDITL:Handle; method:INTEGER); VAR offset : Point; { offset each appended item from this } theDialogRect : Rect; { dialog original portRect } theNewDITL : hItemList; { to append (should be detached from rsrc map) } theOldDITL : hItemList; { the current DITL } theType : INTEGER; { type of item being worked on } firstItem : INTEGER; { item number of first appended item (1 based) } newItems : INTEGER; { number of new items to append } dataSize : LONGINT; { size of data associated with item } theCurrentItem : pDITLItem; { current item in appended list being processed } i : INTEGER; { a counter } err : OSErr; { fuckup notifier } firstTEItem : INTEGER; { first TE item. need to special case adding } { a TE item where none were before } theID : INTEGER; { ID for creating control } theIDPtr : IntPtr; theHelpHandle : hHelpItem; { for 'hdlg' resource } myItemKind : INTEGER; { itemKind } myItemHandle : Handle; { itemBox } myItemBox : Rect; { itemBox } expanded : INTEGER; { expanded right or bottom? } tempRect : Rect; { place holder } savedPort : GrafPtr; { saved port } BEGIN IF theDITL = NIL THEN Exit(DoAppendDITL); { abort abort abort } GetPort(savedPort); { sanity } SetPort(theDialog); HidePen; theDialogRect := theDialog^.portRect; { current dialog portRect } expanded := growNothing; { did not grow rectangle } IF method < 0 THEN { relative to topLeft of item |method| } BEGIN GetDItem(theDialog, -method, myItemKind, myItemHandle, myItemBox); offset := myItemBox.topLeft; END ELSE BEGIN offset := theDialog^.portRect.topLeft; { append rel to topLeft } CASE method OF appendDITLRight: offset.h := theDialogRect.right; { relative to topRight of portRect } appendDITLBottom: offset.v := theDialogRect.bottom; { relative to botLeft of portRect } END; {case} END; theOldDITL := hItemList( DialogPeek(theDialog)^.items ); { current item list } theNewDITL := hItemList(theDITL); { list to be appended } firstItem := theOldDITL^^.iNumberItems + 2; { item # of first appended item } newItems := theNewDITL^^.iNumberItems + 1; { # items to append } { this are 1 based } firstTEItem := GetFirstTEItem(theDialog); { first TE item in old list } { this is 1 based } HLock(Handle(theNewDITL)); { lock new list } theCurrentItem := @theNewDITL^^.iDITLItems; { go through items in new list } FOR i := 1 TO newItems DO BEGIN { loop de loop } WITH theCurrentItem^ DO BEGIN myItemBox := dItemRect; { get item rectangle } OffsetRect(myItemBox, offset.h, offset.v); { move it } InvalRect(myItemBox); { Invalidate the rectangle } theType := BAND(dItemType, low7Bits); { what kind of item is it } dataSize := GetUSB(dItemData[0]); { and how big is the data } dItemHandle := NIL; { pessimism } CASE theType OF helpItem: BEGIN theID := IntPtr(@dItemData[1])^; { get the help item type } IF theID = HMScanAppendhdlg THEN BEGIN theIDPtr := IntPtr(@dItemData[5]); { stuff offset into ditl } theIDPtr^ := firstItem - 1; { stuff invalid ID marker } END; END; ctrlItem + btnCtrl, ctrlItem + chkCtrl, ctrlItem + radCtrl: BEGIN dItemHandle := Handle(NewControl(theDialog, myItemBox, StringPtr(@dItemData[0])^, TRUE, { visible, val, min, max } 0, 0, 1, { procID, refCon } BAND(theType, buttonMask), 0)); { We get the new contrlRect 'cuz NewControl may have changed the contrlRect } IF dItemHandle <> NIL THEN BEGIN myItemBox := ControlHandle(dItemHandle)^^.contrlRect; InvalRect(myItemBox); { invalidate it since we are not } END; END; ctrlItem + resCtrl: BEGIN { We stuff the new rect in the CNTL resource before NewControl } { so that the control won't get confused about drawing/erasing } { in the old location } dItemHandle := GetResource('CNTL', IntPtr(@dItemData[1])^); IF (dItemHandle <> NIL) THEN BlockMove(@myItemBox,Ptr(dItemHandle^),SIZEOF(Rect)); dItemHandle := Handle(GetNewControl(IntPtr(@dItemData[1])^, theDialog)); IF dItemHandle <> NIL THEN BEGIN myItemBox := ControlHandle(dItemHandle)^^.contrlRect; InvalRect(myItemBox); { didn't really draw it so... } END; END; statText: { static text item } err := PtrToHand(@dItemData[1], dItemHandle, dataSize); { copy data } editText: { yummy. a text edit item } BEGIN err := PtrToHand(@dItemData[1], dItemHandle, dataSize); { copy data } InsetRect(myItemBox, -teSlop, -teSlop); { expand to take care of outline } InvalRect(myItemBox); { box. so inval it } InsetRect(myItemBox, teSlop, teSlop); END; iconItem: { an icon! } BEGIN IF IsCGrafPort(CGrafPtr(theDialog)) THEN{ check for a color dialog } dItemHandle := Handle(GetCIcon(IntPtr(@dItemData[1])^)); { try for a cicn } IF dItemHandle = NIL THEN { nope, get BW } dItemHandle := GetIcon(IntPtr(@dItemData[1])^); { load the icon } END; picItem: { a picture (of vanna white) } dItemHandle := Handle(GetPicture((IntPtr(@dItemData[1])^))); END; {case} dItemRect := myItemBox; { in case itemBox changed } END; {with theCurrentItem} UnionRect(myItemBox, theDialogRect, tempRect); { do we need to grow dlog rect? } IF tempRect.right > theDialogRect.right THEN { grown to the right } expanded := BOR(expanded, growRight); { set right grow bit } IF tempRect.bottom > theDialogRect.bottom THEN { grown to the bottom } expanded := BOR(expanded, growBottom); { set bottom grow bit } theDialogRect := tempRect; { set dlog rect to grown rect } dataSize := EvenInt(dataSize); { item data is padded to even len } theCurrentItem := pDITLItem(Ptr(ORD4(@theCurrentItem^) + dataSize + sizeof(DITLItem))); { move to next item } END; {for i} HUnlock(Handle(theNewDITL)); { append new list to old } HLock(Handle(theNewDITL)); theCurrentItem := @theNewDITL^^.iDITLItems; err := PtrAndHand(Ptr(theCurrentItem), { -2 is to skip numItems field } Handle(theOldDITL), GetHandleSize(Handle(theNewDITL)) -2); HUnlock(Handle(theNewDITL)); IF err <> noErr THEN { cannot add items to DITL } BEGIN { cleanup handles and/or resources allocated } HLock(Handle(theNewDITL)); { lock new list } theCurrentItem := @theNewDITL^^.iDITLItems; { go thru items in new list } FOR i := 1 TO newItems DO BEGIN { loop de loop } WITH theCurrentItem^ DO BEGIN theType := BAND(dItemType, low7Bits); { what kind of item is it } dataSize := EvenInt(GetUSB(dItemData[0])); { and how big is the data } IF dItemHandle <> NIL THEN BEGIN CASE theType OF ctrlItem + btnCtrl, { dispose of it } ctrlItem + chkCtrl, ctrlItem + radCtrl, ctrlItem + resCtrl : DisposeControl( ControlHandle(dItemHandle) ); statText, { static text item } editText: DisposHandle(dItemHandle); iconItem, { an icon! } picItem: { a picture (of vanna white) } ReleaseResource(dItemHandle); END; {case} END; dItemHandle := NIL; END; {with theCurrentItem} theCurrentItem := pDITLItem(Ptr(ORD4(@theCurrentItem^) + dataSize + sizeof(DITLItem))); END; {for} HUnlock(Handle(theNewDITL)); { now cleanup local variables that indicate state of appending operation } newItems := 0; { no new items } expanded := growNothing; { no growing of the dialog } theDialogRect := theDialog^.portRect; { mark no size change of dialog } END; {if err <> noErr} WITH theOldDITL^^ DO iNumberItems := iNumberItems + newItems; { add itemCount } IF BAND(expanded, growBottom) = growBottom THEN { add an extra 5 if growing } theDialogRect.bottom := theDialogRect.bottom + dialogSlop; IF BAND(expanded, growRight) = growRight THEN { add an extra 5 if growing } theDialogRect.right := theDialogRect.right + dialogSlop; { only redraw the rectangle if size has changed } IF NOT EqualPt(theDialogRect.botRight, theDialog^.portRect.botRight) THEN SizeWindow(theDialog, theDialogRect.right, theDialogRect.bottom, FALSE); { resize the window if needed } IF firstTEItem = 0 THEN { no TE items beforeÉ } BEGIN firstTEItem := GetFirstTEItem(theDialog); { got one now? } IF firstTEItem > 0 THEN BEGIN HideDItem(theDialog, firstTEItem); { select it please } ShowDItem(theDialog, firstTEItem); SelIText(theDialog, firstTEItem, 0, 32767); END; END; ShowPen; SetPort(savedPort); END; { returns the number of dialog items in the dialog item list } { this is 1 based } FUNCTION DoCountDITL( theDialog:DialogPtr) : INTEGER; BEGIN DoCountDITL := 0; IF theDialog <> NIL THEN DoCountDITL := IntHandle(DialogPeek(theDialog)^.items)^^ + 1; END; PROCEDURE DoShortenDITL( theDialog:DialogPtr; numberItems:INTEGER); { removes "number of items" from end of DITL. need to remember that if we have a editText item in the area being deleted, we must be sure that we are not in it. if we are, we need to make sure we move the current text edit item to one that is available. it there are none, we need to set editField to -1 } VAR theOldDITL : hItemList; lastItem : INTEGER; lastSavedItem : INTEGER; dataSize : LONGINT; theCurrentItem : pDITLItem; i : INTEGER; newDITLSize : LONGINT; tempKind : INTEGER; tempRect : Rect; tempHandle : Handle; theState1 :INTEGER; savedPort : GrafPtr; killCurrent : BOOLEAN; { are we disposing of the current TE item } itemKind: INTEGER; itemHandle: Handle; itemRect: Rect; changeIt: BOOLEAN; BEGIN IF numberItems <= 0 THEN Exit(DoShortenDITL); { abort abort abort } GetPort(savedPort); { sanity } SetPort(theDialog); theOldDITL := hItemList( DialogPeek(theDialog)^.items ); { the current item list } newDITLSize := 2; { how many bytes to keep in item list } lastItem := theOldDITL^^.iNumberItems + 1; { last item in DITL (1 based) } lastSavedItem := lastItem - numberItems; { itemNumber of last to save (1 base) } killCurrent := (DialogPeek(theDialog)^.editField+1 > lastSavedItem); FOR i := lastSavedItem + 1 TO lastItem DO BEGIN GetDItem(theDialog, i, itemKind, itemHandle, itemRect); itemKind := BAND(BNOT(itemDisable), itemKind); { clear disable bit } IF itemKind = editText THEN InsetRect(itemRect, -teSlop, -teSlop); EraseRect(itemRect); InvalRect(itemRect); IF itemKind = editText THEN HideDItem(theDialog, i); IF itemHandle <> NIL THEN BEGIN changeIt := TRUE; CASE itemKind OF statText, editText: DisposHandle( itemHandle ); ctrlItem + btnCtrl, { a control, eh? } ctrlItem + chkCtrl, ctrlItem + radCtrl, ctrlItem + resCtrl : { a resource control, eh? } DisposeControl( ControlHandle(itemHandle) ); iconItem, picItem: ReleaseResource(itemHandle); OTHERWISE changeIt := FALSE; END; {case} IF changeIt THEN SetDItem(theDialog, i, userItem, NIL, itemRect); END; {if itemHandle <> NIL } END; theState1 := HGetState(Handle(theOldDITL)); { get state of DITL } HLock(Handle(theOldDITL)); { lock it down } theCurrentItem := @theOldDITL^^.iDITLItems; { the current item to look at } FOR i := 1 TO lastSavedItem DO { and a 1, and a 2, and a 3, 4, 5, 6 } BEGIN dataSize := EvenInt(GetUSB(theCurrentItem^.dItemData[0])); newDITLSize := newDITLSize + dataSize + sizeof(DITLItem); { yup } theCurrentItem := pDITLItem(Ptr(ORD4(@theCurrentItem^) + dataSize + sizeof(DITLItem))); END; {for } SetHandleSize(Handle(theOldDITL), newDITLSize); { reduce datasize of DITL } WITH theOldDITL^^ DO iNumberItems := iNumberItems - numberItems; { reduce # items in DITL } HSetState(Handle(theOldDITL), theState1); { restore state of DITL } IF killCurrent THEN BEGIN i := DialogPeek(theDialog)^.editField+1; IF i > 0 THEN SelIText(theDialog, i, 0, 32767); END; SetPort(savedPort); { go on home } END; { This function takes a grafPtr and returns TRUE if it is a color grafPort } FUNCTION IsCGrafPort(theCPort: CGrafPtr): BOOLEAN; BEGIN IsCGrafPort := (BAND(theCPort^.portVersion, $0000C000) = $0000C000); END; { This is a utility routine which returns the item number (1 based) of the first text edit item in the dialog item list, 0 if none is found } FUNCTION GetFirstTEItem(theDialog: DialogPtr) : INTEGER; VAR theOldDITL : hItemList; lastItem : INTEGER; dataSize : LONGINT; theCurrentItem : pDITLItem; i : INTEGER; theType : INTEGER; firstTEItem : INTEGER; BEGIN theOldDITL := hItemList( DialogPeek(theDialog)^.items ); WITH theOldDITL^^ DO BEGIN lastItem := iNumberItems; theCurrentItem := @iDITLItems; END; GetFirstTEItem := 0; FOR i := 0 TO lastItem DO BEGIN WITH theCurrentItem^ DO BEGIN theType := BAND(dItemType, low7Bits); dataSize := EvenInt(GetUSB(dItemData[0])); IF theType = editText THEN IF dItemRect.left < hiddenThreshold THEN { hidden? } BEGIN GetFirstTEItem := i+1; { return 1 based value } Exit(GetFirstTEItem); { exit please } END; END; theCurrentItem := pDITLItem(Ptr(ORD4(@theCurrentItem^) + dataSize + sizeof(DITLItem))); END; {for } END; {function} { this function casts a Signed Byte into an integer } FUNCTION GetUSB(a : SignedByte) : INTEGER; { cast signed byte into unsigned byte } BEGIN GetUSB := BAND($000000FF, a); END; { this function evens out an integer (rounding up) } FUNCTION EvenInt(a : INTEGER) : INTEGER; BEGIN EvenInt := BAND(a+1, $FFFFFFFE); END; END.