home *** CD-ROM | disk | FTP | other *** search
- Path: sparky!uunet!elroy.jpl.nasa.gov!sdd.hp.com!saimiri.primate.wisc.edu!usenet.coe.montana.edu!news.u.washington.edu!stein.u.washington.edu!jesjones
- From: jesjones@stein.u.washington.edu (Jesse Jones)
- Newsgroups: comp.sys.mac.programmer
- Subject: Disabling editBox items (long)
- Date: 22 Jan 1993 03:59:27 GMT
- Organization: University of Washington, Seattle
- Lines: 230
- Message-ID: <1jnrevINNkkk@shelley.u.washington.edu>
- NNTP-Posting-Host: stein.u.washington.edu
- Summary: Modula-2 code to disable editBox's.
-
-
- There have been several posts asking how to disable editBox's in the last
- year or so. I had some code that did the trick for a dialog with one editBox,
- but it was a bit of a hack involving a userItem and a hidden editBox. A few
- days ago I modified another of the dialogs in my program so that it required
- two editBox's to be disabled. Naturaly I took the opportunity to rewrite my
- code in a more general fashion.
-
- The new code requires that the dialog procedure call only two procedures,
- works with any number of editBox's, can disable/enable any number of editBox's,
- and doesn't require any modifications to the DITL list. The two required
- procedures are listed below:
-
- PROCEDURE EnableEditBox (dptr: DialogPtr; item: INTEGER; enable: BOOLEAN);
-
- PROCEDURE GreyFilter (dptr: DialogPtr; VAR Event: EventRecord;
- VAR item: INTEGER): BOOLEAN;
-
- EnableEditBox should be called once at the start of the dialog procedure to
- either disable the item or enable it (it may have been disabled the last
- time the dialog procedure was called). The GreyFilter is a filter proc passed
- to ModalDialog. It's used to grey out the editBox's when an update event occurs.
- You can, of course, use your own filter proc as long as you also call
- GreyFilter. And that's all there is to using the code!
-
- The code itself is a bit more complex. To keep track of the disabled
- editBox's I place a handle to an array of booleans in the dialog's window refCon.
- The size of the array is determined by the number of items in the dialog. If an
- element in the array is TRUE then the editBox with corresponding item number is
- disabled. The type declaration for the array is shown below.
-
- TYPE
- GreyHand = POINTER TO GreyPtr;
- GreyPtr = POINTER TO GreyList;
- GreyList = ARRAY [1..1000] OF BOOLEAN;
-
- When the EnableEditBox procedure disables an editBox it first checks to see
- if the editBox is the currently selected editBox. If it is selected the below
- procedure is called to find another editBox to select.
-
- PROCEDURE GetAnotherEditBox (dptr: DialogPtr; item: INTEGER): INTEGER;
- VAR
- peek : DialogPeek;
- index: INTEGER;
- type : INTEGER;
- data : HANDLE;
- box : Rect;
- BEGIN
- peek := DialogPeek(dptr);
- FOR index := 1 TO INT(peek^.items^^)+1 DO
- GetDItem(dptr, index, type, data, box);
- type := And(type, 127); (* mask out itemDisable flag *)
- IF (type = editText) AND (index <> item) THEN RETURN index END;
- END;
- RETURN 0; (* return 0 if can't find an editBox *)
- END GetAnotherEditBox;
-
- And now we come to the central procedure, EnableEditBox. This procedure does
- the following:
- 1) Init the array of disabled editBox's. NewHandleClear is used so that all
- the of the elements start out FALSE.
- 2) GetDItem is called to find the type for the item. The itemDisable flag
- is then masked out so that we can use a simple equality test with editBox.
- 3) To disable an editBox it is first changed to a staticText item. This
- prevents the user from selecting or tabbing to the disabled editBox. If
- the editBox is the currently selected editBox we either select a different
- editBox or tell the Dialog Manager that there are no longer any editBox's
- by setting editField to -1.
- 4) Enabling an editBox is simpler: its type is set back to editText and its
- text is selected (which resets editField). Both enable and disable
- invalidate the items rectangle so that the Dialog Manager and GreyFilter
- can redraw the item.
-
- PROCEDURE EnableEditBox (dptr: DialogPtr; item: INTEGER; enable: BOOLEAN);
- VAR
- type : INTEGER;
- new : INTEGER;
- data : HANDLE;
- box : Rect;
- list : GreyHand;
- peek : DialogPeek;
- sPort: GrafPtr;
- BEGIN
- GetPort(sPort);
- SetPort(dptr);
- peek := DialogPeek(dptr);
- list := GetWRefCon(dptr);
- IF list = NIL THEN (* create the array *)
- list := NewHandleClear(LONG(peek^.items^^)+1);
- SetWRefCon(dptr, list);
- END;
- GetDItem(dptr, item, type, data, box); (* get the item type *)
- type := And(type, $7F); (* remove itemDisable flag *)
- IF (enable = FALSE) AND (type = editText) THEN
- SetDItem(dptr, item, statText+itemDisable, data, box);
- IF peek^.editField = item-1 THEN (* is it the selected editBox? *)
- new := GetAnotherEditBox(dptr, item);
- IF item > 0 THEN
- SelIText(dptr, new, 0, 20); (* select a different editBox *)
- ELSE
- SelIText(dptr, item, 0, 0);
- peek^.editField := -1; (* there are no more editBox's! *)
- END;
- END;
- InvalRect(box);
- list^^[item] := TRUE;
- ELSIF enable AND list^^[item] THEN
- SetDItem(dptr, item, editText, data, box);
- SelIText(dptr, item, 0, 20);
- InvalRect(box);
- list^^[item] := FALSE;
- END;
- SetPort(sPort);
- END EnableEditBox;
-
- GreyFilter calls the below procedure to grey out the text in a disabled
- editBox. GetBounds is a utility procedure I wrote to get the bounds for
- a dialog item. You can just as easily use GetDItem.
-
- PROCEDURE GreyEditBox (dptr: DialogPtr; item: INTEGER);
- VAR
- box : Rect;
- sPen : PenState;
- sPort: GrafPtr;
- BEGIN
- GetPort(sPort);
- SetPort(dptr);
- GetPenState(sPen);
- box := GetBounds(dptr, item);
- InsetRect(box, -3, -3); (* grey out the text *)
- PenPat(gray);
- PenMode(patBic);
- PaintRect(box);
- PenPat(black); (* draw the frame *)
- PenMode(patCopy);
- FrameRect(box);
- SetPenState(sPen);
- SetPort(sPort);
- END GreyEditBox;
-
- All of my dialogs use a filter proc called DefaultFilter. If the dialog
- needs to do some extra filtering it uses a different filter proc that first
- calls DefaultFilter. GreyFilter is no exception: it calls DefaultFilter and
- takes action only when DefaultFilter returns FALSE.
- The DefaultFilter is used to keep my dialogs consistent: Return/Enter always
- work like a click on the OK button, and Escape/Tilde work like a click on the
- Cancel button. Even more important, DefaultFilter updates the windows in my
- application. If this isn't done its possible that applications in the background
- will get no time while the dialog is up (see TechNote 304 for details).
-
- PROCEDURE DefaultFilter (dptr: DialogPtr; VAR Event: EventRecord;
- VAR item: INTEGER): BOOLEAN;
- VAR
- ch, key: CHAR;
- wptr : WindowPtr;
- control: ControlHandle;
- button : INTEGER;
- handled: BOOLEAN;
- BEGIN
- handled := FALSE;
- IF Event.what = keyDown THEN
- ch := Event.msgChars[3];
- key := Event.msgChars[2];
- IF (key = ReturnKey) OR (key = EnterKey) THEN
- button := 1;
- handled := TRUE;
- ELSIF (key = TildeKey) OR (key = EscapeKey) THEN
- button := 2;
- handled := TRUE;
- ELSIF (cmd IN Event.modiFlags) AND (ch = '.') THEN
- button := 2;
- handled := TRUE;
- END;
- IF handled THEN (* make sure the button is enabled *)
- control := GetControl(dptr, button);
- IF control^^.contrlHilite = 0 THEN item := button END;
- END;
- ELSIF Event.what = updateEvt THEN
- wptr := Event.message;
- IF wptr <> dptr THEN
- WindEvent(Event); (* update my windows *)
- END;
- END;
- RETURN handled;
- END DefaultFilter;
-
- The GreyFilter takes care of greying out all of the disabled editBox's. This
- is a simple matter except for one minor detail: filter procs are called before
- the Dialog Manager takes any action. So, it's not sufficient to just grey out
- the editBox when we get an updateEvt. What we would have to do is first draw
- the text in the editBx, grey it out, and then validate the editBox so the
- Dialog Manager doesn't overwrite all of our work.
- The GreyFilter procedure takes a simpler approach. Update events are handled
- normally by the Dialog Manager, but when an update event occurs GreyFilter adds
- a special key to the Event queue to signal that an update occured. After the
- Dialog Manager processes the update event it will call GreyFilter again with
- the special key so that the editBox's may be greyed out.
-
- PROCEDURE GreyFilter (dptr: DialogPtr; VAR Event: EventRecord;
- VAR item: INTEGER): BOOLEAN;
- VAR
- list : GreyHand;
- peek : DialogPeek;
- index : INTEGER;
- handled: BOOLEAN;
- err : OSErr;
- BEGIN
- handled := DefaultFilter(dptr, Event, item);
- IF NOT handled THEN
- IF Event.what = updateEvt THEN
- err := PostEvent(keyDown, 0);
- ELSIF Event.what = keyDown THEN
- IF LoWord(Event.message) = 0 THEN
- list := GetWRefCon(dptr);
- peek := DialogPeek(dptr);
- IF list <> NIL THEN
- FOR index := 1 TO INT(peek^.items^^)+1 DO
- IF list^^[index] THEN GreyEditBox(dptr, index) END;
- END;
- END;
- handled := TRUE;
- END;
- END;
- END;
- RETURN handled;
- END GreyFilter;
-
- --Jesse Jones
-
- jesjones@u.washington.edu
-