home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1993 #3 / NN_1993_3.iso / spool / comp / sys / mac / programm / 21927 < prev    next >
Encoding:
Internet Message Format  |  1993-01-21  |  10.3 KB

  1. 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
  2. From: jesjones@stein.u.washington.edu (Jesse Jones)
  3. Newsgroups: comp.sys.mac.programmer
  4. Subject: Disabling editBox items (long)
  5. Date: 22 Jan 1993 03:59:27 GMT
  6. Organization: University of Washington, Seattle
  7. Lines: 230
  8. Message-ID: <1jnrevINNkkk@shelley.u.washington.edu>
  9. NNTP-Posting-Host: stein.u.washington.edu
  10. Summary: Modula-2 code to disable editBox's.
  11.  
  12.  
  13.    There have been several posts asking how to disable editBox's in the last
  14. year or so. I had some code that did the trick for a dialog with one editBox,
  15. but it was a bit of a hack involving a userItem and a hidden editBox. A few
  16. days ago I modified another of the dialogs in my program so that it required
  17. two editBox's to be disabled. Naturaly I took the opportunity to rewrite my
  18. code in a more general fashion. 
  19.  
  20.    The new code requires that the dialog procedure call only two procedures,
  21. works with any number of editBox's, can disable/enable any number of editBox's,
  22. and doesn't require any modifications to the DITL list. The two required
  23. procedures are listed below:
  24.  
  25. PROCEDURE EnableEditBox (dptr: DialogPtr; item: INTEGER; enable: BOOLEAN);
  26.  
  27. PROCEDURE GreyFilter (dptr: DialogPtr; VAR Event: EventRecord; 
  28.                              VAR item: INTEGER): BOOLEAN;
  29.                              
  30. EnableEditBox should be called once at the start of the dialog procedure to
  31. either disable the item or enable it (it may have been disabled the last
  32. time the dialog procedure was called). The GreyFilter is a filter proc passed 
  33. to ModalDialog. It's used to grey out the editBox's when an update event occurs.
  34. You can, of course, use your own filter proc as long as you also call 
  35. GreyFilter. And that's all there is to using the code! 
  36.  
  37.    The code itself is a bit more complex. To keep track of the disabled 
  38. editBox's I place a handle to an array of booleans in the dialog's window refCon.
  39. The size of the array is determined by the number of items in the dialog. If an 
  40. element in the array is TRUE then the editBox with corresponding item number is 
  41. disabled. The type declaration for the array is shown below.
  42.  
  43. TYPE
  44.     GreyHand = POINTER TO GreyPtr;
  45.     GreyPtr  = POINTER TO GreyList;
  46.     GreyList = ARRAY [1..1000] OF BOOLEAN;        
  47.     
  48.    When the EnableEditBox procedure disables an editBox it first checks to see
  49. if the editBox is the currently selected editBox. If it is selected the below
  50. procedure is called to find another editBox to select.
  51.  
  52. PROCEDURE GetAnotherEditBox (dptr: DialogPtr; item: INTEGER): INTEGER;
  53.     VAR
  54.         peek : DialogPeek;
  55.         index: INTEGER;
  56.         type : INTEGER;
  57.         data : HANDLE;
  58.         box  : Rect;
  59. BEGIN
  60.     peek := DialogPeek(dptr);
  61.     FOR index := 1 TO INT(peek^.items^^)+1 DO
  62.         GetDItem(dptr, index, type, data, box);
  63.         type := And(type, 127);                (* mask out itemDisable flag *)
  64.         IF (type = editText) AND (index <> item) THEN RETURN index END;
  65.     END;
  66.     RETURN 0;                                    (* return 0 if can't find an editBox *)
  67. END GetAnotherEditBox;
  68.  
  69.    And now we come to the central procedure, EnableEditBox. This procedure does 
  70. the following:
  71.     1) Init the array of disabled editBox's. NewHandleClear is used so that all
  72.         the of the elements start out FALSE.
  73.     2) GetDItem is called to find the type for the item. The itemDisable flag
  74.         is then masked out so that we can use a simple equality test with editBox.
  75.     3) To disable an editBox it is first changed to a staticText item. This 
  76.         prevents the user from selecting or tabbing to the disabled editBox. If
  77.         the editBox is the currently selected editBox we either select a different
  78.         editBox or tell the Dialog Manager that there are no longer any editBox's 
  79.         by setting editField to -1.
  80.     4) Enabling an editBox is simpler: its type is set back to editText and its
  81.         text is selected (which resets editField). Both enable and disable 
  82.         invalidate the items rectangle so that the Dialog Manager and GreyFilter
  83.         can redraw the item.
  84.  
  85. PROCEDURE EnableEditBox (dptr: DialogPtr; item: INTEGER; enable: BOOLEAN);
  86.     VAR
  87.         type : INTEGER;
  88.         new  : INTEGER;
  89.         data : HANDLE;
  90.         box  : Rect;
  91.         list : GreyHand;
  92.         peek : DialogPeek;
  93.         sPort: GrafPtr;
  94. BEGIN
  95.     GetPort(sPort);
  96.     SetPort(dptr);
  97.     peek := DialogPeek(dptr);
  98.     list := GetWRefCon(dptr);
  99.     IF list = NIL THEN                        (* create the array *)
  100.         list := NewHandleClear(LONG(peek^.items^^)+1);    
  101.         SetWRefCon(dptr, list);
  102.     END;
  103.     GetDItem(dptr, item, type, data, box);    (* get the item type *)
  104.     type := And(type, $7F);                   (* remove itemDisable flag *)
  105.     IF (enable = FALSE) AND (type = editText) THEN     
  106.         SetDItem(dptr, item, statText+itemDisable, data, box);    
  107.         IF peek^.editField = item-1 THEN      (* is it the selected editBox? *)
  108.             new := GetAnotherEditBox(dptr, item);        
  109.             IF item > 0 THEN
  110.                 SelIText(dptr, new, 0, 20);   (* select a different editBox *)        
  111.             ELSE                                                        
  112.                 SelIText(dptr, item, 0, 0);                    
  113.                 peek^.editField := -1;        (* there are no more editBox's! *)        
  114.             END;                                                        
  115.         END;
  116.         InvalRect(box);                                            
  117.         list^^[item] := TRUE;                                    
  118.     ELSIF enable AND list^^[item] THEN                            
  119.         SetDItem(dptr, item, editText, data, box);        
  120.         SelIText(dptr, item, 0, 20);                        
  121.         InvalRect(box);                                            
  122.         list^^[item] := FALSE;                                    
  123.     END;
  124.     SetPort(sPort);
  125. END EnableEditBox;
  126.  
  127.     GreyFilter calls the below procedure to grey out the text in a disabled
  128. editBox. GetBounds is a utility procedure I wrote to get the bounds for
  129. a dialog item. You can just as easily use GetDItem.
  130.  
  131. PROCEDURE GreyEditBox (dptr: DialogPtr; item: INTEGER);
  132.     VAR
  133.         box  : Rect;
  134.         sPen : PenState;
  135.         sPort: GrafPtr;
  136. BEGIN    
  137.     GetPort(sPort);
  138.     SetPort(dptr);
  139.     GetPenState(sPen);
  140.     box := GetBounds(dptr, item);
  141.     InsetRect(box, -3, -3);        (* grey out the text *)
  142.     PenPat(gray);
  143.     PenMode(patBic);
  144.     PaintRect(box);
  145.     PenPat(black);                    (* draw the frame *)
  146.     PenMode(patCopy);
  147.     FrameRect(box); 
  148.     SetPenState(sPen);
  149.     SetPort(sPort);
  150. END GreyEditBox;
  151.  
  152.     All of my dialogs use a filter proc called DefaultFilter. If the dialog
  153. needs to do some extra filtering it uses a different filter proc that first
  154. calls DefaultFilter. GreyFilter is no exception: it calls DefaultFilter and
  155. takes action only when DefaultFilter returns FALSE.
  156.     The DefaultFilter is used to keep my dialogs consistent: Return/Enter always
  157. work like a click on the OK button, and Escape/Tilde work like a click on the
  158. Cancel button. Even more important, DefaultFilter updates the windows in my
  159. application. If this isn't done its possible that applications in the background
  160. will get no time while the dialog is up (see TechNote 304 for details). 
  161.  
  162. PROCEDURE DefaultFilter (dptr: DialogPtr; VAR Event: EventRecord; 
  163.                                  VAR item: INTEGER): BOOLEAN;
  164.     VAR 
  165.         ch, key: CHAR;
  166.         wptr   : WindowPtr;
  167.         control: ControlHandle;
  168.         button : INTEGER;
  169.         handled: BOOLEAN;
  170. BEGIN
  171.     handled := FALSE;
  172.     IF Event.what = keyDown THEN
  173.         ch := Event.msgChars[3];
  174.         key := Event.msgChars[2];
  175.         IF (key = ReturnKey) OR (key = EnterKey) THEN 
  176.             button := 1;
  177.             handled := TRUE;
  178.         ELSIF (key = TildeKey) OR (key = EscapeKey) THEN 
  179.             button := 2;
  180.             handled := TRUE;
  181.         ELSIF (cmd IN Event.modiFlags) AND (ch = '.') THEN 
  182.             button := 2;
  183.             handled := TRUE;
  184.         END;
  185.         IF handled THEN                    (* make sure the button is enabled *)
  186.             control := GetControl(dptr, button);
  187.             IF control^^.contrlHilite = 0 THEN item := button END;
  188.         END;
  189.     ELSIF Event.what = updateEvt THEN
  190.         wptr := Event.message;
  191.         IF wptr <> dptr THEN
  192.             WindEvent(Event);                        (* update my windows *)
  193.         END;
  194.     END;
  195.     RETURN handled;
  196. END DefaultFilter;
  197.  
  198.    The GreyFilter takes care of greying out all of the disabled editBox's. This
  199. is a simple matter except for one minor detail: filter procs are called before
  200. the Dialog Manager takes any action. So, it's not sufficient to just grey out 
  201. the editBox when we get an updateEvt. What we would have to do is first draw
  202. the text in the editBx, grey it out, and then validate the editBox so the
  203. Dialog Manager doesn't overwrite all of our work.
  204.    The GreyFilter procedure takes a simpler approach. Update events are handled
  205. normally by the Dialog Manager, but when an update event occurs GreyFilter adds
  206. a special key to the Event queue to signal that an update occured. After the
  207. Dialog Manager processes the update event it will call GreyFilter again with
  208. the special key so that the editBox's may be greyed out.
  209.  
  210. PROCEDURE GreyFilter (dptr: DialogPtr; VAR Event: EventRecord; 
  211.                              VAR item: INTEGER): BOOLEAN;
  212.     VAR 
  213.         list   : GreyHand;
  214.         peek   : DialogPeek;
  215.         index  : INTEGER;
  216.         handled: BOOLEAN;
  217.         err    : OSErr;
  218. BEGIN
  219.     handled := DefaultFilter(dptr, Event, item);
  220.     IF NOT handled THEN
  221.         IF Event.what = updateEvt THEN        
  222.             err := PostEvent(keyDown, 0);        
  223.         ELSIF Event.what = keyDown THEN
  224.             IF LoWord(Event.message) = 0 THEN
  225.                 list := GetWRefCon(dptr);        
  226.                 peek := DialogPeek(dptr);
  227.                 IF list <> NIL THEN
  228.                     FOR index := 1 TO INT(peek^.items^^)+1 DO
  229.                         IF list^^[index] THEN GreyEditBox(dptr, index) END;
  230.                     END;
  231.                 END;
  232.                 handled := TRUE;                            
  233.             END;
  234.         END;                                
  235.     END;
  236.     RETURN handled;
  237. END GreyFilter;
  238.  
  239.   --Jesse Jones
  240.   
  241. jesjones@u.washington.edu
  242.