;-*- Mode: Lisp; Package: (g :use (common-lisp)) -*- G Source Code

; Source Code for G, a low-level, device-independent, graphics language
; for Macintosh Common Lisp.  See http://envy.cs.umass.edu/People/sutton/G/g.html.

(defpackage :g
            (:use :common-lisp :ccl))

(in-package :g)


;;;; OBJECTS

(export '(*g-device* g-view g-window g-device 
          g-get-parent g-set-parent g-get-children g-close-view))

(defvar *g-device*)                                           ;[Doc]

(defclass g-view (view)                                       ;[Doc]
  ((cs-left :initform 0.0s0 :accessor cs-left)
   (cs-bottom :initform 0.0s0 :accessor cs-bottom)
   (cs-right :initform  1.0s0 :accessor cs-right)
   (cs-top :initform  1.0s0 :accessor cs-top)
   (offsetx :accessor offsetx)
   (offsety :accessor offsety)
   (scalex :accessor scalex)
   (scaley :accessor scaley)))

(defmethod initialize-instance ((view g-view) &key parent)    ;[Doc]
  (without-event-processing
    (call-next-method)
    (unless (or (typep view 'g-device)
                (typep view 'window)
                (eq parent :none))
      (unless parent
        (setq parent (or (front-window :class 'g-window) (front-window))))
      (set-view-container view parent)
      (set-view-size view (view-size parent)))
    (g-update-normalization view)))

(defclass g-device (g-view)                                   ;[Doc]
  ((children :initform nil :accessor children))
  (:default-initargs 
    :view-size (make-point *screen-width* *screen-height*)))

(defclass g-window (g-view window)                            ;[Doc]
  ((parent :initarg :parent)
   (last-color :initform nil))
  (:default-initargs 
    :parent *g-device*
    :color-p t))

(defmethod initialize-instance :after                         ;[Doc]
           ((window g-window) &key g-viewport gd-viewport g-viewport-r gd-viewport-r)
  (without-event-processing
    (when g-viewport (apply #'g-set-viewport window g-viewport))
    (when gd-viewport (apply #'gd-set-viewport window gd-viewport))
    (when g-viewport-r (apply #'g-set-viewport-r window g-viewport-r))
    (when gd-viewport-r (apply #'gd-set-viewport-r window gd-viewport-r))
    (let ((device (g-get-parent window)))
      (push window (children device))))
  (when (typep (target) 'listener)
    (window-select (target))))

(defmethod g-close-view ((view view))                       ;[Doc]
  "Let the parent know this view is no longer in use"
  (remove-subviews (g-get-parent view) view))

(defmethod g-close-view ((window g-window))
  "Close the window and let the parent know this view is no longer in use"
  (setf (children (g-get-parent window))
        (remove window (children (g-get-parent window))))
  (window-close window))

(defmethod g-close-view ((window window))
  (window-close window))

(defmethod g-get-parent ((view simple-view))                       ;[Doc]
  (view-container view))

(defmethod g-get-parent ((window g-window))
  (slot-value window 'parent))

(defmethod g-get-parent ((window window))
  *g-device*)

(defmethod g-set-parent ((view view) new-parent)
  (let ((parent (g-get-parent view)))
    (when parent (remove-subviews parent view))
    (set-view-container view new-parent)))
    
(defmethod g-set-parent ((window window) new-parent)
  (with-slots (parent) window
    (when parent 
      (setf (children parent)
            (remove window (children parent))))
    (setq parent new-parent)))    

(defmethod g-get-children ((view g-view))                     ;[Doc]
  (subviews view))

(defmethod g-get-children ((device g-device))
  (if (eq device *g-device*) 
    (union (children device) (windows))
    (children device)))


	
;;; VIEWS and COLORS and COORDINATE SYSTEMS

(export '(g-set-viewport g-get-viewport gd-set-viewport gd-get-viewport 
          g-set-viewport-r g-get-viewport-r gd-set-viewport-r gd-get-viewport-r 
          g-set-viewport-size gd-set-viewport-size 
          g-set-coordinate-system g-get-coordinate-system gd-get-coordinate-system
          g-set-coordinate-system-r g-get-coordinate-system-r gd-get-coordinate-system-r
          g-set-coordinate-system-scale g-set-coordinate-system-scale-r
          g-set-cs-r g-get-cs-r gd-get-cs-r
          g-set-cs g-get-cs gd-get-cs
          g-set-cs-scale g-get-cs-scale
          g-accept-new-viewport-size 
          g-accept-new-viewport-position 
          g-coord-x g-coord-y gd-coord-x gd-coord-y 
          g-offset-x g-offset-y gd-offset-x gd-offset-y 
          g-color-rgb g-color-rgb-255
          g-color-bw 
          g-color-pen
          g-color-user-pick
          g-color-black
          g-color-white
          g-color-pink
          g-color-red
          g-color-orange
          g-color-yellow
          g-color-green
          g-color-dark-green
          g-color-light-blue
          g-color-blue
          g-color-purple
          g-color-brown
          g-color-tan
          g-color-light-gray
          g-color-gray
          g-color-cyan
          g-color-magenta
          g-color-dark-gray
          g-color-flip
          g-color-invisible
          g-color-on
          g-color-off
          g-set-color
          set-view-size
          set-view-position
          g-accept-new-viewport-position
          g-update-normalization
          gd-convert-x
          gd-convert-y
          g-convert-x
          g-convert-y))


(defmethod set-view-size ((view g-view) h &optional v)
  (declare (ignore h v))
  (without-event-processing
    (call-next-method)
    (g-update-normalization view)
    (g-accept-new-viewport-size view)))

(defmethod set-view-position ((view g-view) x-pos &optional y-pos)
  (declare (ignore x-pos y-pos))
  (without-event-processing
    (call-next-method)
    (g-accept-new-viewport-position view)))

(defmethod g-accept-new-viewport-size ((view g-view))         ;[Doc]
  )

(defmethod g-accept-new-viewport-position ((view g-view))
  )


(defclass screen-coordinates (view) ())

(defmethod g-accept-new-viewport-position :before ((view screen-coordinates))
  (set-view-scroll-position 
   view (local-to-global view (view-scroll-position view)))
  (loop for child in (g-get-children view)
        do (g-accept-new-viewport-position child)))


(defun gd-get-viewport (view)                                 ;[Doc]
  (let ((point1 (view-position view))
        (point2 (view-size view)))
    (setq point2 (add-points point1 point2))
    (values (point-h point1)
            (point-v point1)
            (1- (point-h point2))
            (1- (point-v point2)))))

(defun g-get-viewport (view)                                  ;[Doc]
  (let ((parent (g-get-parent view)))
    (multiple-value-bind (dx1 dy1 dx2 dy2) (gd-get-viewport view)
      (let ((x1 (g-coord-x parent dx1))
            (y1 (g-coord-y parent dy1))
            (x2 (g-coord-x parent dx2))
            (y2 (g-coord-y parent dy2)))
        (values (min x1 x2)
                (min y1 y2)
                (max x1 x2)
                (max y1 y2))))))
          
(defun gd-get-viewport-r (view)                               ;[Doc]
  (multiple-value-bind (x1 y1 x2 y2) (gd-get-viewport view)
    (values x1 y1 (- x2 x1) (- y2 y1))))

(defun g-get-viewport-r (view)                                ;[Doc]
  (multiple-value-bind (x1 y1 x2 y2) (g-get-viewport view)
    (values (min x1 x2)
            (min y1 y2)
            (abs (- x2 x1))
            (abs (- y2 y1)))))
          
(defun gd-set-viewport (view dx1 dy1 dx2 dy2)       ;[Doc]
  (set-view-position view (min dx1 dx2) (min dy1 dy2))
  (set-view-size view 
                 (1+ (abs (- dx1 dx2)))
                 (1+ (abs (- dy1 dy2)))))

(defun g-set-viewport (view vpx1 vpy1 vpx2 vpy2)              ;[Doc]
  (let ((parent (g-get-parent view)))
    (gd-set-viewport view
                     (gd-coord-x parent vpx1)
                     (gd-coord-y parent vpy1)
                     (and vpx2 (gd-coord-x parent vpx2))
                     (and vpy2 (gd-coord-y parent vpy2)))))

                                                              ;[Doc]
(defun gd-set-viewport-r (view dx dy &optional delta-x delta-y)
  (when (null dx) (setq dx (nth-value 0 (gd-get-viewport view))))
  (when (null dy) (setq dy (nth-value 1 (gd-get-viewport view))))
  (when (null delta-x) (setq delta-x (nth-value 2 (gd-get-viewport-r view))))
  (when (null delta-y) (setq delta-y (nth-value 3 (gd-get-viewport-r view))))
  (gd-set-viewport view dx dy (+ dx delta-x) (+ dy delta-y)))

(defun g-set-viewport-r (view x y &optional delta-x delta-y)  ;[Doc]
  (when (null x) (setq x (nth-value 0 (g-get-viewport view))))
  (when (null y) (setq y (nth-value 1 (g-get-viewport view))))
  (when (null delta-x) (setq delta-x (nth-value 2 (g-get-viewport-r view))))
  (when (null delta-y) (setq delta-y (nth-value 3 (g-get-viewport-r view))))
  (g-set-viewport view x y (+ x delta-x) (+ y delta-y)))

(defun g-get-cs (view)
  (g-get-coordinate-system view))

(defun g-get-coordinate-system (view)                         ;[Doc]
  (with-slots (cs-left cs-right cs-bottom cs-top) view
    (values (min cs-left cs-right)
            (min cs-top cs-bottom)
            (max cs-right cs-left)
            (max cs-top cs-bottom)
            (if (<= cs-left cs-right)
              (if (<= cs-bottom cs-top)
                :lower-left
                :upper-left)
              (if (<= cs-bottom cs-top)
                :lower-right
                :upper-right)))))

(defun gd-get-cs (view)
  (gd-get-coordinate-system view))

(defun gd-get-coordinate-system (view)                        ;[Doc]      ; 
  (let ((min-point (view-scroll-position view))
        (max-point (view-size view)))
    (setq max-point (add-points min-point max-point))
    (values (point-h min-point)
            (point-v min-point)
            (1- (point-h max-point))
            (1- (point-v max-point))
            :upper-left)))

(defun g-get-cs-r (view)
  (g-get-coordinate-system-r view))

                                                              ;[Doc]
(defun g-get-coordinate-system-r (view)
  (multiple-value-bind (x1 y1 x2 y2 corner) (g-get-coordinate-system view)
    (values x1 y1 (- x2 x1) (- y2 y1) corner)))

(defun gd-get-cs-r (view)
  (gd-get-coordinate-system-r view))

                                                              ;[Doc]
(defun gd-get-coordinate-system-r (view)
  (multiple-value-bind (x1 y1 x2 y2 corner) (gd-get-coordinate-system view)
    (values x1 y1 (- x2 x1) (- y2 y1) corner)))

                                                              ;[Doc]
(defun g-get-cs-scale (view)
  (g-get-coordinate-system-scale view))

                                                              ;[Doc]
(defun g-get-coordinate-system-scale (view)
  (multiple-value-bind (x1 y1 x2 y2 corner) (g-get-coordinate-system view)
    (declare (ignore x2 y2))
    (with-slots (scalex scaley) view
      (values x1 y1 scalex scaley corner))))

(defun g-set-cs (view x1 y1 x2 y2 &optional (corner :lower-left))
  (g-set-coordinate-system view x1 y1 x2 y2 corner))

                                                              ;[Doc]
(defun g-set-coordinate-system (view x1 y1 x2 y2 &optional (corner :lower-left))
  (setq x1 (coerce x1 'float)
        y1 (coerce y1 'float)
        x2 (coerce x2 'float)
        y2 (coerce y2 'float))
  (cond ((= x1 x2)
         (print "Attempt to set left and right of G coordinate system to same values.")
         (setq x2 (+ x1 1)))
        ((= y1 y2)
         (print "Attempt to set top and bottom of G coordinate system to same values.")
         (setq y2 (+ y1 1)))
        (t (with-slots (cs-left cs-right cs-top cs-bottom) view
    (setf cs-left (if (member corner '(:lower-left :upper-left)) x1 x2))
    (setf cs-bottom (if (member corner '(:lower-left :lower-right)) y1 y2))
    (setf cs-right (if (member corner '(:lower-left :upper-left)) x2 x1))
    (setf cs-top (if (member corner '(:lower-left :lower-right)) y2 y1)))))
  (g-update-normalization view))

(defun g-set-cs-r (view x y delta-x delta-y &optional (corner :lower-left))
  (g-set-coordinate-system-r view x y delta-x delta-y corner))

                                                              ;[Doc]
(defun g-set-coordinate-system-r (view x y delta-x delta-y &optional (corner :lower-left))
  (g-set-coordinate-system view x y (+ x delta-x) (+ y delta-y) corner))

                                                              ;[Doc]
(defun g-set-cs-scale (view x y x-scale &optional y-scale (corner :lower-left))
  (g-set-coordinate-system-scale view x y x-scale y-scale corner))

                                                              ;[Doc]
(defun g-set-coordinate-system-scale (view x y x-scale &optional y-scale (corner :lower-left))
  (when (not (numberp x-scale))
    (setq x-scale (ecase x-scale
                    (:inches 72)
                    (:centimeters 28.35)
                    (:pixels 1)
                    (:points 1))))
  (when (not (numberp y-scale))
    (setq y-scale (ecase y-scale
                    ('nil x-scale)
                    (:inches 72)
                    (:centimeters 28.35)
                    (:pixels 1)
                    (:points 1))))
  (multiple-value-bind (dx1 dy1 dx2 dy2) (gd-get-viewport view)
    (let ((x2 (+ x (/ (abs (- dx1 dx2)) (float x-scale))))
          (y2 (+ y (/ (abs (- dy1 dy2)) (float y-scale)))))
      (g-set-coordinate-system view x y x2 y2 corner))))

;(defun gd-within-coordinate-system-p (view dx dy)
;  (multiple-value-bind (min-x min-y max-x max-y) (gd-get-coordinate-system view)
;    (and (<= min-x dx max-x)
;         (<= min-y dy max-y))))

(defun g-update-normalization (view)
  "Updates state variables of normalized coordinate system"
  (with-slots (scalex scaley offsetx offsety cs-left cs-bottom cs-right cs-top) view
    (multiple-value-bind (x1 y1 x2 y2) (gd-get-coordinate-system view)
      (if (= cs-right cs-left)
        (error "Attempt to establish invalid (zero area) G coordinate system")
        (setf scalex (/ (- x2 x1) (- cs-right cs-left))))
      (setf offsetx (- x1 (* cs-left scalex)))
      (if (= cs-bottom cs-top)
        (error "Attempt to establish invalid (zero area) G coordinate system")
        (setf scaley (/ (- y2 y1) (- cs-bottom cs-top))))
      (setf offsety (- y1 (* cs-top scaley))))))

(defun gd-coord-x (view x)                                    ;[Doc]
  (with-slots (offsetx scalex) view
    (round (+ offsetx
              (* x scalex)))))

(defun gd-coord-y (view y)                                    ;[Doc]
  (with-slots (offsety scaley) view
    (round (+ offsety
              (* y scaley)))))

(defun gd-coords (view x y)
  (values (gd-coord-x view x) (gd-coord-y view y)))
                                                              ;[Doc]
(defun gd-offset-x (view x-offset)
  "Returns the length in device coords (pixels) of the x-distance in normal coords"
  (with-slots (scalex) view
    (round (* x-offset scalex))))
                                                              ;[Doc]
(defun gd-offset-y (view y-offset)
  "Returns the length in device coords (pixels) of the y-distance in normal coords"
  (with-slots (scaley) view
    (round (* y-offset scaley))))

(defun gd-offset (view x-offset y-offset)
  (values (gd-offset-x view x-offset) (gd-offset-y view y-offset)))

;;; Converting from device to normal coordinates

(defun g-coord-x (view dx)                                    ;[Doc]
  (with-slots (offsetx scalex) view
    (/ (- dx offsetx) scalex)))

(defun g-coord-y (view dy)                                    ;[Doc]
  (with-slots (offsety scaley) view
    (/ (- dy offsety) scaley)))

(defun g-coords (view dx dy)
  (values (g-coord-x view dx) (g-coord-y view dy)))

(defun g-offset-x (view dx-offset)                            ;[Doc]
  (with-slots (scalex) view
    (round (/ dx-offset scalex))))

(defun g-offset-y (view dy-offset)                            ;[Doc]
  (with-slots (scaley) view
    (round (/ dy-offset scaley))))

(defun g-offset (view dx-offset dy-offset)
  (values (g-offset-x view dx-offset) (g-offset-y view dy-offset)))

;;; Converting coordinates between views:

                                                              ;[Doc]
(defun gd-convert-x (from-view to-view dx)
  (point-h (global-to-local to-view (local-to-global from-view (make-point dx 0)))))

                                                              ;[Doc]
(defun gd-convert-y (from-view to-view dy)
  (point-v (global-to-local to-view (local-to-global from-view (make-point 0 dy)))))

                                                              ;[Doc]
(defun g-convert-x (from-view to-view x)
  (g-coord-x to-view (gd-convert-x from-view to-view (gd-coord-x from-view x))))

                                                              ;[Doc]
(defun g-convert-y (from-view to-view y)
  (g-coord-y to-view (gd-convert-y from-view to-view (gd-coord-y from-view y))))


;; Color Routines

(defvar *pens* (make-hash-table :test #'equal))
(defvar *colors* (make-hash-table :test #'equal))

(defun translate-pattern (keyword)
  (ecase keyword
    (:black-pattern *black-pattern*)
    (:white-pattern *white-pattern*)
    (:gray-pattern *gray-pattern*)
    (:light-gray-pattern *light-gray-pattern*)
    (:dark-gray-pattern *dark-gray-pattern*)))

                                                              ;[Doc]
(defun g-color-pen (view color pattern &optional mode x-size y-size)
  "Returns a new color with specified pen characteristics"
  (declare (ignore view))
  (let (pen)
    (setq pen (if (atom color)
                (list (or pattern :black-pattern)
                      (or mode :patCopy)
                      (make-point (or x-size 1) (or y-size 1)))
                (list (or pattern (second color))
                      (or mode (third color))
                      (make-point (or x-size (point-h (fourth color)))
                                  (or y-size (point-v (fourth color)))))))
    (setq pen (or (gethash pen *pens*)
                  (setf (gethash pen *pens*) pen)))
    (setq color (cons (if (atom color)
                        color (first color))
                      pen))
    (or (gethash color *colors*)
        (setf (gethash color *colors*) color))))
     
(defun g-color-pen-flip (view color)
  (g-color-pen view color :black-pattern :patXor))

(defun g-color-pen-invisible (view color)
  (g-color-pen view color :black-pattern :NotPatOr))

(defun g-color-size (view color x-size &optional y-size)
  (g-color-pen view color nil nil x-size y-size))

                                                              ;[Doc]
(defun g-color-black (view) (declare (ignore view)) *black-color*)
(defun g-color-white (view) (declare (ignore view)) *white-color*)
(defun g-color-pink (view) (declare (ignore view)) *pink-color*)
(defun g-color-red (view) (declare (ignore view)) *red-color*)
(defun g-color-orange (view) (declare (ignore view)) *orange-color*)
(defun g-color-yellow (view) (declare (ignore view)) *yellow-color*)
(defun g-color-green (view) (declare (ignore view)) *green-color*)
(defun g-color-dark-green (view) (declare (ignore view)) *dark-green-color*)
(defun g-color-light-blue (view) (declare (ignore view)) *light-blue-color*)
(defun g-color-blue (view) (declare (ignore view)) *blue-color*)
(defun g-color-purple (view) (declare (ignore view)) *purple-color*)
(defun g-color-brown (view) (declare (ignore view)) *brown-color*)
(defun g-color-tan (view) (declare (ignore view)) *tan-color*)
(defun g-color-light-gray (view) (declare (ignore view)) *light-gray-color*)
(defun g-color-gray (view) (declare (ignore view)) *gray-color*)
(defun g-color-dark-gray (view) (declare (ignore view)) *dark-gray-color*)
(defun g-color-on (view) (declare (ignore view)) *black-color*)
(defun g-color-off (view) (declare (ignore view)) *white-color*)
(defun g-color-cyan (view) (g-color-rgb view 0 1 1))
(defun g-color-magenta (view) (g-color-rgb view 1 0 1))

(defun g-color-flip (view) 
  (g-color-pen-flip view *black-color*))

(defun g-color-invisible (view) 
  (g-color-pen-invisible view *black-color*))

(defun g-color-rgb (view red green blue)                      ;[Doc]
  (declare (ignore view))
  (setq red (min 1.0 (max 0.0 red)))
  (setq green (min 1.0 (max 0.0 green)))
  (setq blue (min 1.0 (max 0.0 blue)))
  (make-color (floor (* 65535 red))
              (floor (* 65535 green))
              (floor (* 65535 blue))))

(defun g-color-rgb-255 (view red green blue)                  ;[Doc]
  (declare (ignore view))
  (setq red (min 255 (max 0 red)))
  (setq green (min 255 (max 0 green)))
  (setq blue (min 255 (max 0 blue)))
  (make-color (floor (* 257 red))
              (floor (* 257 green))
              (floor (* 257 blue))))

(defvar black *black-color*)
(defvar white *white-color*)
(defvar pink *pink-color*)
(defvar red *red-color*)
(defvar orange *orange-color*)
(defvar yellow *yellow-color*)
(defvar green *green-color*)
(defvar dark-green *dark-green-color*)
(defvar light-blue *light-blue-color*)
(defvar blue *blue-color*)
(defvar purple *purple-color*)
(defvar brown *brown-color*)
(defvar tan *tan-color*)
(defvar light-gray *light-gray-color*)
(defvar gray *gray-color*)
(defvar dark-gray *dark-gray-color*)
(defvar on *black-color*)
(defvar off *white-color*)
(defvar cyan (g-color-cyan t))
(defvar magenta (g-color-magenta t))
(defvar flip (g-color-flip t))
(defvar invisible (g-color-invisible t))

(defun g-color-bw (view intensity)                            ;[Doc]
;  (when (= 4 (ccl::screen-bits (ccl::window-screen (view-window view))))
;    (setq intensity (/ (round (* 15 intensity)) 15)))
  (setq intensity (- 1 intensity))
  (g-color-rgb view intensity intensity intensity))

(defun g-color-user-pick (view &rest args)                    ;[Doc]
  (declare (ignore view))
  (apply #'user-pick-color args))

(defun g-set-color (view color)                               ;[Doc]
  (set-color-if-needed (view-window view) color))

(defun set-color-if-needed (window color)
  "Sets the fore-color and pen of the window, if needed, to color"
  (if (not (typep window 'g-window))
    (if (atom color)                  ; not a g-window
      (set-fore-color window color)
      (progn (set-fore-color window (first color))
             (pen-normal window)
             (when (neq (second color) :black-pattern)
               (set-pen-pattern window (translate-pattern (second color))))
             (when (neq (third color) :patCopy)
               (set-pen-mode window (third color)))
             (when (neq (fourth color) #@(1 1))
               (set-pen-size window (fourth color)))))       
    (with-slots (last-color) window   ; is a g-window
      (unless (eq color last-color)
        (if (atom color)
          (if (atom last-color)       ; color is atomic
            (set-fore-color window color)
            (progn (when (neq color (first last-color))
                     (set-fore-color window color))
                   (pen-normal window)))
          (if (atom last-color)       ; color is list
            (progn (when (neq (first color) last-color)       ; last-color atomic
                     (set-fore-color window (first color)))
                   (when (neq (second color) :black-pattern)
                     (set-pen-pattern window (translate-pattern (second color))))
                   (when (neq (third color) :patCopy)
                     (set-pen-mode window (third color)))
                   (when (neq (fourth color) #@(1 1))
                     (set-pen-size window (fourth color))))
            (progn (when (neq (first color) (first last-color))       ; both lists
                     (set-fore-color window (first color)))
                   (when (neq (second color) (second last-color))
                     (set-pen-pattern window (translate-pattern (second color))))
                   (when (neq (third color) (third last-color))
                     (set-pen-mode window (third color)))
                   (when (neq (fourth color) (fourth last-color))
                     (set-pen-size window (fourth color)))))) 
        (setq last-color color)))))


;;; GD-GRAPHICS

(export 'gd-draw-point)
(export 'gd-draw-line)
(export 'gd-draw-line-r)
(export 'gd-outline-rect)
(export 'gd-outline-rect-r)
(export 'gd-fill-rect)
(export 'gd-fill-rect-r)
(export 'gd-draw-circle)
(export 'gd-draw-disk)
(export 'gd-draw-arc)
(export 'gd-draw-text)
(export 'gd-draw-text-centered)
(export 'gd-text-width)
(export 'gd-text-height)
(export 'gd-get-cursor-position)

(defun gd-draw-point (view dx dy &optional color-code)        ;[Doc]
  (without-interrupts
;   (unless (eq *current-view* view) (focus-view view))
   (with-focused-view view
   (when color-code (set-color-if-needed (view-window view) color-code))
   (#_MoveTo :long (make-point dx dy))
   (#_Line :long #@(0 0)))))
                                                              ;[Doc]
(defun gd-draw-line (view dx1 dy1 dx2 dy2 &optional color-code)
  (without-interrupts
;   (unless (eq *current-view* view) (focus-view view))
   (with-focused-view view
   (when color-code (set-color-if-needed (view-window view) color-code))
   (#_MoveTo :long (make-point dx1 dy1))
   (#_LineTo :long (make-point dx2 dy2)))))

                                                              ;[Doc]
(defun gd-draw-line-r (view dx dy delta-x delta-y &optional color-code)
  (gd-draw-line view dx dy (+ dx delta-x) (+ dy delta-y) color-code))

                                                              ;[Doc]
(defun gd-outline-rect (view dx1 dy1 dx2 dy2 &optional color-code)
  (gd-outline-rect-r view dx1 dy1 (- dx2 dx1) (- dy2 dy1) color-code))

                                                              ;[Doc]
(defun gd-outline-rect-r (view dx dy delta-x delta-y &optional color-code)
  (incf delta-x dx)
  (incf delta-y dy)
  (rlet ((rect :rect))
    (points-to-rect (make-point dx dy)
                    (make-point delta-x delta-y)
                    rect)
    (incf (rref rect :rect.bottom))
    (incf (rref rect :rect.right))
    (without-interrupts
;   (unless (eq *current-view* view) (focus-view view))
   (with-focused-view view
     (when color-code (set-color-if-needed (view-window view) color-code))
     (#_FrameRect rect)))))

                                                              ;[Doc]
(defun gd-fill-rect (view dx1 dy1 dx2 dy2 &optional color-code)
  (gd-fill-rect-r view dx1 dy1 (- dx2 dx1) (- dy2 dy1) color-code))

                                                              ;[Doc]
(defun gd-fill-rect-r (view dx dy delta-x delta-y color-code)
  (incf delta-x dx)
  (incf delta-y dy)
  (rlet ((rect :rect))
    (points-to-rect (make-point dx dy)
                    (make-point delta-x delta-y)
                    rect)
    (incf (rref rect :rect.bottom))
    (incf (rref rect :rect.right))
    (without-interrupts
;   (unless (eq *current-view* view) (focus-view view))
   (with-focused-view view
     (when color-code (set-color-if-needed (view-window view) color-code))
     (#_PaintRect rect)))))

                                                              ;[Doc]
(defun gd-draw-circle (view dx dy dradius &optional color-code)
  (without-interrupts
;   (unless (eq *current-view* view) (focus-view view))
   (with-focused-view view
   (when color-code (set-color-if-needed (view-window view) color-code))
   (ccl::with-rectangle-arg 
     (r (- dx dradius) (- dy dradius) (+ dx dradius) (+ dy dradius)) 
     (#_FrameOval r)))))

                                                              ;[Doc]
(defun gd-draw-arc (view dx dy dradius start-angle angle &optional color-code)
  (without-interrupts
;   (unless (eq *current-view* view) (focus-view view))
   (with-focused-view view
   (when color-code (set-color-if-needed (view-window view) color-code))
   (ccl::with-rectangle-arg 
     (r (- dx dradius) (- dy dradius) (+ dx dradius) (+ dy dradius))
     (#_FrameArc r (- 90 start-angle) (- angle))))))

                                                              ;[Doc]
(defun gd-draw-disk (view dx dy dradius &optional color-code)
  (without-interrupts
;   (unless (eq *current-view* view) (focus-view view))
   (with-focused-view view
   (when color-code (set-color-if-needed (view-window view) color-code))
   (ccl::with-rectangle-arg 
     (r (- dx dradius) (- dy dradius) (+ dx dradius) (+ dy dradius)) 
     (#_PaintOval r)))))

                                                              ;[Doc]
(defun gd-draw-text (view text font dx dy &optional color-code)
  (without-interrupts
   ;   (unless (eq *current-view* view) (focus-view view))
   (with-focused-view view
     (when color-code (set-color-if-needed (view-window view) color-code))
     (#_MoveTo :long (make-point dx dy))
     (let ((old-font (view-font view)))
       (unwind-protect 
         (progn (set-view-font view font)
                (stream-write-string view text 0 (length text)))
         (set-view-font view old-font))))))
 
                                                              ;[Doc]
(defun gd-draw-text-centered (view string font dx dy &optional color-code)
  (let ((half-length (round (* .5 (gd-text-width view string font))))
	(half-height (round (* .5 (gd-text-height view string font)))))
   (gd-draw-text view string font (- dx half-length) (- dy half-height) color-code)))

(defun gd-text-width (view-ignore string character-style)     ;[Doc]
  (declare (ignore view-ignore))
  (string-width string character-style))

                                                              ;[Doc]
(defun gd-text-height (view-ignore text-ignore character-style)
  (declare (ignore view-ignore text-ignore))
  (font-info character-style))

; gd-read-cursor (calls view-mouse-position and converts coords)

                                                              ;[Doc]
(defun gd-get-cursor-position (view)
  "Returns the current cursor position in appropriate coordinates"
  (let* ((point (view-mouse-position view))
         (dx (point-h point))
         (dy (point-v point)))
      (values dx dy)))




;;; G-GRAPHICS

(export 'g-clear)
(export 'g-make-visible)
(export 'g-draw-point)
(export 'g-draw-line)
(export 'g-draw-line-r)
(export 'g-outline-rect)
(export 'g-outline-rect-r)
(export 'g-fill-rect)
(export 'g-fill-rect-r)
(export 'g-draw-circle)
(export 'g-draw-disk)
(export 'g-draw-arc)
(export 'g-draw-text)
(export 'g-draw-text-centered)
(export 'g-text-width)
(export 'g-text-height)
(export 'g-get-cursor-position)

                                                              ;[Doc]
(defun g-clear (view &optional (color (g-color-off view)))
  (multiple-value-bind (min-x min-y max-x max-y) (gd-get-coordinate-system view)
    (gd-fill-rect view min-x min-y max-x max-y color)))

(defun g-make-visible (view)                                  ;[Doc]
  (if (typep view 'window)
    (window-select view)
    (g-make-visible (view-container view))))

(defun g-draw-point (view x y &optional color)                ;[Doc]
  (let ((dx (gd-coord-x view x))
	(dy (gd-coord-y view y)))
     (gd-draw-point view dx dy color)))

(defun g-draw-line (view x1 y1 x2 y2 &optional color)         ;[Doc]
  (let ((dx1 (gd-coord-x view x1))
	(dy1 (gd-coord-y view y1))
	(dx2 (gd-coord-x view x2))
	(dy2 (gd-coord-y view y2)))
    (gd-draw-line view dx1 dy1 dx2 dy2 color)))
                                                              ;[Doc]
(defun g-draw-line-r (view x y delta-x delta-y &optional color)
  (g-draw-line view x y (+ x delta-x) (+ y delta-y) color))

(defun g-outline-rect (view x1 y1 x2 y2 &optional color)      ;[Doc]
  (let ((dx1 (gd-coord-x view x1))
	(dy1 (gd-coord-y view y1))
	(dx2 (gd-coord-x view x2))
	(dy2 (gd-coord-y view y2)))
     (gd-outline-rect view dx1 dy1 dx2 dy2 color)))
                                                              ;[Doc]
(defun g-outline-rect-r (view x y delta-x delta-y &optional color)
  (let ((dx1 (gd-coord-x view x))
	(dy1 (gd-coord-y view y))
	(dx2 (gd-coord-x view (+ x delta-x)))
	(dy2 (gd-coord-y view (+ y delta-y))))
    (gd-outline-rect view dx1 dy1 dx2 dy2 color)))

(defun g-fill-rect (view x1 y1 x2 y2 &optional color)         ;[Doc]
  (let ((dx1 (gd-coord-x view x1))
	(dy1 (gd-coord-y view y1))
	(dx2 (gd-coord-x view x2))
	(dy2 (gd-coord-y view y2)))
    (gd-fill-rect view dx1 dy1 dx2 dy2 color)))
                                                              ;[Doc]
(defun g-fill-rect-r (view x y delta-x delta-y &optional color)
  (let ((dx1 (gd-coord-x view x))
	(dy1 (gd-coord-y view y))
	(dx2 (gd-coord-x view (+ x delta-x)))
	(dy2 (gd-coord-y view (+ y delta-y))))
    (gd-fill-rect view dx1 dy1 dx2 dy2 color)))

(defun g-draw-circle (view x y radius &optional color)        ;[Doc]
  (let ((dx (gd-coord-x view x))
	(dy (gd-coord-y view y))
	(dradius (gd-offset-x view radius)))
    (gd-draw-circle view dx dy dradius color)))

(defun g-draw-disk (view x y radius &optional color)          ;[Doc]
  (let ((dx (gd-coord-x view x))
	(dy (gd-coord-y view y))
	(dradius (gd-offset-x view radius)))
    (gd-draw-disk view dx dy dradius color)))
                                                              ;[Doc]
(defun g-draw-arc (view x y radius start-angle angle &optional color)
  (let ((dx (gd-coord-x view x))
	(dy (gd-coord-y view y))
	(dradius (gd-offset-x view radius)))
    (gd-draw-arc view dx dy dradius start-angle angle color)))

(defun g-draw-text (view string font x y &optional color)     ;[Doc]
  (let ((dx (gd-coord-x view x))
	(dy (gd-coord-y view y)))
    (gd-draw-text view string font dx dy color)))
                                                              ;[Doc]
(defun g-draw-text-centered (view string font x y &optional color)
  (let ((half-length (* .5 (g-text-width view string font)))
	(half-height (* .5 (g-text-height view string font))))
   (g-draw-text view string font (- x half-length) (- y half-height) color)))

                                                              ;[Doc]
(defun g-text-width (view string character-style)
  (g-offset-x view (gd-text-width view string character-style)))

(defun g-text-height (view text character-style)              ;[Doc]
 (g-offset-y view (gd-text-height view text character-style)))

                                                              ;[Doc]
(defun g-get-cursor-position (view)
  "Returns the current cursor position in appropriate coordinates"
  (multiple-value-bind (dx dy) (gd-get-cursor-position view)
    (when dx
      (g-coords view dx dy))))



;;; EVENTS


; View-click-event-handler for a gus-window calls g-click-event-handler and
;  gd-click-event-handler for each gus context.
; Window-mouse-up-event-handler for a gus-window calls
;  g-mouse-up-event-handler for each gus-context.

(export '(gd-click-event-handler g-click-event-handler
          g-mouse-up-event-handler gd-cursor g-cursor
          g-draw-view view-draw-contents
          view-click-event-handler
          *grow-cursor* *cross-hair-cursor*))

(defmethod view-draw-contents ((view g-view))
  (g-draw-view view))

(defmethod g-draw-view ((view g-view))                        ;[Doc]
  (loop for child in (g-get-children view)
        do (view-draw-contents child)))

; To respond to a mouse click on a view you specialize a method
;    (gd-click-event-handler g-view dx dy)
; or (g-click-event-handler g-view x y)
; which will be called each time there is a mouse click in that view.

(defmethod view-click-event-handler :after ((view g-view) point-of-click)
  (let ((dx (point-h point-of-click))
        (dy (point-v point-of-click)))
    (gd-click-event-handler view dx dy)
    (g-click-event-handler view 
                           (g-coord-x view dx)
                           (g-coord-y view dy))))

(defmethod gd-click-event-handler ((view g-view) dx dy)       ;[Doc]
  (declare (ignore dx dy)))
(defmethod g-click-event-handler ((view g-view) x y)          ;[Doc]
  (declare (ignore x y)))

(defmethod window-mouse-up-event-handler ((view g-view))
  (g-mouse-up-event-handler view))

(defmethod g-mouse-up-event-handler ((view g-view)) )

(defmethod view-cursor ((view g-view) point-of-click)
  (let ((dx (point-h point-of-click))
        (dy (point-v point-of-click)))
    (or (g-cursor view
                  (g-coord-x view dx)
                  (g-coord-y view dy))
        (gd-cursor view dx dy)
        *arrow-cursor*)))

(defmethod gd-cursor ((view g-view) dx dy)                    ;[Doc]
  (declare (ignore dx dy)))
(defmethod g-cursor ((view g-view) x y)                       ;[Doc]
  (declare (ignore x y)))

;;;;;;;;;;;;;;;;;;;;
;; 
;; cursor hacking
;;

(defun make-cursor (data-string mask-string hotspot)
  (when (or (> (length (string data-string)) 64)
            (> (length (string mask-string)) 64))
    (error "data-string & mask-string must be < 64 chars long"))
  (rlet ((data :bits16)
         (mask :bits16))
    (with-pstrs ((data-str data-string)
                 (mask-str mask-string))
      (#_StuffHex :ptr data :ptr data-str)
      (#_StuffHex :ptr mask :ptr mask-str))
    (make-record :cursor
               :data data
               :mask mask
               :hotspot hotspot)))

(defun make-grow-cursor ()
  (make-cursor "00003FC02040204027F82448244824483FC80408040807F80000000000000000"
               "00003FC03FC03FC03FF83FF83FF83FF83FF807F807F807F80000000000000000"
               #@(2 3)))

(defvar *grow-cursor* (make-grow-cursor))

;the cross-hair-cursor
(defun make-cross-hair-cursor ()
  (make-cursor "04000400040004000400FFE00400040004000400040004000000000000000000"
               "0000000000000000000000000000000000000000000000000000000000000000"
               #@(5 5)))

(defvar *cross-hair-cursor* (make-cross-hair-cursor))



;;; ADDITIONAL

(export '(gd-draw-arrow gd-draw-arrowhead g-draw-arrow g-draw-arrowhead
          gd-draw-arrow-r gd-draw-arrowhead-r g-draw-arrow-r g-draw-arrowhead-r))

                                                              ;[Doc]
(defun gd-draw-arrow (view dx1 dy1 dx2 dy2 &optional color)
  "Draws an arrow starting at dx1,dy1 and ending at dx2,dy2 of color"
  (gd-draw-arrowhead view dx1 dy1 dx2 dy2 1.0 0.25 color))
                                                              ;[Doc]
(defun gd-draw-arrow-r (view dx dy delta-x delta-y &optional color)
  "Draws an arrow starting at dx,dy and ending at dx+delta-x,dy+delta-y of color"
  (gd-draw-arrowhead view dx dy (+ dx delta-x) (+ dy delta-y) 1.0 0.25 color))

(defvar angle-tangent 0.7)
;	Angle-tangent is the tangent of the angle between the base  main
;  part of the arrow and one of the two parts of the arrowhead.  I don't
;  know what happens if you make this parameter negative.  The default
;  value is 0.7.
                                                              ;[Doc]
(defun gd-draw-arrowhead (view dx1 dy1 dx2 dy2 body-size head-size &optional color)
  "Draws an arrowhead dx2,dy2 from dx1,dy1 of color and sizes"
  (let ((delta-x (* (- dx2 dx1) head-size))
        (delta-y (* (- dy2 dy1) head-size)))
    (unless (= 0 body-size)			;draw arrow body
      (gd-draw-line-r view dx2 dy2
		      (round (* body-size (- dx1 dx2)))
		      (round (* body-size (- dy1 dy2))) color))
    (gd-draw-line-r view dx2 dy2
		    (round (- (* (- delta-y) Angle-tangent) delta-x))
		    (round (- (* delta-x Angle-tangent) delta-y))
		    color)
    (gd-draw-line-r view dx2 dy2
		    (round (- (* delta-y Angle-tangent) delta-x))
		    (round (- (* (- delta-x) Angle-tangent) delta-y))
		    color)))
                                                              ;[Doc]
(defun gd-draw-arrowhead-r (view dx dy delta-x delta-y body-size head-size &optional color)
  "Draws an arrowhead starting at dx,dy and ending at dx+delta-x,dy+delta-y of color and sizes"
  (gd-draw-arrowhead view dx dy (+ dx delta-x) (+ dy delta-y) body-size head-size color))

(defun g-draw-arrow (view x1 y1 x2 y2 &optional color)        ;[Doc]
  (let ((dx1 (gd-coord-x view x1))
	(dy1 (gd-coord-y view y1))
	(dx2 (gd-coord-x view x2))
	(dy2 (gd-coord-y view y2)))
    (gd-draw-arrowhead view dx1 dy1 dx2 dy2 1.0 0.25 color)))

                                                              ;[Doc]
(defun g-draw-arrow-r (view x y delta-x delta-y &optional color)
  (g-draw-arrow view x y (+ x delta-x) (+ y delta-y) color))

                                                              ;[Doc]
(defun g-draw-arrowhead (view x1 y1 x2 y2 body-size head-size &optional color)
  (let ((dx1 (gd-coord-x view x1))
	(dy1 (gd-coord-y view y1))
	(dx2 (gd-coord-x view x2))
	(dy2 (gd-coord-y view y2)))
    (gd-draw-arrowhead view dx1 dy1 dx2 dy2 body-size head-size color)))

                                                              ;[Doc]
(defun g-draw-arrowhead-r (view x y delta-x delta-y body-size head-size &optional color)
  (g-draw-arrowhead view x y (+ x delta-x) (+ y delta-y) body-size head-size color))


(export 'maintain-g-viewports-of-children)
(defclass maintain-g-viewports-of-children (g-view) () )

(defmethod g-accept-new-viewport-size 
           ((view maintain-g-viewports-of-children))
  (let* ((children (g-get-children view))
         (g-viewports (loop for child in children
                            collect (multiple-value-list 
                                     (g-get-viewport child)))))
    (call-next-method view)
    (loop for child in children
          for g-viewport in g-viewports
          do (apply #'g-set-viewport child g-viewport))))





;;; FINAL INITIALIZATION

(unless (boundp '*g-device*)
  (setq *g-device* (make-instance 'g-device)))

;;; More things, on g

(export '(with-g-coordinate-system
           with-g-cs
           with-g-coordinate-system-r
           with-g-cs-r
           with-g-coordinate-system-scale
           with-g-cs-scale))

(defmacro with-g-coordinate-system ((view x1 y1 x2 y2 &optional corner) &body body)
  `(multiple-value-bind (oldx1 oldy1 oldx2 oldy2 old-corner) (g-get-cs ,view)
    (unwind-protect 
      (progn (g-set-coordinate-system ,view ,x1 ,y1 ,x2 ,y2 ,corner)
             . ,body)
      (g-set-coordinate-system ,view oldx1 oldy1 oldx2 oldy2 old-corner))))

(defmacro with-g-cs ((view x1 y1 x2 y2 &optional corner) &body body)
  `(with-g-coordinate-system (,view ,x1 ,y1 ,x2 ,y2 ,corner) . ,body))

(defmacro with-g-coordinate-system-r ((view x y delta-x delta-y &optional corner) &body body)
  `(multiple-value-bind (oldx1 oldy1 oldx2 oldy2 old-corner) (g-get-cs ,view)
    (unwind-protect 
      (progn (g-set-coordinate-system-r ,view ,x ,y ,delta-x ,delta-y ,corner)
             . ,body)
      (g-set-coordinate-system ,view oldx1 oldy1 oldx2 oldy2 old-corner))))

(defmacro with-g-cs-r ((view x1 y1 x2 y2 &optional corner) &body body)
  `(with-g-coordinate-system-r (,view ,x1 ,y1 ,x2 ,y2 ,corner) . ,body))

(defmacro with-g-coordinate-system-scale ((view x y xscale &optional yscale corner) &body body)
  `(multiple-value-bind (oldx1 oldy1 oldx2 oldy2 old-corner) (g-get-cs ,view)
    (unwind-protect 
      (progn (g-set-coordinate-system-scale ,view ,x ,y ,xscale ,yscale ,corner)
             . ,body)
      (g-set-coordinate-system ,view oldx1 oldy1 oldx2 oldy2 old-corner))))

(defmacro with-g-cs-scale ((view x y xscale &optional yscale corner) &body body)
  `(with-g-coordinate-system-scale (,view ,x ,y ,xscale ,yscale ,corner) . ,body))

#|
(export 'basic-g-colors)                ; mix this with views to make color-slots
(export '(black white pink red orange yellow green dark-green light-blue blue purple 
         brown tan light-gray gray dark-gray flip invisible on off))

(defclass basic-g-colors (view)
  (black white pink red orange yellow green dark-green light-blue blue purple 
         brown tan light-gray gray dark-gray flip invisible on off))

(defmethod initialize-instance ((view basic-g-colors) &key)
  (without-event-processing
    (call-next-method)
    (with-slots (black white pink red orange yellow green dark-green light-blue blue purple 
                       brown tan light-gray gray dark-gray flip invisible on off) view
      (setq black (g-color-black view))
      (setq white (g-color-white view))
      (setq pink (g-color-pink view))
      (setq red (g-color-red view))
      (setq orange (g-color-orange view))
      (setq yellow (g-color-yellow view))
      (setq green (g-color-green view))
      (setq dark-green (g-color-dark-green view))
      (setq light-blue (g-color-light-blue view))
      (setq blue (g-color-blue view))
      (setq purple (g-color-purple view))
      (setq brown (g-color-brown view))
      (setq tan (g-color-tan view))
      (setq light-gray (g-color-light-gray view))
      (setq gray (g-color-gray view))
      (setq dark-gray (g-color-dark-gray view))
      (setq flip (g-color-flip view))
      (setq invisible (g-color-invisible view))
      (setq on (g-color-on view))
      (setq off (g-color-off view)))))
|#

(export '(gd-within-viewport-p g-within-viewport-p))

(defun gd-within-viewport-p (view dx dy)
  "Is dx,dy within viewport of view, in the coordinate system of the parent of view?"
  (multiple-value-bind (x1 y1 x2 y2) (gd-get-viewport view)
    (and (< x1 dx x2)
         (< y1 dy y2))))

(defun g-within-viewport-p (view x y)
  "Is x,y within viewport of view, in the coordinate system of the parent of view?"
  (multiple-value-bind (x1 y1 x2 y2) (g-get-viewport view)
    (and (< x1 x x2)
         (< y1 y y2))))

;