;;; -*- Mode: LISP; Syntax: Common-Lisp; Package: GEM; Base: 10 -*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;         The Garnet User Interface Development Environment.      ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; This code was written as part of the Garnet project at          ;;;
;;; Carnegie Mellon University, and has been placed in the public   ;;;
;;; domain.  If you are using this code or any part of Garnet,      ;;;
;;; please contact garnet@cs.cmu.edu to be put on the mailing list. ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; CHANGE LOG:
;;; 03/16/94  Andrew Mickish - Macroexpanded If-Debug call in Set-Interest-...
;;; 01/19/94  Andrew Mickish - Created

(in-package "GEM")


;; Internal variables that maintain state when checking for double clicking
(defparameter *last-state* NIL)
(defparameter *last-code* NIL)
(defparameter *last-time* 0)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EVENT HANDLING for mouse-move events -- see also MAC-Set-Interest-In-Moved
;;;    (other event-handler methods are defined in mac.lisp)

(defvar *active-drawable* NIL)

;; The ccl:*eventhook* variable contains a function that gets first pick on
;; all the events.  We set this variable with MAC-mouse-move-event-handler
;; when we want to get mouse-move events.  If the function returns T, then
;; MCL assumes the event has been handled, and does not pass it to the other
;; event-handling methods.
;;
;; The MAC-mouse-event-handler executes under the assumption that Garnet is
;; currently interested in mouse-move events.  (If we didn't want them, then
;; inter::Set-Interest-In-Moved or the view-[de]activate-event-handers would
;; have set ccl:*eventhook* to NIL.)
;;
(let ((old-x 0)
      (old-y 0))
  (defun mac-mouse-move-event-handler ()
    ;; The WHAT field of *current-event* is 0 unless something like a keyboard
    ;; or mouse-click event is being handled
    (when (and (zerop (ccl:rref ccl:*current-event* :EventRecord.what))
               gem::*active-drawable*)
      ;; Convert coordinates from global to event-window
      (let* ((opal-window (MAC-window-from-drawable
                           *root-window* gem::*active-drawable*))
             (wptr (ccl:wptr gem::*active-drawable*))
             (top-window (ccl:window-object wptr))
             (where (ccl::%global-to-local
                     wptr (ccl:rref ccl:*current-event* :EventRecord.where))))
        (unless (eq *active-drawable* top-window)
          (setf where (ccl:convert-coordinates where
                                               top-window *active-drawable*)))
        (let ((x (ccl:point-h where))
              (y (ccl:point-v where)))
          (unless (and (< -2 (- old-x x) 2) (< -2 (- old-y y) 2))
            (setf old-x x old-y y)
            (inter::Do-Motion-Notify opal-window x y NIL)
            T))))))

;; The view-[de]activate-event-handlers maintain the currently active Garnet
;; window in *active-drawable* and toggle interest in mouse-move events via
;; the ccl:*eventhook* variable.
;;
(defun process-activate-event (event-window)
  (let ((opal-window (MAC-window-from-drawable *root-window* event-window)))
    (setf gem::*active-drawable* event-window)
    (setf ccl:*eventhook* (if (and opal-window (g-value opal-window :interested?))
                              'mac-mouse-move-event-handler))))

(defmethod ccl:view-activate-event-handler ((event-window gem::MAC-DRAWABLE))
  (process-activate-event event-window)
  (call-next-method))

(defmethod ccl:view-activate-event-handler ((event-window gem::MAC-SUBDRAWABLE))
  (process-activate-event event-window)
  (call-next-method))

(defmethod ccl:view-deactivate-event-handler
    ((event-window gem::MAC-DRAWABLE))
  (when (eq event-window gem::*active-drawable*)
    (setf gem::*active-drawable* NIL)
    (setf ccl:*eventhook* NIL))
  (call-next-method))

(defmethod ccl:view-deactivate-event-handler
    ((event-window gem::MAC-SUBDRAWABLE))
  (when (eq event-window gem::*active-drawable*)
    (setf gem::*active-drawable* NIL)
    (setf ccl:*eventhook* NIL))
  (call-next-method))
                  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;; The state parameter that is passed around in the mouse event handler functions
;; for the Mac corresponds to modifier keys like "control", and also whether the
;; event is a double click.  This function sets a bit in the state when it
;; determines that we are processing the second click of a double click.
;;
(defun MAC-Check-Double-Press (root-window state code time)
  (declare (ignore root-window))
  (if inter::*double-click-time*    ; just check if non-nil
      (let ((what (ccl:rref ccl:*current-event* :EventRecord.what)))
	;; Don't count double-click if it is because of key repeat (return NIL)
	(unless (eq what 5)
	  ;; else do check
	  (let ((newcode (if (and (eq state *last-state*)
				  (eq code *last-code*)
				  (<= (- time *last-time*) (traps:_getdbltime)))
			     (+ code inter::*double-offset*) ;; is double click
			     code)))  ;; else not double click
	    ;; set up for next time
	    (setf *last-state* state)
	    (setf *last-code* code)
	    (setf *last-time* time)
	    ;;(format t "code = ~S   newcode = ~b~%" code newcode)
	    newcode)))
      ;; else not interested in double click
      code))


(defun MAC-discard-mouse-moved-events (root-window)
  (declare (ignore root-window)))

(defun MAC-set-interest-in-moved (window interestedp)

  ;; This macroexpansion of
  ;;   (inter::if-debug :mouse
  ;;       (format t "interested in mouse moved now ~s~%" interestedp))
  ;; is required because if-debug is a macro which hasn't been defined yet.
  (IF (AND INTERACTORS::*INT-DEBUG*
           (INTERACTORS::TRACE-TEST :MOUSE))
    (PROGN (LET ((*PRINT-PRETTY* NIL))
             (FORMAT T "interested in mouse moved now ~s~%" INTERESTEDP))))

  (let ((drawable (get-value window :drawable)))
    (s-value window :interested? interestedp)
    (if (and drawable (eq drawable gem::*active-drawable*))
        (setf ccl:*eventhook* (if interestedp
                                  'mac-mouse-move-event-handler)))))

  
(defun MAC-translate-mouse-character (root-window button-code modifier-bits
                                    event-key)
  (declare (ignore root-window))
  (case event-key
    (:button-release
     (aref inter::*mouse-up-translations*  button-code
           (inter::modifier-index modifier-bits)))
    (:button-press
     (aref inter::*mouse-down-translations*  button-code
           (inter::modifier-index modifier-bits)))
    ))


(defun key-code-to-button-code (key-code)
  (if (eq inter::*middledown-key* key-code)
      inter::*middle-button*
      (if (eq inter::*rightdown-key* key-code)
          inter::*right-button*
          (if (eq inter::*leftdown-key* key-code)
              inter::*left-button*))))

;;; The code parameter is the message field of ccl:*current-event*, documented
;;; in Inside Macintosh I, p. 252.  The "key-code" is the high-order word,
;;; which corresponds to the physical key on the keyboard.
;;;
;;; The key-code is important in distinguishing which key generated a
;;; character.  For example, ctrl-a and the Home key both generate the
;;; character-code #\Home, so you wouldn't be able to catch the CONTROL key
;;; during the ctrl-a event if you just looked at the character-code.
;;; You have to look at the key-code, too.
;;;
;;; Mapping of input keystrokes to character/symbol generated by this function:
;;;
;;;     main key       control?     shift?      RESULT
;;;     --------       --------     ------      ------
;;;    alphabetic        NIL         NIL         #\a
;;;    alphabetic        NIL          T          #\A
;;;    alphabetic         T          NIL      :control-\a
;;;    alphabetic         T           T       :control-A
;;;
;;;    numeric,special
;;;    such as [         NIL         NIL         #\[
;;;        [             NIL          T          #\{       <-- mapped by MCL
;;;        [              T         T/NIL     :control-[   <-- shift ignored
;;;
;;; The treatment of the Meta (option) key is identical to Control.
;;; With numeric and special characters, we ignore the shift key in the
;;; presence of the Control and Meta keys.  This is because the Mac does
;;; not return the expected character from a shift-modified special key.
;;; For example, you would expect shift-control-[ to map to control-{,
;;; but the Mac says it is control-esc.  There is no mapping that we can
;;; impose manually, like the arithmetic adjustment of #x20 that we perform
;;; on alphabetic characters (maybe you could build the required hash table,
;;; but this would change with different key-caps settings and different
;;; keyboards).


(defun MAC-translate-character (window x y state code time)
  (let* ((character-code (logand code #xFF))  ;; low-order word
         (key-code (ash code -8))             ;; high-order word
         (button-code (key-code-to-button-code key-code)))
    (if button-code
        (progn
          (inter::Do-Button-Press window x y state button-code time :BUTTON-PRESS)
          ;(format t "---->~X ~X ~S~%" key-code character-code button-code)
          NIL)

        (let ((char (gethash key-code inter::*keysym-translations*)))
          ;; Raise to upper case if necessary
          (when (characterp char)    ;; Unless it's a function key like :F1
            (let ((mapped-code (char-code char)))
              ;; If alphabetic...
              (if (<= #x61 mapped-code #x7A)
                (if (or (ccl:shift-key-p) (ccl:caps-lock-key-p))
                  (setf char (code-char (- (char-code char) #x20))))
                ;; Not alphabetic, so use Mac-generated character code.
                (unless (or (ccl:control-key-p) (ccl:option-key-p))
                  (setf char (code-char character-code))))))

          ;; BITS is ultimately an index into the inter::*prefixes* array that
          ;; corresponds to the set of modifier prefixes.
          (let ((bits (logand #b1100 state)))  ;; Throw away shifts
            (setf char (inter::base-char-to-character char bits)))
          
          ;(format t "---->~X ~X ~S~%" key-code character-code char)
          char))))
