(ns chess
(:import (java.awt BorderLayout Color Dimension Font Toolkit)
(java.awt.event ActionListener KeyAdapter KeyEvent WindowAdapter
WindowListener)
(javax.swing BoxLayout JButton JDialog JFrame JLabel JPanel JSpinner
SpinnerNumberModel Timer WindowConstants)))
(def left-key KeyEvent/VK_CONTROL)
(def right-key KeyEvent/VK_ENTER)
(def left (ref 3600))
(def right (ref 3600))
(def original-left (ref 3600))
(def original-right (ref 3600))
(def current (ref left))
(when ((ns-refers *ns*) 'case) (ns-unmap *ns* 'case))
(defmacro case [value & pairs]
(let [val (gensym "val")]
`(let [~val ~value]
(cond ~@(mapcat (fn [[key action]] `((= ~val ~key) ~action))
(partition 2 pairs))))))
(defn paint-bar [g [x y] over?]
(.setColor g (if over? Color/red Color/green))
(.fillRect g x y 300 60))
(defn paint-clock [g [x y] seconds]
(let [x0 (+ x 150)
y0 (+ y 150)
minutes (/ seconds 60)
cx (fn [rad len] (+ x0 (* (Math/cos rad) len)))
cy (fn [rad len] (+ y0 (* (Math/sin rad) len)))]
(.setColor g Color/white)
(.fillArc g x y 300 300 0 360)
(.setColor g Color/gray)
(doseq [deg (range 0 360 6)]
(let [rad (/ (* deg Math/PI) 180)
len (if (zero? (mod deg 30)) 120 130)]
(.drawLine g (cx rad 140) (cy rad 140) (cx rad len) (cy rad len))))
(.setColor g Color/black)
(.drawArc g x y 300 300 0 360)
(let [rad (/ (* (mod (- 45 seconds) 60) 6 Math/PI) 180)]
(.drawLine g x0 y0 (cx rad 140) (cy rad 140)))
(let [rad (/ (* (mod (- 45 minutes) 60) 6 Math/PI) 180)]
(.drawLine g x0 y0 (cx rad 100) (cy rad 100)))))
(defn paint-progress [g [x y] percentage]
(.setColor g (cond (> percentage 0.5) Color/green
(> percentage 0.1) Color/yellow
true Color/red))
(.fillRect g x y (* 300 percentage) 30)
(.setColor g Color/black)
(.drawRect g x y 300 30))
(defn paint-digital [g [x y] seconds]
(.setColor g Color/black)
(.setFont g (Font. "Courier" Font/BOLD 100))
(.drawString g (format "%02d:%02d" (int (/ seconds 60)) (mod seconds 60))
x (+ y 80)))
(defn paint-clocks [g]
(if (= @current left)
(paint-bar g [20 20] (<= @left 0))
(paint-bar g [400 20] (<= @right 0)))
(paint-clock g [20 100] @left)
(paint-clock g [400 100] @right)
(let [full (max @original-left @original-right)]
(paint-progress g [20 420] (/ @left full))
(paint-progress g [400 420] (/ @right full)))
(paint-digital g [20 450] @left)
(paint-digital g [400 450] @right))
(defn set-times [parent]
(let [frame (JDialog. parent "Setup")
panel (JPanel.)
labels (JPanel.)
edits (JPanel.)
left-time (JSpinner. (SpinnerNumberModel.
(int (/ @original-left 60)) 0 1000 1))
right-time (JSpinner. (SpinnerNumberModel.
(int (/ @original-right 60)) 0 1000 1))
button (JButton. "OK")
listener (proxy [ActionListener] []
(actionPerformed [e]
(dosync
(ref-set original-left (* (.getValue left-time) 60))
(ref-set original-right (* (.getValue right-time) 60))
(ref-set left @original-left)
(ref-set right @original-right))
(.repaint parent)
(.dispose frame)))]
(.addActionListener button listener)
(doto labels
(.setLayout (BoxLayout. labels BoxLayout/Y_AXIS))
(.add (JLabel. "Left [min]:"))
(.add (JLabel. "Right [min]:")))
(doto edits
(.setLayout (BoxLayout. edits BoxLayout/Y_AXIS))
(.add left-time)
(.add right-time))
(doto panel
(.setLayout (BorderLayout.))
(.add labels BorderLayout/WEST)
(.add edits BorderLayout/EAST)
(.add button BorderLayout/SOUTH))
(doto frame
(.add panel)
(.pack)
(.setLocationRelativeTo parent)
(.setVisible true))))
(defn clock []
(let [timer (Timer. 1000 nil)
frame (JFrame. "Chess Clock")
panel (proxy [JPanel ActionListener] []
(paintComponent [g]
(proxy-super paintComponent g)
(paint-clocks g))
(getPreferredSize []
(Dimension. 720 550))
(actionPerformed [e]
(dosync (alter @current dec))
(.repaint this)
(when (or (<= @left 0) (<= @right 0))
(.stop timer)
(.beep (Toolkit/getDefaultToolkit)))))
key-listener (proxy [KeyAdapter] []
(keyPressed [key]
(case (.getKeyCode key)
KeyEvent/VK_P (if (.isRunning timer)
(.stop timer)
(.start timer))
left-key (when (= @current left)
(dosync (ref-set current right)))
right-key (when (= @current right)
(dosync (ref-set current left)))
KeyEvent/VK_S (do (.stop timer)
(set-times frame))
KeyEvent/VK_R (dosync
(ref-set left @original-left)
(ref-set right @original-right)))
(.repaint panel)))
window-listener (proxy [WindowAdapter] []
(windowClosing [e]
(.stop timer)
(.dispose frame)))]
(.addActionListener timer panel)
(doto panel
(.setBackground Color/gray)
(.setFocusable true)
(.addKeyListener key-listener))
(doto frame
(.add panel)
(.setDefaultCloseOperation WindowConstants/DO_NOTHING_ON_CLOSE)
(.addWindowListener window-listener)
(.pack)
(.setVisible true))))