(ns arabic-flashcards
(:use [clojure.contrib.duck-streams :only (slurp* spit)]
[clojure.contrib.fcase :only (case)]
[clojure.contrib.seq-utils :only (shuffle)]
[clojure.contrib.str-utils :only (re-split)])
(:import (java.awt BorderLayout Font GridBagConstraints GridBagLayout Insets)
(java.awt.event ActionListener KeyListener)
(javax.swing JButton JFileChooser JFrame JLabel JOptionPane
JScrollPane JTextArea)))
(def global-font (Font. "SansSerif" Font/PLAIN 18))
(def definition-font (Font. "SansSerif" Font/ITALIC 16))
(def solution-font (Font. "Scheherazade" Font/PLAIN 36))
(def window-width 400)
(def default-close JFrame/EXIT_ON_CLOSE)
(def words (ref nil))
(def current (ref nil))
(defn generate-words [str]
(filter identity
(for [line (re-split #"\n" str)]
(let [split (re-split #" = " line)]
(and (= (count split) 2) split)))))
(defn remove-current-word []
(let [new-words (concat (take @current @words) (drop (inc @current) @words))]
(dosync (ref-set words new-words)
(alter current dec)))
(empty? @words))
(defn show-answer []
[nil (second (nth @words @current))])
(defn initialize-words []
(dosync (alter words shuffle)
(ref-set current 0))
[(first (first @words)) "?"])
(defn next-word []
(dosync (alter current inc))
(if (>= @current (count @words))
(initialize-words)
[(first (nth @words @current)) "?"]))
(def flashcards nil)
(defn start-the-questions []
(let [frame (JFrame.)
remaining (JLabel.)
definition (JLabel. "" JLabel/CENTER)
solution (JLabel. "" JLabel/CENTER)
update-fields (fn [[def sol]]
(.setText remaining (format "Remaining: %d word(s)."
(count @words)))
(when def (.setText definition def))
(when sol (.setText solution sol)))
state (ref 'question)
listener (proxy [KeyListener] []
(keyPressed [e]
(case (.getKeyChar e)
\n (when (= @state 'answer)
(dosync (ref-set state 'question))
(update-fields (next-word)))
\r (when (= @state 'answer)
(dosync (ref-set state 'question))
(if (remove-current-word)
(do (.dispose frame) (flashcards))
(update-fields (next-word))))
\space (when (= @state 'question)
(dosync (ref-set state 'answer))
(update-fields (show-answer)))
\q (do (.dispose frame) (flashcards))))
(keyReleased [e])
(keyTyped [e]))]
(.setFont definition definition-font)
(.setFont solution solution-font)
(update-fields (initialize-words))
(doto frame
(.setTitle "Arabic Flashcards")
(.addKeyListener listener)
(.setDefaultCloseOperation default-close)
(.setLayout (BorderLayout.))
(.add remaining BorderLayout/PAGE_START)
(.add definition BorderLayout/CENTER)
(.add solution BorderLayout/PAGE_END)
(.pack)
(.setSize window-width (.getHeight frame))
(.setLocationRelativeTo nil)
(.setVisible true))))
(defn constraints [x y n]
(GridBagConstraints. x y n 1 1 1 GridBagConstraints/CENTER
GridBagConstraints/HORIZONTAL (Insets. 0 0 0 0) 0 0))
(defn no-overwrite? [frame]
(= (JOptionPane/showConfirmDialog
frame "Overwrite existing file?" "Confirm Overwrite"
JOptionPane/OK_CANCEL_OPTION JOptionPane/QUESTION_MESSAGE)
JOptionPane/CANCEL_OPTION))
(defn flashcards []
(let [frame (JFrame.)
text (JTextArea. 20 30)
load (JButton. "Load")
save (JButton. "Save")
start (JButton. "Start")
listener (proxy [ActionListener] []
(actionPerformed [e]
(case (.getSource e)
start (let [str (generate-words (.getText text))]
(dosync (ref-set words str))
(.dispose frame)
(start-the-questions))
load (let [chooser (JFileChooser.)]
(when (= (.showOpenDialog chooser frame)
JFileChooser/APPROVE_OPTION)
(let [file (.getSelectedFile chooser)]
(.setText text (slurp* file)))))
save (let [chooser (JFileChooser.)]
(when (= (.showSaveDialog chooser frame)
JFileChooser/APPROVE_OPTION)
(let [file (.getSelectedFile chooser)]
(when-not (and (.exists file)
(no-overwrite? frame))
(spit file (.getText text)))))))))]
(doto text
(.setFont global-font)
(.setText (apply str
(reduce concat
(map #(format "%s = %s\n" (first %) (second %))
@words)))))
(.addActionListener load listener)
(.addActionListener save listener)
(.addActionListener start listener)
(doto frame
(.setTitle "Dictionary Setup")
(.setDefaultCloseOperation default-close)
(.setLayout (GridBagLayout.))
(.add (JScrollPane. text) (constraints 0 0 2))
(.add load (constraints 0 1 1))
(.add save (constraints 1 1 1))
(.add start (constraints 0 2 2))
(.pack)
(.setLocationRelativeTo nil)
(.setVisible true))))
(defn load-dictionary [filename]
(let [str (generate-words (slurp filename))]
(dosync (ref-set words str)))
(start-the-questions))
(if (= (count *command-line-args*) 1)
(load-dictionary (first *command-line-args*))
(flashcards))