5ht^XX
Td.8~.8'F'.
.
F'xJh:>:>'F'$Intrfc/ToolIntf.TextxxNJIntrfc/ToolIntf.TextTextxtFJ" .
8.
:HH'@]8`H'F)@l%L:@#'J'
OSERRS.ERR6:":"9)e noI{$X-}
{$U-}
{$R-} {To avoid bug in range checking of MacPasLib}

{Scott Knaster    Macintosh Technical Support     5/10/84}

program modal;

   uses
      {$U Obj/Memtypes    } Memtypes,
      {$U Obj/QuickDraw   } QuickDraw,
      {$U Obj/OSIntf      } OSIntf,
      {$U Obj/ToolIntf    } ToolIntf,
      {$U Obj/PackIntf    } PackIntf;

   const
      numItems = 16;
      numRadGroups = 2;
      numChks = 1;
      OKBtn = OK;          {OK and Cancel constants defined by Dialog Mgr;}
      cancelBtn = Cancel;
      stopChk = 14;
      textItem = 6;        {item number for editable text item}
      btnOff = 0;
      btnOn = 1;
      noWrap = -1;


   {I have radio buttons handled as sets.  Don't do it this way!  A much better way is
    to put the default setting and the "group" number in the control's refCon field.  Then, when a
    radio button is turned on, the code will walk through the control list (probably with GetDItem)
    , check each control to make sure it's a radio button, and turn it off if its group was hit. }


    var
 {  Note that for both radio buttons and check boxes, I don't need to keep a "nowOn" field - the
    Control's value field works fine.}
      radSet : packed array [1..numRadGroups] of packed record
                                                defaultOn, nowOn : 0..255;
                                                btns : set of 1..numItems;
                                                        end; {record}
      chkBoxes : packed array [1..numChks] of packed record
                                                default : btnOff..btnOn;
                                                btn : 1..numItems;
                                              end;

      index : integer;
      debug : text;
      theDialog : DialogPtr;{pointer to dialog record - returned by GetNewDialog}
      itemHit : integer;   {item where event occurred - returned by ModalDialog}
      {the next three things are returned by GetDItem :}
      theType : integer;   {gives the type of the item requested}
      theTextHdl, theOkHdl, itemHdl, radHdl : handle;    {gives a handle to the item}
      txtBox, OKbox, itemBox, radBox : Rect;    {gives the display rectangle of the item}
      theText : str255;    {text of the edit item - returned by GetIText}
      iBeamHdl : CursHandle; {handle to the special cursor}
      theDlogPeek : DialogPeek; {also a pointer to a dialog record - this type
                                  of pointer is required to set up the editable
                                  text for no word wrap}
      wasDown : boolean;   {used to obscure cursor after a click in the text}
      teHdl : teHandle;
      paperDef : integer;


   function theFilter (theDialog : DialogPtr; var theEvent : EventRecord;
                    var item : integer) : boolean;
   {This function is called by ModalDialog for every event that occurs while it's
    in control.  It's used to perform special filtering (hence the clever name)
    of events before letting ModalDialog take over.  For example, we use it to
    change the cursor to an I-beam when it's on editable text and to obscure
    (hide) the I-beam cursor after the user clicks in editable text (a la Lisa).
    We must also tell ModalDialog that a press of Return or Enter by the user
    should be treated the same as if he had clicked on the OK button
    (per user interface standards).}

   const
     crCode = 13;      {ASCII code generated by Return and Enter keys.  Note that}
     enterCode = 3;    {this is good for any keyboard, no matter how it's mapped
                         or what language the user speaks (keypad too)}

   var
      mouseLoc : Point;
      finalTicks : longint;

   begin
      itemHit := 0;        {We return these two values.  Initialize them.}
      theFilter := false;

      {Frill: When you click in editable text and start typing, TextEdit calls
       ObscureCursor, which hides the cursor until you move the mouse again.
       This keeps the cursor from getting in your way while you type.  Lisa
       editing adds a nice touch:  the cursor is obscured as soon as you click,
       before you start typing.  This stuff does the same thing.
       It would be nice if we could just wait for a MouseUp event in the text,
       but TextEdit "eats" the MouseUp event when it calls WaitMouseUp instead
       of StillDown; so, we do it this way.}

      if wasDown and not StillDown
         then begin
                 ObscureCursor;    {User clicked in editable text}
                 wasDown := false; {Reset "mouse down" flag}
              end;

      case theEvent.what of
         nullEvent : begin  {Nothing is going on, so fix the cursor}
                        GetMouse (mouseLoc);
                        if PtInRect (mouseLoc, txtBox) {box is TextEdit's box}
                           then SetCursor (iBeamHdl^^)
                           else SetCursor (arrow);       {make sure it's an arrow}
                     end;

         mouseDown : begin
                GlobalToLocal (theEvent.where); {because PtInRect wants it that way}
                if PtInRect (theEvent.where, txtBox)    {Set mouse down flag}
                                   then wasDown := true;{if mouse down in text box}
                LocalToGlobal (theEvent.where); {because Dlog Mgr wants it that way}
                     end;

         keyDown, autoKey :
                  begin
                     if (theEvent.message mod 256) in [crCode, enterCode]
                     then           {user pressed Return or Enter}
                        begin
                           GetDItem (theDialog, OKbtn, theType, theOkHdl, OKbox);
                           HiliteControl (ControlHandle (theOkHdl),
                                          btnOn); {make it look...}
                           Delay (3, finalTicks); {...like the OK button was hit}
                           theFilter := true;   {dialog is over}
                           itemHit := OKBtn;    {simulate user hitting OK}
                     end
                  end
      end  {case theEvent.what}
   end; {function theFilter}


begin {main program}
   InitGraf (@thePort);          {the big five inits}
   InitFonts;
   InitWindows;
   TEInit;
   InitDialogs (nil);

   reset (debug, '.bout');

   iBeamHdl := GetCursor (iBeamCursor); {load special cursor}
   wasDown := false;                {just initializing here}

   {The next chunk of code is used to set up the radio button and check box
    data structures.  It should be done during initialization of the program.
    Since the settings must exist between incarnations of the dialog box,
    these variables must not be local to the procedure that puts up the box.}

   radSet [1].btns := [8, 9];
   radSet [1].defaultOn := 8;
   radSet [1].nowOn := radSet [1].defaultOn;

   radSet [2].btns := [11, 12, 13];
   radSet [2].defaultOn := 12;
   radSet [2].nowOn := radSet [2].defaultOn;

   chkBoxes [1].btn := stopChk;
   chkBoxes [1].default := btnOn;     {default 'Stop printing' check box to on}

   {The following loop actually does the dialog.  It should be called each
    time you want the dialog to happen.}

      repeat
         FlushEvents (everyEvent, 0); {this throws out leftover clicks, keys}
         theDialog := GetNewDialog (1000, nil, pointer (-1));
               {this "draws" the dialog box (but it's invisible)}

         for index := 1 to numRadGroups do   {initialize default radio buttons}
            begin
               GetDItem (theDialog, radSet [index].defaultOn, theType, itemHdl, itemBox);
               SetCtlValue (ControlHandle (itemHdl), btnOn);
            end;

         for index := 1 to numChks do  {Initialize default check boxes}
            begin
               GetDItem (theDialog, chkBoxes [index].btn, theType, itemHdl, itemBox);
               SetCtlValue (ControlHandle (itemHdl), chkBoxes [index].default);
                     {set default value of check box}
            end;

         GetDItem (theDialog, textItem, theType, theTextHdl, txtBox);
            {this tells us three things: (1), theTextHdl is a handle to the editable
             text, which we need to find out the text later; (2), txtBox tells us
             where the text is, so we can change the pointer to an I-beam when
             it moves over the text; and (3), theType tells us the type of the
             item, which in this case we already know is editable text}

         theDlogPeek := DialogPeek (theDialog);  {get peek-type pointer to dialog...}
         theDlogPeek^.textH^^.crOnly := noWrap;   {...so we can turn off word wrap}
         SetPort (theDialog);       {this makes later GlobalToLocal calls work right}
         ShowWindow (theDialog);    {here I am!}
         InitCursor;                {we're ready, show an arrow}

            repeat
               ModalDialog (@theFilter,itemHit); {calls theFilter for every event}
               GetDItem (theDialog, itemHit, theType, itemHdl, itemBox);
                     {find out what was hit}
                case theType of
                   chkCtrl + ctrlItem : {check box was hit; toggle its state}
                      SetCtlValue (ControlHandle (itemHdl),
                      BitXor (GetCtlValue (ControlHandle (itemHdl)), 1));
                   radCtrl + ctrlItem : begin  {radio button was hit}
                      index := 1;
                      while not (itemHit in radSet [index].btns)
                        do index := index + 1; {find out which set
                                                of buttons was hit}
                      GetDItem (theDialog, radSet [index].nowOn, theType,
                                radHdl, radBox);  {get handle to button now on}
                      SetCtlValue (ControlHandle (radHdl), btnOff); {old button off}
                      SetCtlValue (ControlHandle (itemHdl), btnOn); {new button on}
                      radSet [index].nowOn := itemHit; {update data structure}
                                        end; {radCtrl case}

                end; {case theType}

            until itemHit in [OKBtn, cancelBtn];
               {dialog ends when user hits a button (or presses Return or Enter,
                which theFilter makes look like a hit on the OK button)}

         if itemHit = cancelBtn
            then begin {Revert radio buttons to their
                        settings before dialog was
                        called.}
                     for index := 1 to numRadGroups do
                       radSet [index].nowOn := radSet [index].defaultOn;
   {Note that I don't have to revert the checkboxes, since their "default"
    field has not been changed.}
                 end
            else begin  {Update default settings}
                     for index := 1 to numRadGroups do
                       radSet [index].defaultOn := radSet [index].nowOn;
                     for index := 1 to numChks do
                        begin
                           GetDItem (theDialog, chkBoxes [index].btn, theType,
                                     itemHdl, itemBox);
                           chkBoxes [index].default :=
                                  GetCtlValue (ControlHandle (itemHdl));
                        end
                 end;

         GetIText (theTextHdl, theText); {this tells us the text that the user entered}
         {do something with it - what?}
      until  theText = 'quit';   {all done if user typed 'quit'}

end.
