UNIT ActionNDAv2;

 {  ActionNDA -- Version 3.0 
    This NDA reports all action codes passed to the NDA action routine
    (DAAction).  This is a useful test for applications that want to be NDA
    friendly.

    The runAction indicator changes state every time a runAction code is passed
    to DAAction.  A value of 0 is used for the Period field of the NDA header
    so this will occur as often as possible (every time the application calls
    SystemTask).  If the runAction indicator does not blink, then your
    application is not calling SystemTask (applications using TaskMaster don't
    need to worry about this - TaskMaster calls SystemTask for you).

    The cursorAction indicator changes state every time a cursorAction code is
    passed to DAAction.  There are two differences between cursorAction and
    runAction:
      1)  cursorActions occur when only when the NDA is the frontmost
          window.  runActions always occur if the NDA is open.
      2)  cursorActions occur everytime SystemTask is called (if the NDA is
          the frontmost window.)  runActions occur when SystemTask is called
          only if the time period specified by the NDA header's Period field
          has elapsed.  cursorActions allow NDAs the opportunity to change
          the cursor when it is over the NDA's window.

    Whenever an editAction code is passed to DAAction (this will happen when
    Undo, Cut, Copy, Paste, or Clear is selected from the Edit menu), the type
    of editAction is displayed by the NDA for approximately 2 seconds.  If the
    editAction is not displayed, possible problems in your application are:
      o You didn't use the reserved ID numbers (250 through 255) for the
        Undo, Cut, Copy, Paste, Clear and Close menu item ID numbers.
      o Your application does not use TaskMaster and you are not calling
        SystemEdit when the Undo, Cut, Copy, Paste, or Clear menu items
        are selected when a system window (NDA) is the frontmost window.
        Remember, the Undo, Cut, Copy, Paste, Clear and Close menu items
        should always be enabled when a system window is the frontmost
        window.
        
    Whenever an eventAction code is passed to DAAction, the type of contents
    of the event record are displayed by the NDA.  There is a slight delay
    after each event record is displayed, so you can see events that normally
    flash by too fast to see.
    Note: The mouseUpEvt eventAction is handled differently than other
          eventActions.  Since mouseUpEvt eventActions can be passed to an
          NDA when it is unsafe to draw in the NDA window (i.e., when you
          release the mouse button after pulling down a menu over the NDA
          window or when you release the mouse button after dragging the NDA
          window), the event record for the mouseUpEvt is displayed the next
          time a runAction occurs.  Apple IIGS Technical Note #71 tells you
          to invalidate part of the window to cause an update event to happen
          later.  Since this NDA is showing you the current event, that method
          could not be used.
    The events this NDA will see are mouseDownEvt, mouseUpEvt, keyDownEvt,
    autoKeyEvt, updateEvt, and activateEvt.  If you aren't seeing all
    of these events, possible problems in your application are:
      o The taskMask you're passing to TaskMaster does not have the bits
        set to pass those events to a system window (the NDA).
      o Your application does not use TaskMaster and you are not calling
        SystemClick when the event is in the NDAs window.
    }

INTERFACE

  USES
    Types,
    IntMath,
    MiscTool,
    QuickDraw,
    Events,
    Controls,
    Windows,
    Desk;

{$Z+} { Export these procedures and functions. The identifiers for
        these procedures and functions must not be changed! }

  FUNCTION NDAOpen: WindowPtr;
  
  PROCEDURE NDAClose;
  
  FUNCTION NDAAction(code: integer;
                     evntRecPtr: EventRecordPtr): integer;

  PROCEDURE NDAInit(code: integer);

{$Z-} { keep any other procedures or functions local to this source }

IMPLEMENTATION

  CONST
    titleStr = 'ActionNDA';   { the window title }

  VAR
    { These variables are needed in every NDA. }
    myWindOpen: boolean;      { true when NDA is open }
    myWindParams: ParamList;  { window parameter list for NDA }
    myNDATitle: Str255;       { variable for NDA window title }
    myWindPtr: WindowPtr;     { pointer to NDA window }

    { These variables hold the current state of the NDA window. }
    runState,                 { toggled on every runAction }
    cursorState: boolean;     { toggled on every cursorAction }
    editState: integer;       { the last edit Action code received }
    eventState: EventRecord;  { a copy of the last event record passed }
    drawMouseUp: boolean;     { tells DrawRunAction to handle mouseUpEvts }
    editCounter: integer;       { Decremented on every runAction until 0. }
                              { At 0, the editAction is erased and      }
                              { editCounter is set to -1. }


{ Declare the following procedures (specific to this NDA) FORWARD. }

  PROCEDURE DrawEventAction;
    FORWARD;

  PROCEDURE DrawRunAction;
    FORWARD;

  PROCEDURE DrawCursorAction;
    FORWARD;

  PROCEDURE DrawEditAction;
    FORWARD;

  PROCEDURE DrawWindowText;
    FORWARD;


{ The next 4 procedures and functions are in every NDA. }


  FUNCTION NDAOpen: WindowPtr;

    BEGIN
      IF myWindOpen
        THEN
          SelectWindow(myWindPtr) { NDA window is already open, so select it }
        ELSE
          BEGIN   { NDA window is not open }
            { init some of the global variables }
            drawMouseUp := False;
            editCounter := 0;
            editState := 0; { something that won't be drawn }
            myNDATitle := 'ActionNDA';
            WITH myWindParams DO  { init myWindParams }
              BEGIN
                paramLength := sizeof(ParamList);
                wFrameBits := fTitle+fClose+fMove+fVis;
                wTitle := @myNDATitle;
                wRefCon := 0;
                SetRect(wZoom,0,0,0,0);
                wColor := NIL;
                wYOrigin := 0;
                wXOrigin := 0;
                wDataH := 0;
                wDataW := 0;
                wMaxH := 0;
                wMaxW := 0;
                wScrollVer := 0;
                wScrollHor := 0;
                wPageVer := 0;
                wPageHor := 0;
                wInfoRefCon := 0;
                wInfoHeight := 0;
                wFrameDefProc := NIL;
                wInfoDefProc := NIL;
                wContDefProc := NIL;
                SetRect(wPosition, 40, 35, 275, 155);
                wPlane := WindowPtr(-1);
                wStorage := NIL;
              END;
            myWindPtr := NewWindow(myWindParams); { create the NDA window }
            SetSysWindow(myWindPtr);  { and make it a system window }
          END;
      NDAOpen := myWindPtr; { result of function = pointer to NDA window }
      myWindOpen := true; { tell rest of NDA that we're open }
    END; { of NDAOpen }


  PROCEDURE NDAClose;

    BEGIN
      CloseWindow(myWindPtr); { close the NDA window }
      myWindOpen := False;  { tell rest of NDA that we're closed }
    END; { of NDAClose }


  FUNCTION NDAAction(code: integer;
                     evntRecPtr: EventRecordPtr): integer;

    VAR
      currPort: GrafPortPtr;
      r: rect;

    BEGIN
      NDAAction := 0; { default to result of 0 }
      CASE code OF
        eventAction:  { handle the eventAction code }
          BEGIN
            eventState := evntRecPtr^;
            CASE evntRecPtr^.what OF
              mouseDownEvt, keyDownEvt, autoKeyEvt: DrawEventAction;
              mouseUpEvt:
                BEGIN
                  drawMouseUp := true;
                END;
              updateEvt:
                BEGIN
                  currPort := GetPort;
                  SetPort(myWindPtr);
                  SetRect(r, 0, 0, 1000, 1000);
                  InvalRect(r); { for this NDA, redraw everything }
                  BeginUpdate(myWindPtr);
                  DrawWindowText; { redraw the content of the NDA window }
                  DrawRunAction;
                  DrawCursorAction;
                  DrawEventAction;
                  IF editCounter > 0
                    THEN DrawEditAction;
                  EndUpdate(myWindPtr);
                  SetPort(currPort);
                END; { updateEvt }
              activateEvt: DrawEventAction;
            END; { case }
          END;
        runAction:  { handle the runAction code }
          BEGIN
            runState := NOT runState;
            DrawRunAction;
          END;
        cursorAction: { handle the cursorAction code }
          BEGIN
            cursorState := NOT cursorState;
            DrawCursorAction;
          END;
        undoAction, cutAction, copyAction, pasteAction, clearAction:
                    { handle the edit action codes }
          BEGIN
            editState := Code;
            DrawEditAction;
            NDAAction := 1; { yes, we handled the edit }
          END;
      END;
    END; { of NDAAction }


  PROCEDURE NDAInit(code: integer);

    BEGIN
      IF code = 0
        THEN
          BEGIN
            { A DeskShutDown Call, close window if it's open }
            IF myWindOpen
              THEN NDAClose;
          END
        ELSE  { A DeskStartUp Call, init myWindOpen flag }
          myWindOpen := false;
    END; { of NDAInit }


{ The following procedures are specific to this NDA. }


  PROCEDURE DrawEventAction;
  { This procedure displays the contents of the eventRecord stored in
    eventState whenever an eventAction code is received. }

    VAR
      aStr: Str255;
      currPort: GrafPortPtr;
      r: rect;
      aWord, I: integer;
      startTick: Longint;

    BEGIN
      currPort := GetPort;
      SetPort(myWindPtr);
      SetRect(r, 82, 40, 1000, 90);
      EraseRect(r);

      MoveTo(82, 50); { display EventRecord.what }
      CASE eventState.what OF
        mouseDownEvt: DrawString('mouseDownEvt');
        mouseUpEvt: DrawString('mouseUpEvt');
        keyDownEvt: DrawString('keyDownEvt');
        autoKeyEvt: DrawString('autoKeyEvt');
        updateEvt: DrawString('updateEvt');
        activateEvt: DrawString('activateEvt');
      END;

      MoveTo(82, 60); { display EventRecord.message }
      CASE eventState.what OF
        mouseDownEvt, mouseUpEvt:
          BEGIN
            aStr := 'button x';
            Long2Hex(eventState.message, pointer(Longint(@aStr) + 8), 1);
            DrawString(aStr);
          END;
        keyDownEvt, autoKeyEvt:
          BEGIN
            aStr := 'ASCII $xx';
            Long2Hex(eventState.message, pointer(Longint(@aStr) + 8), 2);
            DrawString(aStr);
          END;
        updateEvt, activateEvt:
          BEGIN
            aStr := 'WindowPtr($xx/xxxx)';
            Long2Hex(HiWord(eventState.message),
                     pointer(Longint(@aStr) + 12), 2);
            Long2Hex(LoWord(eventState.message),
                     pointer(Longint(@aStr) + 15), 4);
            DrawString(aStr);
          END;
      END;

      MoveTo(82, 70); { display EventRecord.when }
      aStr := '$xxxxxxxx';
      Long2Hex(eventState.when, pointer(Longint(@aStr) + 2), 8);
      DrawString(aStr);

      MoveTo(82, 80); { display EventRecord.where }
      aStr := '($xxxx, $xxxx)';
      Long2Hex(HiWord(Longint(eventState.where)),
               pointer(Longint(@aStr) + 3), 4);
      Long2Hex(LoWord(Longint(eventState.where)),
               pointer(Longint(@aStr) + 10), 4);
      DrawString(aStr);

      MoveTo(82, 90); { display EventRecord.modifiers }
      aStr := '%';
      aWord := $8000;
      FOR I := 1 TO 16 DO { convert modifiers to a binary string }
        BEGIN
          IF I = 9
            THEN aStr := Concat(aStr, ' ');
          IF BAnd(aWord, eventState.modifiers) <> 0
            THEN aStr := Concat(aStr, '1')
            ELSE aStr := Concat(aStr, '0');
          aWord := BSR(aWord,1);
        END;
      DrawString(aStr);

      SetPort(currPort);  { pause }
      startTick := GetTick;
      REPEAT
      UNTIL (GetTick > startTick + 10);
    END; { of DrawEditAction }


  PROCEDURE DrawRunAction;
  { This procedure paints or erases the runAction indicator whenever
    a runAction code is received.  It also erases the text for the
    last editAction received when editCounter reaches 0, and it displays
    the eventRecord for mouseUp events if the drawMouseUp flag is true. }

    VAR
      currPort: GrafPortPtr;
      r: rect;

    BEGIN
      IF drawMouseUp = true
        THEN
          BEGIN
            DrawEventAction;
            drawMouseUp := False;
          END;

      currPort := GetPort;
      SetPort(myWindPtr);

      IF editCounter > 0
        THEN editCounter := pred(editCounter)
        ELSE
          IF editCounter = 0
            THEN
              BEGIN
                editCounter := pred(editCounter);
                SetRect(r, 90, 15, 150, 26);
                EraseRect(r);
              END;

      SetRect(r, 86, 4, 94, 9);
      IF runState
        THEN PaintOval(r)
        ELSE EraseOval(r);

      SetPort(currPort);
    END; { of DrawRunAction }


  PROCEDURE DrawCursorAction;
  { This procedure paints or erases the cursorAction indicator whenever
    a cursorAction code is received. }

    VAR
      currPort: GrafPortPtr;
      CurrColor: integer;
      r: rect;

    BEGIN
      currPort := GetPort;
      SetPort(myWindPtr);
      SetRect(r, 219, 4, 227, 9);
      IF cursorState
        THEN PaintOval(r)
        ELSE EraseOval(r);
      SetPort(currPort);
    END; { of DrawCursorAction }


  PROCEDURE DrawEditAction;
  { This procedure displays the last edit Action code received.
    It sets editCounter to 120.  editCounter is decremented whenever
    a runAction code is received.  When editCounter reaches 0, the edit
    Action type is erased in the NDA's window. }

    VAR
      currPort: GrafPortPtr;

    BEGIN
      currPort := GetPort;
      SetPort(myWindPtr);
      MoveTo(90, 25);
      CASE editState OF
        undoAction: DrawString('Undo   ');
        cutAction: DrawString('Cut     ');
        copyAction: DrawString('Copy   ');
        pasteAction: DrawString('Paste ');
        clearAction: DrawString('Clear ');
      END;
      SetPort(currPort);
      editCounter := 120;
    END; { of DrawEditAction }


  PROCEDURE DrawWindowText;
  { This procedure draws the static text items in the NDA window. }

    VAR
      currPort: GrafPortPtr;
      r: rect;

    BEGIN
      currPort := GetPort;
      SetPort(myWindPtr);

      MoveTo(5, 10);
      DrawString('runAction:');
      SetRect(r, 85, 3, 95, 10);
      FrameOval(r);

      MoveTo(115, 10);
      DrawString('cursorAction:');
      SetRect(r, 218, 3, 228, 10);
      FrameOval(r);

      MoveTo(5, 25);
      DrawString('editAction:');

      MoveTo(5, 40);
      DrawString('eventAction:');
      MoveTo(39, 50);
      DrawString('what=');
      MoveTo(15, 60);
      DrawString('message=');
      MoveTo(39, 70);
      DrawString('when=');
      MoveTo(32, 80);
      DrawString('where=');
      MoveTo(5, 90);
      DrawString('modifiers=');

      SetForeColor(1);
      MoveTo(5, 105);
      DrawString('Copyright 1990,');
      MoveTo(5, 115);
      DrawString('Apple Computer, Inc.');
      SetForeColor(0);

      SetPort(currPort);
    END; { of DrawWindowText }

  END.
