3. "6F^5PH r^E1!1!O;
;File ButCDef.TEXT
;--------------------------------------------------------------------------
;
;  Standard Button Definition Procedure for the
;       MacIntosh Control Manager
;
;  written by Andy Hertzfeld  August, 1982
;
;  (c) 1982 by Apple Computer, Inc.  All rights reserved.
;
;    This file contains the control definition procedures
;    that define the standard "button" type controls.  These
;    include PushButtons, CheckBoxes, and a checkBox variant called
;    RadioButton
;
;  Modification History:
;
;    22-Aug-82  AJH  Added clipping, centering to PushButProc
;    25-Aug-82  AJH  Fixed corner-clobber bug in pushButProc
;    29-Aug-82  AJH  Added "255" hiliting,
;    29-Sep-82  AJH  Added CheckBox control definition procedure
;    03-Oct-82  AJH  Fixed bug in scrollBar positioning -- wasn't ctl relative
;    05-Oct-82  AJH  Fixed checkBox flashing, added code saving optimizations
;    10-Oct-82  AJH  Converted for QuickDraw Trap Interface
;    17-Oct-82  AJH  Made controlProcs preserve A1
;    14-Nov-82  AJH  Improved PushButton roundness scaling; removed box in arrowBits
;    16-Nov-82  AJH  Made branch tables offset based
;    28-Dec-82  AJH  Put button definitions in their own file
;    11-Mar-83  AJH  fixed up check box drawing
;    27-Mar-83  AJH  made it respect initial clip in button drawing
;    01-Apr-83  AJH  made it use hardwired gray
;    04-Apr-83  AJH  made it force size 12 for text
;    07-Jun-83  AJH  fixed scrambling bug, changed check draw for new QuickDraw
;    29-Aug-83  AJH  fixed scrambling bug in CalcPBut
;    27-Sep-83  AJH  made variants > 7 not force the system font
;    12-Oct-83  AJH  changed button disabling
;    17-Oct-83  AJH  back to "gray-out" button disabling
;    31-Dec-83  AJH  made text of checkBox/radioButttons grayed out if disabled
;
;----------------------------------------------------------------------------

                .INCLUDE  TlAsm/SYSEQU.TEXT
                .INCLUDE  TlAsm/SYSMACS.TEXT
                .INCLUDE  TlAsm/GRAFEQU.TEXT
                .INCLUDE  TlAsm/GRAFTYPES.TEXT
                .INCLUDE  TlAsm/TOOLEQU.TEXT
                .INCLUDE  TlAsm/RESEQU.TEXT
                .INCLUDE  TlAsm/QuickMacs.TEXT
                .INCLUDE  TlAsm/ToolMacs.TEXT
;
                .PROC     BDEF,0

;
; FUNCTION PushButProc( selector:   INTEGER;
;                       theControl: ControlHandle;
;                       message:    INTEGER;
;                       param:      LongInt): LongInt;
;
;   PushButProc is the control definition procedure for simple pushButtons, one of the
; standard control types supported by the user interface toolBox. mon
;
SavePen         .EQU    -20
IndicatorRect   .EQU    -30
;
                BRA.S    @0                     ;skip header

; standard header

                .WORD    0                      ;flags
                .ASCII   'CDEF'
                .WORD    0
                .WORD    1                      ;version #
@0
                LINK    A6,#-30                 ;set up a stack frame to address parameters
                MOVEM.L D3-D7/A1-A4,-(SP)       ;save work registers
;
; buttons only handle messages 0,1 and 2
;
               CMP      #3,12(A6)               ;inspect message value
               BGE.S    DoneP1                  ;if >2, nothing to do
;
; save the penState and set it our way
;
                PEA     SavePen(A6)             ;push pointer to savePenState
                _GetPenState                    ;remember current penState
                _PenNormal                      ;set the pen the way we want it
;
; fetch the parameters into registers
;
                LEA     8(A6),A0                ;get ptr to first parameter
                MOVE.L  (A0)+,D3                ;get param in D3
                MOVE.W  (A0)+,D0                ;get message
                MOVE.L  (A0)+,A3                ;get the control handle
                MOVE.W  (A0)+,D6                ;get selection index
                MOVE.W  D6,D7                   ;remember raw selection code
                AND     #7,D6                   ;strip high part of selection code

                CLR.L   (A0)                    ;clear out function result
                MOVE.L  (A3),A0                 ;get control pointer in A0
;
; case out on the message number
;
                ADD     D0,D0                   ;double for word index
                LEA     GoPushBut,A1            ;get table address
                ADD     0(A1,D0),A1             ;compute dispatch address
                JSR     (A1)                    ;dispatch to appropriate routine
;
; restore original pen state
;
                PEA     SavePen(A6)             ;push savePenState
                _SetPenState                    ;restore original pen state
;
; we're done -- restore registers and return to caller
;
DoneP1
                MOVEM.L (SP)+,D3-D7/A1-A4       ;restore work registers
                UNLK    A6                      ;unlink stack frame
TenBytExit      MOVE.L  (SP)+,A0                ;get return address
                ADD     #12,SP                  ;strip parameters
                JMP     (A0)                    ;return to caller
;
; PushButProc dispatch table -- entries must be long branches!
;
GoPushBut
                .WORD   DrawPBut-GoPushBut      ;draw is message 0
                .WORD   HitPBut-GoPushBut       ;hit test is message 1
                .WORD   CalcPBut-GoPushBut      ;calc regions is message 2

ButStub         RTS
;
;  DrawPBut draws the pushButton
;
DrawPBut
                TST.B   ContrlVis(A0)           ;is it visible?
                BEQ.S   ButStub                 ;if not, we're done
;
; calculate roundness as function of rectangle size
;
                BSR     RoundCalc               ;compute roundNess factor in D4
;
; erase the bounding rectangle (if necessary)
;
                LEA     ContrlRect(A0),A4       ;get pointer to bounding rect
                TST.B   D6                      ;is it a pushButton?
                BEQ.S   EraseCBound             ;push buttons are always erased
                TST.B   D3                      ;draw all?
                BNE.S   SkipCErase              ;if not, don't bother to erase
;
EraseCBound
                MOVE.L  A4,-(SP)                ;push rect
                MOVE.L  D4,-(SP)                ;push rounding factor
                _EraseRoundRect                 ;paint it the background color
;
;  save the current font and force the system font
;
SkipCErase
                MOVE.L  LGlobals(A5),A0         ;get LisaGrafGlobals base
                MOVE.L  ThePort(A0),A0          ;get current port
                MOVE.L  txFont(A0),D3           ;remember the font,face
                MOVE    D7,D0                   ;save selection code
                MOVE.W  txSize(A0),D7           ;remember the size

                SUBQ    #8,D0                   ;was code 8 or greater?
                BGE.S   @1                      ;if so, use window's font

                CLR.L   txFont(A0)              ;force system font, normal face
                MOVE    #12,TxSize(A0)          ;force size = 12
@1
                MOVE.L  ClipRgn(A0),-(SP)       ;push the current clipRgn handle
;
; save old clip region and clip to the bounding rectangle sected with oldClip
;
                CLR.L    -(SP)                  ;make space for region handle
                _NewRgn                         ;allocate a region
                MOVE.L  (SP),A2                 ;remember region but leave on stack
                _GetClip                        ;remember the current clipRgn
                MOVE.L   (A3),A0                ;get control pointer
                PEA      ContrlRect(A0)         ;push pointer to its bounding rect
                _ClipRect                       ;make that the clipping region

                MOVE.L   A2,-(SP)               ;push the old ClipRgn
                MOVE.L   4(SP),-(SP)            ;the answer goes into current clip
                _SectRgn                        ;intersect new and old
;
; get a pointer to the title string and push it
;
                MOVE.L  (A3),A0                 ;get control pointer
                PEA     ContrlTitle(A0)         ;point to title string
;
; position the pen to center the string in its rectangle
;
                CLR.W   -(SP)                   ;make room for function result
                PEA     ContrlTitle(A0)         ;point to title string
                _StringWidth                    ;Get width of string
                MOVE.W  (SP)+, D0               ;String width in D0
;
                MOVE.L  (A3),A0                 ;handle -> pointer
                LEA     ContrlRect(A0),A4       ;set up pointer to bounding rect

                TST     D6                      ;is it a checkBox?
                BNE     PosCheck                ;checkBoxes center differently
;
                MOVE    RIGHT(A4),D1            ;get right coordinate
                SUB     LEFT(A4),D1             ;get width of button
                SUB     D0,D1                   ;get buttonWidth -stringWidth
                ASR     #1,D1                   ;divide by 2
                ADD     LEFT(A4),D1             ;add to left for starting X
;
DrawBTitle
                MOVE.W  D1, -(SP)               ;push left coordinate
;
                MOVE.W  Bottom(A4), D0          ;Get bottom coordinate
                MOVE    D0,D1                   ;remember in D1
                SUB.W   Top(A4),D0              ;get height
                SUB     #16,D0                  ;subtract 16
                ASR     #1,D0                   ;divide by 2
                SUB     D0,D1                   ;compute centered baseline
                SUBQ    #4,D1                   ;leave 4 for descenders
                MOVE.W  D1, -(SP)               ;Push it
                MOVE.W  D1,D5                   ;remember y position
                _MoveTo                         ;Move the pen there
                _DrawString                     ;draw it
;
; restore original font, face and size
;
                MOVE.L  LGlobals(A5),A0         ;get LisaGrafGlobals base
                MOVE.L  ThePort(A0),A0          ;get current port
                MOVE.L  D3,txFont(A0)           ;restore the font,face
                MOVE.W  D7,TxSize(A0)           ;restore the size

                TST     D6                      ;is it a checkBox?
                BNE     PlotCheck               ;if so, go draw it
;
                MOVE.L  A4,-(SP)                ;push the rectangle pointer
                MOVE.L  D4,-(SP)                ;push rounding factor
                _FrameRoundRect                 ;frame the button
;
; hilite the button if necessary
;
                MOVE.L  (A3),A0                 ;get control pointer
                MOVE.B  ContrlHilite(A0),D0     ;is it hilited?
                BEQ.S   DoneDrwBut              ;if not, we're done
;
                CMP.B   #$FE,D0                 ;is it the special hilite state?
                BHS.S   SpecialHilite           ;if so, go do it
;
                MOVE.L  A4,-(SP)                ;push rectangle
                MOVE.L  D4,-(SP)                ;push rounding factor
                _InverRoundRect                 ;hilite by inverting
;
; restore original clipping region and we're done
;
DoneDrwBut
                _PenNormal                      ;set the pen back to normal
                MOVE.L  A2,-(SP)                ;push old clip region
                _SetClip                        ;restore it
                MOVE.L  A2,-(SP)                ;dispose of temporary region
                _DisposRgn                      ;de-allocate it
                RTS                             ;all done!
;
; SpecialHilite handles drawing the disabled button

SpecialHilite
               BSR.S    DisableText
               BRA.S    DoneDrwBut
DisableText
               MOVE.L    A4,-(SP)                 ;push rectangle
               MOVE.L    #$00010003,-(SP)
               _InsetRect

               MOVE.L    A4,-(SP)

               MOVE.L    (A5),A0
               PEA       Gray(A0)
               _PenPat
               MOVE      #patBIC,-(SP)
               _PenMode

               _PaintRect                       ;gray it out
               _PenNormal

               MOVE.L    A4,-(SP)
               MOVE.L    #$FFFFFFFD,-(SP)
               _InsetRect

               RTS
;
; PosCheck does horizontal position for check box buttons.  It computes the position
; in D1 and dives back into common code
;
PosCheck
                MOVE.W  Left(A4),D1             ;get the left edge
                ADD.W   #18,D1                  ;leave room for check box
                BRA.S   DrawBTitle              ;back to common code
;
; RoundCalc calculates the rounding factor in D4 based on the control's rect
;
RoundCalc
                TST     D6                      ;is it a checkBox?
                BNE.S   CheckRound              ;if so, special case it
;
                MOVE.W  ContrlRect+Bottom(A0),D4 ;get bottom coordinate
                SUB.W   ContrlRect+Top(A0),D4   ;figure out vertical height
                LSR     #1,D4                   ;scale it down by a factor of 2
                MOVE    D4,D0                   ;fill both halves with it
                SWAP    D4                      ;get in high part
                MOVE    D0,D4                   ;and in low part
                RTS
;
CheckRound      MOVEQ   #0,D4                   ;checkBoxes are square!
                RTS
;
; HitPBut handles the button hit-test
;
HitPBut
                MOVE.B  ContrlHilite(A0),D0     ;get hiliteState
                ADDQ.B  #1,D0                   ;is it 255?
                BEQ.S   @1                      ;if so, skip
                ADDQ.B  #1,D0                   ;how about 254?
                BEQ.S   Return254               ;if so, we're done
;
                CLR.W   -(SP)                   ;make room for function result
                MOVE.L  D3,-(SP)                ;push the point
                PEA     ContrlRect(A0)          ;push address of rect
                _PtInRect                       ;in the rectangle?
                TST.B   (SP)+                   ;examine result
                BEQ.S   @1                      ;if not, we're done
                MOVE    #inButton,22(A6)        ;return that it was
                TST     D6                      ;a checkBox?
                BEQ.S   @1                      ;if not, we're done
                ADDQ    #1,22(A6)               ;if so, flag it
@1              RTS

Return254
                MOVE    #254,22(A6)             ;indicate its 254-disabled
                RTS
;
; CalcPBut returns the bounding region of the button
;
CalcPBut
                TST     D6                      ;is it a checkBox?
                BNE.S   CalcSquare              ;check box bounds are just rects
;
                BSR.S   RoundCalc               ;calculate rounding factor
                _HidePen                        ;dont draw anything
                _OpenRgn

                BSET     #7,(A3)                ;lock it down

                MOVE.L   (A3),A0                ;get pointer to control
                PEA     ContrlRect(A0)          ;push rectangle pointer
                MOVE.L  D4,-(SP)                ;push rounding factor
                _FrameRoundRect                 ;frame the button

                MOVE.L  D3,-(SP)                ;push the region
                _CloseRgn                       ;make the rounded rectangular region
                _ShowPen

                BCLR    #7,(A3)                 ;unlock the control
;
; set the pattern for indicator dragging
;
DragGray
                MOVE.L  (A5),A0                 ;get qDraw globals
                LEA     Gray(A0),A0             ;point to gray pattern
                MOVE.L  (A0)+,DragPattern       ;move in the 1st half
                MOVE.L  (A0),DragPattern+4      ;move in the 2nd half
                RTS                             ;all done!
;
CalcSquare
                MOVE.L  D3,-(SP)                ;push the region
                PEA     ContrlRect(A0)          ;push the rectangle pointer
                _RectRgn                        ;make a rectangulare region
                BRA.S   DragGray                ;all done -- go set drag pattern
;
; PlotCheck takes care of drawing the actual check box of the check box button.  It
; figures out where to draw the box, draws it, and then checks it or not based on
; the current value and hilite state of the button
;
PlotCheck
                SUBQ    #8,SP                   ;allocate a rectangle on the stack
                ADDQ    #2,D5                   ;bump down a little
                MOVE    D5,Bottom(SP)           ;set up the bottom
                SUB     #12,D5                  ;compute the top
                MOVE    D5,Top(SP)              ;set up the top
                MOVE    Left(A4),D5             ;get left edge of boundsRect
                ADDQ    #2,D5                   ;indent 2 pixels
                MOVE    D5,Left(SP)             ;that's the left of the checkRect
                ADD     #12,D5                  ;compute right edge
                MOVE    D5,Right(SP)            ;update the right edge
;
; erase the check box
;
                MOVE.L  SP,-(SP)                ;push rectangle pointer
                _EraseRect                      ;erase it
;
; OK, now we must fill in the checkBox rectangle based on the value and hilite state
; of the control
;
                MOVE.L  (A3),A0                 ;get control handle
                MOVE.W  ContrlValue(A0),D5      ;get the value
                MOVE.B  ContrlHilite(A0),D0     ;is it hilited?
                BEQ.S   FrameCheck              ;if not, skip
;
; its hilited so up the penSize to indicate its hilited
;
                CMP.B   #$FE,D0                 ;disabled?
                BLO.S   SkipDis                 ;if so, skip doubling
;
                BSR     DisableText

                BRA.S    FrameCheck

SkipDis
                MOVE.L  #$00020002,-(SP)
                _PenSize                        ;up the penSize
FrameCheck
                MOVE.L  SP,-(SP)                ;push the rectangle

                CMP.W   #2,D6                   ;test for radio button
                BEQ.S   @1                      ;if its a radio button, go do it
                _FrameRect                      ;frame it
                BRA.S    @2
@1
                _FrameOval
@2
                _PenNormal
;
; now we can draw the check if we're supposed to
;
                LSR     #1,D5                   ;check out the low bit of D5
                BCC.S   DonePCheck              ;if its off, we're done
;
                CMP.W   #2,D6                   ;test for radio button
                BEQ.S   DrawRButton             ;if its a radio button, go do it
;
                MOVE.L  SP,-(SP)                ;push the rectangle pointer
                MOVE.L  OneOne,-(SP)            ;push the inset factor
                _InsetRect                      ;inset the rectangle

                MOVE.L  TopLeft(SP),-(SP)       ;push top left
                _MoveTo                         ;move to it
                MOVE.L  BotRight(SP),-(SP)      ;push bottom right
                _LineTo                         ;draw one line of the cross
;
                MOVE    Right(SP),-(SP)         ;push right
                MOVE    Top+2(SP),-(SP)         ;push top
                SUBQ    #1,(SP)                 ;bias the top
                _MoveTo                         ;move to it
                MOVE    Left(SP),-(SP)          ;push left
                SUBQ    #1,(SP)                 ;bias the left
                MOVE    Bottom+2(SP),-(SP)      ;push bottom
                _LineTo                         ;draw the line
                BRA.S   DonePCheck
;
;  draw the radio button check mark -- a little circle
;
DrawRButton
                MOVE.L  SP,-(SP)                ;push pointer to rect
                MOVE.L  #$00030003,-(SP)        ;push inset factor
                _InsetRect                      ;inset it
                MOVE.L  SP,-(SP)                ;push rect again
                _PaintOval                      ;draw the circle?
;
DonePCheck
                ADDQ    #8,SP                   ;pop off the rectangle
                BRA     DoneDrwBut              ;all done!

               .END
