changeset 26:1237f7555029

Rearranged namespaces. Mouse events represented by a record. Added alignment args to anchor.
author Mikhail Kryshen <mikhail@kryshen.net>
date Mon, 21 Jun 2010 04:00:45 +0400
parents 07ee065cbb3e
children 61bc04f94d61 4cb70c5a6e0d
files project.clj src/indyvon/component.clj src/indyvon/core.clj src/indyvon/event.clj src/indyvon/layers.clj
diffstat 5 files changed, 304 insertions(+), 273 deletions(-) [+]
line wrap: on
line diff
--- a/project.clj	Mon Jun 21 01:18:50 2010 +0400
+++ b/project.clj	Mon Jun 21 04:00:45 2010 +0400
@@ -1,8 +1,8 @@
 (defproject indyvon "1.0.0-SNAPSHOT"
-  :description "FIXME: write"
+  :description "INteractive DYnamic VisualizatiON library"
   :dependencies [[org.clojure/clojure "1.2.0-master-SNAPSHOT"]
                  [org.clojure/clojure-contrib "1.2.0-SNAPSHOT"]]
   :dev-dependencies [[leiningen/lein-swank "1.2.0-SNAPSHOT"]]
   :namespaces [indyvon.core
-               indyvon.event
+               indyvon.layers
                indyvon.component])
--- a/src/indyvon/component.clj	Mon Jun 21 01:18:50 2010 +0400
+++ b/src/indyvon/component.clj	Mon Jun 21 04:00:45 2010 +0400
@@ -5,10 +5,8 @@
 ;;
 
 (ns indyvon.component
-  (:use indyvon.core)
-  (:require (indyvon [event :as event]))
+  (:use indyvon.core indyvon.layers)
   (:import (java.awt Component Dimension Color)
-           (java.awt.event MouseEvent)
            (javax.swing JFrame JPanel)))
 
 (defn- font-context [component]
@@ -35,7 +33,7 @@
 
 (defn make-jpanel
   ([layer]
-     (make-jpanel layer (event/make-event-dispatcher)))
+     (make-jpanel layer (make-event-dispatcher)))
   ([layer event-dispatcher]
      (let [context (default-context)
            context (assoc context :dispatcher event-dispatcher)
@@ -53,9 +51,6 @@
   (do
     (def frame (JFrame. "Test"))
     
-    (defn handler [event context]
-      (println (:layer context) (.paramString event)))
-      
     (def layer1
          (reify
           Layer
@@ -65,7 +60,7 @@
           (size [this context] [30 20])
           MouseHandler
           (handle-mouse [this context event]
-             (println "layer1" (.paramString event)))))
+             (println "layer1" event))))
     
     (def layer1b (border-layer layer1 2 3))
     
@@ -80,7 +75,7 @@
           (size [this context] [70 65])
           MouseHandler
           (handle-mouse [this context event]
-             (println "layer2" (.paramString event)))))
+             (println "layer2" event))))
     
     (def layer3
          (border-layer (text-layer "Sample\ntext" :right :center)))
--- a/src/indyvon/core.clj	Mon Jun 21 01:18:50 2010 +0400
+++ b/src/indyvon/core.clj	Mon Jun 21 04:00:45 2010 +0400
@@ -5,15 +5,17 @@
 ;;
 
 (ns indyvon.core
-  (:import (java.awt Color Font Cursor)
-           (java.awt.font FontRenderContext TextLayout)
-           (java.awt.event MouseEvent)))
+  (:import (java.awt Color Font)
+           (java.awt.event MouseListener MouseMotionListener)))
 
 (defprotocol Layer
   "Basic UI element."
   (render! [this context graphics])
   (size [this context]))
 
+;; TODO: modifiers
+(defrecord MouseEvent [id when x y x-on-screen y-on-screen button])
+
 (defprotocol MouseHandler
   "Layers that also satisfy this protocol will recieve mouse events."
   (handle-mouse [this context event]))
@@ -25,6 +27,29 @@
   (hovered? [this layer])
   (picked? [this layer]))
 
+(defprotocol Anchored
+  "Provide anchor point for Layers. Used by viewport."
+  (anchor [this context h-align v-align]
+          "Anchor point: [x y], h-align could be :left, :center
+           or :right, v-align is :top, :center or :bottom"))
+
+;; Default implementation of Anchored for any Layer.
+(extend-protocol Anchored
+  indyvon.core.Layer
+  (anchor [this context h-align v-align]
+          (if (and (= h-align :left)
+                   (= v-align :top))
+            [0 0]
+            (let [size (size this context)]
+              [(case h-align
+                 :top 0
+                 :center (/ (size 0) 2)
+                 :right (size 0))
+               (case v-align
+                 :left 0
+                 :center (/ (size 1) 2)
+                 :bottom (size 1))]))))
+
 (defrecord Theme [fore-color back-color border-color font])
 
 (defn default-theme []
@@ -81,134 +106,151 @@
           (.dispose graphics))))))
 
 ;;
-;; Layer implementations.
+;; EventDispatcher implementation
 ;;
 
-(defn border-layer
-  "Decorate layer with a border."
-  ([content]
-     (border-layer content 1))
-  ([content width]
-     (border-layer content width 0))
-  ([content width gap]
-     (let [offset (+ width gap)]
-       (reify Layer
-        (render! [l c g]
-           (let [w (:width c)
-                 h (:height c)]
-             (.setColor g (-> c :theme :border-color))
-             (doseq [i (range 0 width)]
-               (.drawRect g i i (- w 1 i i) (- h 1 i i)))
-             (draw! content c g offset offset (- w offset offset)
-                    (- h offset offset))))
-        (size [l c]
-           (let [s (size content c)]
-             [(+ (s 0) offset offset)
-              (+ (s 1) offset offset)]))))))
+(def awt-events
+     {java.awt.event.MouseEvent/MOUSE_CLICKED  :mouse-clicked
+      java.awt.event.MouseEvent/MOUSE_DRAGGED  :mouse-dragged
+      java.awt.event.MouseEvent/MOUSE_ENTERED  :mouse-entered
+      java.awt.event.MouseEvent/MOUSE_EXITED   :mouse-exited
+      java.awt.event.MouseEvent/MOUSE_MOVED    :mouse-moved
+      java.awt.event.MouseEvent/MOUSE_PRESSED  :mouse-pressed
+      java.awt.event.MouseEvent/MOUSE_RELEASED :mouse-released})
+
+(defn- registered-parent
+  "Returns first context parent registered for event processing."
+  [context-tree context]
+  (let [parent (:parent context)]
+    (cond
+     (nil? parent) nil
+     (contains? context-tree parent) parent
+     :default (recur context-tree parent))))
+
+(defn- add-context
+  [context-tree context]
+  (let [parent (registered-parent context-tree context)]
+    (assoc context-tree parent (cons context (context-tree parent))
+           context nil)))
 
-;; Define as macro to avoid unnecessary calculation of inner and outer
-;; sizes in the first case.
-(defmacro align-xy [inner outer align first center last]
-  `(case ~align
-         ~first 0
-         ~center (/ (- ~outer ~inner) 2)
-         ~last (- ~outer ~inner)))
+(defn- inside?
+  ([x y context]
+     (inside? x y (:x context) (:y context)
+              (:width context) (:height context)))
+  ([px py x y w h]
+     (and (>= px x)
+          (>= py y)
+          (< px (+ x w))
+          (< py (+ y h)))))
 
-(defmacro align-x [inner outer align]
-  `(align-xy ~inner ~outer ~align :left :center :right))
+(defn- under-cursor
+  "Returns a sequence of contexts under cursor."
+  ([context-tree x y]
+     (under-cursor context-tree x y nil))
+  ([context-tree x y context]
+     (some #(if (inside? x y %)
+              (conj (under-cursor context-tree x y %) %))
+           (context-tree context))))
 
-(defmacro align-y [inner outer align]
-  `(align-xy ~inner ~outer ~align :top :center :bottom))
-
-(defn- re-split [re s]
-  (seq (.split re s)))
+(defn- remove-all [coll1 coll2 pred]
+  (filter #(not (some (partial pred %) coll2)) coll1))
 
-(defn- layout-text [lines font font-context]
-  (map #(TextLayout. % font font-context) lines))
+;; (defn- translate-mouse-event
+;;   [event x y id]
+;;   (proxy [MouseEvent] [(.getComponent event)
+;;                        id
+;;                        (.getWhen event)
+;;                        (.getModifiers event)
+;;                        (- (.getX event) x)
+;;                        (- (.getY event) y)
+;;                        (.getClickCount event)
+;;                        (.isPopupTrigger event)]
+;;     (getXOnScreen [] (.getXOnScreen event))
+;;     (getYOnScreen [] (.getYOnScreen event))))
 
-(defn- text-width [layouts]
-  (reduce #(max %1 (.getAdvance %2)) 0 layouts))
-
-(defn- text-height [layouts]
-  (reduce #(+ %1 (.getAscent %2) (.getDescent %2) (.getLeading %2))
-          0 layouts))
+(defn- translate-mouse-event
+  [event x y id]
+  (MouseEvent. id (.getWhen event)
+               (- (.getX event) x) (- (.getY event) y)
+               (.getXOnScreen event) (.getYOnScreen event)
+               (.getButton event)))
 
-(defn text-layer
-  "Creates a layer to display multiline text."
-  ([text]
-     (text-layer text :left :top))
-  ([text h-align v-align]
-     (let [lines (re-split #"\r\n|\n|\r|\u0085|\u2028|\u2029" text)]
-       (reify Layer
-        (render! [l c g]
-           (let [w (:width c)
-                 h (:height c)
-                 font (.getFont g)
-                 font-context (:font-context c)
-                 layouts (layout-text lines font font-context)
-                 y (align-y (text-height layouts) h v-align)]
-             (loop [layouts layouts, y y]
-               (when-first [layout layouts]
-                 (let [ascent (.getAscent layout)
-                       lh (+ ascent (.getDescent layout) (.getLeading layout))
-                       x (align-x (.getAdvance layout) w h-align)]
-                   (.draw layout g x (+ y ascent))
-                   (recur (next layouts) (+ y lh)))))))
-        (size [l c]
-           (let [layouts (layout-text lines
-                                      (-> c :theme :font)
-                                      (:font-context c))
-                 width (text-width layouts)
-                 height (text-height layouts)]
-             [width height]))))))
+(defn- translate-and-dispatch
+  ([contexts event]
+     (translate-and-dispatch contexts event (awt-events (.getID event))))
+  ([contexts event id]
+     (doseq [context contexts]
+       (handle-mouse
+        (:layer context) context 
+        (translate-mouse-event event (:x context) (:y context) id)))
+     id))
 
-(defprotocol Anchored
-  "Provide anchor point for Layers. Used by viewport."
-  (anchor [this context] "Anchor point: [x y]"))
+(defn- dispatch-mouse-motion*
+  "Dispatches mouse motion events. Returns a new set of contexts which
+  currently are under cursor."
+  [hovered context-tree event]
+  (let [x (.getX event)
+        y (.getY event)
+        hovered2 (under-cursor context-tree x y)
+        pred #(= (:layer %1) (:layer %2))
+        exited (remove-all hovered hovered2 pred)
+        entered (remove-all hovered2 hovered pred)
+        moved (remove-all hovered2 entered pred)]
+    (translate-and-dispatch exited event :mouse-exited)
+    (translate-and-dispatch entered event :mouse-entered)
+    (translate-and-dispatch moved event :mouse-moved)
+    hovered2))
 
-;; Default implementation of Anchored for any Layer.
-(extend-protocol Anchored
-  indyvon.core.Layer
-  (anchor [this context] [0 0]))
+(defn- dispatch-mouse-motion
+  [hovered-ref context-tree event]
+  (dosync
+   (alter hovered-ref dispatch-mouse-motion* context-tree event)))
+
+(defn- dispatch-mouse-button*
+  "Dispatches mouse button events. Returns a new set of contexts which
+  currently are picked with a pressed button."
+  [picked hovered event]
+  (if (= (translate-and-dispatch hovered event) :mouse-pressed)
+    hovered
+    nil))
 
-(defn viewport
-  "Creates scrollable viewport layer."
-  ([content] (viewport content :left :top))
-  ([content h-align v-align]
-  (let [x (ref 0)
-        y (ref 0)
-        fix-x (ref 0)
-        fix-y (ref 0)
-        last-width (ref 0)
-        last-height (ref 0)]
+(defn- dispatch-mouse-button
+  [picked-ref hovered-ref event]
+  (dosync
+   (alter picked-ref dispatch-mouse-button* @hovered-ref event)))
+
+(defn make-event-dispatcher []
+  (let [context-tree-r (ref {}) ; register
+        context-tree (ref {})   ; dispatch
+        hovered (ref '())
+        picked (ref '())]
     (reify
-     Layer
-     (render! [layer c g]
-        (let [anchor (anchor content c)
-              width (:width c)
-              height (:height c)]
-          (dosync
-           (alter x + (align-x width @last-width h-align))
-           (alter y + (align-y height @last-height v-align))
-           (ref-set last-width width)
-           (ref-set last-height height))
-          (draw! content c g
-                 (- 0 @x (anchor 0))
-                 (- 0 @y (anchor 1)))))
-     (size [layer c] (size content c))
-     MouseHandler
-     (handle-mouse [layer c e]
-        (when (= (.getID e) MouseEvent/MOUSE_PRESSED)
-          (dosync
-           (ref-set fix-x (.getXOnScreen e))
-           (ref-set fix-y (.getYOnScreen e)))
-          (->> Cursor/MOVE_CURSOR Cursor. (.setCursor (:target c))))
-        (when (= (.getID e) MouseEvent/MOUSE_RELEASED)
-          (->> Cursor/DEFAULT_CURSOR Cursor. (.setCursor (:target c))))
-        (when (= (.getID e) MouseEvent/MOUSE_DRAGGED)
-          (dosync
-           (alter x + (- @fix-x (.getXOnScreen e)))
-           (alter y + (- @fix-y (.getYOnScreen e)))
-           (ref-set fix-x (.getXOnScreen e))
-           (ref-set fix-y (.getYOnScreen e)))
-          (update c)))))))
+     EventDispatcher
+     (listen! [this component]
+        (doto component
+          (.addMouseListener this)
+          (.addMouseMotionListener this)))
+     (register [this context]
+        (when (satisfies? MouseHandler (:layer context))
+          (dosync (alter context-tree-r add-context context))))
+     (commit [this]
+        (dosync (ref-set context-tree @context-tree-r)
+                (ref-set context-tree-r {})))
+     (picked? [this layer] false)
+     (hovered? [this layer] false)
+     MouseListener
+     (mouseEntered [this event]
+        (dispatch-mouse-motion hovered @context-tree event))
+     (mouseExited [this event]
+        (dispatch-mouse-motion hovered @context-tree event))
+     (mouseClicked [this event]
+        (dispatch-mouse-button picked hovered event))
+     (mousePressed [this event]
+        (dispatch-mouse-button picked hovered event))
+     (mouseReleased [this event]
+        (dispatch-mouse-button picked hovered event))
+     MouseMotionListener
+     (mouseDragged [this event]
+        (translate-and-dispatch @picked event))
+     (mouseMoved [this event]
+        (dispatch-mouse-motion hovered @context-tree event)))))
--- a/src/indyvon/event.clj	Mon Jun 21 01:18:50 2010 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-;;
-;; Copyright (C) 2010 Mikhail Kryshen <mikhail@kryshen.net>
-;;
-;; This file is part of Indyvon.
-;;
-
-(ns indyvon.event
-  (:use indyvon.core)
-  (:import (java.awt.event MouseEvent MouseListener MouseMotionListener)
-           java.lang.ref.WeakReference))
-
-(defn- registered-parent
-  "Returns first context parent registered for event processing."
-  [context-tree context]
-  (let [parent (:parent context)]
-    (cond
-     (nil? parent) nil
-     (contains? context-tree parent) parent
-     :default (recur context-tree parent))))
-
-(defn- add-context
-  [context-tree context]
-  (let [parent (registered-parent context-tree context)]
-    (assoc context-tree parent (cons context (context-tree parent))
-           context nil)))
-
-(defn- inside?
-  ([x y context]
-     (inside? x y (:x context) (:y context)
-              (:width context) (:height context)))
-  ([px py x y w h]
-     (and (>= px x)
-          (>= py y)
-          (< px (+ x w))
-          (< py (+ y h)))))
-
-(defn- under-cursor
-  "Returns a sequence of contexts under cursor."
-  ([context-tree x y]
-     (under-cursor context-tree x y nil))
-  ([context-tree x y context]
-     (some #(if (inside? x y %)
-              (conj (under-cursor context-tree x y %) %))
-           (context-tree context))))
-
-(defn- remove-all [coll1 coll2 pred]
-  (filter #(not (some (partial pred %) coll2)) coll1))
-
-(defn- translate-mouse-event
-  [event x y id]
-  (proxy [MouseEvent] [(.getComponent event)
-                       id
-                       (.getWhen event)
-                       (.getModifiers event)
-                       (- (.getX event) x)
-                       (- (.getY event) y)
-                       (.getClickCount event)
-                       (.isPopupTrigger event)]
-    (getXOnScreen [] (.getXOnScreen event))
-    (getYOnScreen [] (.getYOnScreen event))))
-
-(defn- translate-and-dispatch
-  ([contexts event]
-     (translate-and-dispatch contexts event (.getID event)))
-  ([contexts event id]
-     (doseq [context contexts]
-       (handle-mouse
-        (:layer context) context 
-        (translate-mouse-event event (:x context) (:y context) id)))))
-
-(defn- dispatch-mouse-motion*
-  "Dispatches mouse motion events. Returns a new set of contexts which
-  currently are under cursor."
-  [hovered context-tree event]
-  (let [x (.getX event)
-        y (.getY event)
-        hovered2 (under-cursor context-tree x y)
-        pred #(= (:layer %1) (:layer %2))
-        exited (remove-all hovered hovered2 pred)
-        entered (remove-all hovered2 hovered pred)
-        moved (remove-all hovered2 entered pred)]
-    (translate-and-dispatch exited event MouseEvent/MOUSE_EXITED)
-    (translate-and-dispatch entered event MouseEvent/MOUSE_ENTERED)
-    (translate-and-dispatch moved event MouseEvent/MOUSE_MOVED)
-    hovered2))
-
-(defn- dispatch-mouse-motion
-  [hovered-ref context-tree event]
-  (dosync
-   (alter hovered-ref dispatch-mouse-motion* context-tree event)))
-
-(defn- dispatch-mouse-button*
-  "Dispatches mouse button events. Returns a new set of contexts which
-  currently are picked with a pressed button."
-  [picked hovered event]
-  (translate-and-dispatch hovered event)
-  (if (= (.getID event) MouseEvent/MOUSE_PRESSED)
-    hovered
-    nil))
-
-(defn- dispatch-mouse-button
-  [picked-ref hovered-ref event]
-  (dosync
-   (alter picked-ref dispatch-mouse-button* @hovered-ref event)))
-
-(defn make-event-dispatcher []
-  (let [context-tree-r (ref {}) ; register
-        context-tree (ref {})   ; dispatch
-        hovered (ref '())
-        picked (ref '())]
-    (reify
-     EventDispatcher
-     (listen! [this component]
-        (doto component
-          (.addMouseListener this)
-          (.addMouseMotionListener this)))
-     (register [this context]
-        (when (satisfies? MouseHandler (:layer context))
-          (dosync (alter context-tree-r add-context context))))
-     (commit [this]
-        (dosync (ref-set context-tree @context-tree-r)
-                (ref-set context-tree-r {})))
-     (picked? [this layer] false)
-     (hovered? [this layer] false)
-     MouseListener
-     (mouseEntered [this event]
-        (dispatch-mouse-motion hovered @context-tree event))
-     (mouseExited [this event]
-        (dispatch-mouse-motion hovered @context-tree event))
-     (mouseClicked [this event]
-        (dispatch-mouse-button picked hovered event))
-     (mousePressed [this event]
-        (dispatch-mouse-button picked hovered event))
-     (mouseReleased [this event]
-        (dispatch-mouse-button picked hovered event))
-     MouseMotionListener
-     (mouseDragged [this event]
-        (translate-and-dispatch @picked event))
-     (mouseMoved [this event]
-        (dispatch-mouse-motion hovered @context-tree event)))))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/indyvon/layers.clj	Mon Jun 21 04:00:45 2010 +0400
@@ -0,0 +1,134 @@
+;;
+;; Copyright (C) 2010 Mikhail Kryshen <mikhail@kryshen.net>
+;;
+;; This file is part of Indyvon.
+;;
+
+(ns indyvon.layers
+  (:use indyvon.core)
+  (:import (java.awt Cursor)
+           (java.awt.font FontRenderContext TextLayout)))
+
+;; Define as macro to avoid unnecessary calculation of inner and outer
+;; sizes in the first case.
+(defmacro align-xy [inner outer align first center last]
+  `(case ~align
+         ~first 0
+         ~center (/ (- ~outer ~inner) 2)
+         ~last (- ~outer ~inner)))
+
+(defmacro align-x [inner outer align]
+  `(align-xy ~inner ~outer ~align :left :center :right))
+
+(defmacro align-y [inner outer align]
+  `(align-xy ~inner ~outer ~align :top :center :bottom))
+
+(defn border-layer
+  "Decorate layer with a border."
+  ([content]
+     (border-layer content 1))
+  ([content width]
+     (border-layer content width 0))
+  ([content width gap]
+     (let [offset (+ width gap)]
+       (reify Layer
+        (render! [l c g]
+           (let [w (:width c)
+                 h (:height c)]
+             (.setColor g (-> c :theme :border-color))
+             (doseq [i (range 0 width)]
+               (.drawRect g i i (- w 1 i i) (- h 1 i i)))
+             (draw! content c g offset offset (- w offset offset)
+                    (- h offset offset))))
+        (size [l c]
+           (let [s (size content c)]
+             [(+ (s 0) offset offset)
+              (+ (s 1) offset offset)]))))))
+
+(defn- re-split [re s]
+  (seq (.split re s)))
+
+(defn- layout-text [lines font font-context]
+  (map #(TextLayout. % font font-context) lines))
+
+(defn- text-width [layouts]
+  (reduce #(max %1 (.getAdvance %2)) 0 layouts))
+
+(defn- text-height [layouts]
+  (reduce #(+ %1 (.getAscent %2) (.getDescent %2) (.getLeading %2))
+          0 layouts))
+
+(defn text-layer
+  "Creates a layer to display multiline text."
+  ([text]
+     (text-layer text :left :top))
+  ([text h-align v-align]
+     (let [lines (re-split #"\r\n|\n|\r|\u0085|\u2028|\u2029" text)]
+       (reify Layer
+        (render! [l c g]
+           (let [w (:width c)
+                 h (:height c)
+                 font (.getFont g)
+                 font-context (:font-context c)
+                 layouts (layout-text lines font font-context)
+                 y (align-y (text-height layouts) h v-align)]
+             (loop [layouts layouts, y y]
+               (when-first [layout layouts]
+                 (let [ascent (.getAscent layout)
+                       lh (+ ascent (.getDescent layout) (.getLeading layout))
+                       x (align-x (.getAdvance layout) w h-align)]
+                   (.draw layout g x (+ y ascent))
+                   (recur (next layouts) (+ y lh)))))))
+        (size [l c]
+           (let [layouts (layout-text lines
+                                      (-> c :theme :font)
+                                      (:font-context c))
+                 width (text-width layouts)
+                 height (text-height layouts)]
+             [width height]))))))
+
+(defn viewport
+  "Creates scrollable viewport layer."
+  ([content] (viewport content :left :top))
+  ([content h-align v-align]
+  (let [x (ref 0)
+        y (ref 0)
+        fix-x (ref 0)
+        fix-y (ref 0)
+        last-width (ref 0)
+        last-height (ref 0)]
+    (reify
+     Layer
+     (render! [layer c g]
+        (let [anchor (anchor content c h-align v-align)
+              width (:width c)
+              height (:height c)]
+          (dosync
+           (alter x + (align-x width @last-width h-align))
+           (alter y + (align-y height @last-height v-align))
+           (ref-set last-width width)
+           (ref-set last-height height))
+          (draw! content c g
+                 (- 0 @x (anchor 0))
+                 (- 0 @y (anchor 1)))))
+     (size [layer c] (size content c))
+     MouseHandler
+     (handle-mouse [layer c e]
+       (case (:id e)
+         :mouse-pressed
+         (do
+           (dosync
+            (ref-set fix-x (:x-on-screen e))
+            (ref-set fix-y (:y-on-screen e)))
+           (->> Cursor/MOVE_CURSOR Cursor. (.setCursor (:target c))))
+         :mouse-released
+         (->> Cursor/DEFAULT_CURSOR Cursor. (.setCursor (:target c)))
+         :mouse-dragged
+         (do
+           (dosync
+            (alter x + (- @fix-x (:x-on-screen e)))
+            (alter y + (- @fix-y (:y-on-screen e)))
+            (ref-set fix-x (:x-on-screen e))
+            (ref-set fix-y (:y-on-screen e)))
+           (update c))
+         nil))))))