                               (label "# beats:") cbb])))
 
 (defn make-melody-panel [id]
-  (let [pan (xyz-panel :id id 
+  (let [pan (xyz-panel :id :kb
                        :size panel-size
                        :items []
                        )
         controls (make-panel-controls id)
         playsolo (button :text "Play Solo"
                          :listen [:action #(play-solo id %)])
-        verticalpanel (vertical-panel :items [pan playsolo controls])
         f (frame :title (s/join ["Tune " (str id)])
                  :id (s/join ["frame-" (str id)])
                  :listen [:window-closing
                           (fn [e]
                             (swap! melody-map dissoc id))])
+        verticalpanel (vertical-panel :items [pan playsolo controls])
         numcbs 1]
-    (swap! melody-map assoc id {:pitches numcbs, :beats numcbs, :root pan})
-    (add-note-circles id)
+    (swap! melody-map assoc id {:pitches numcbs, :beats numcbs, :root f})
+    (add-note-circles id pan)
     (config! f :content verticalpanel)
     (-> f pack! show!)
-    pan))  
+    f))
+
+(defn make-circles-hash [id]
+  (let [root (get-in @melody-map [id :root])
+        panel (select root [:#kb])
+        melprefix (s/join ["#kb-" id "-mel-"])
+        rhyprefix (s/join ["#kb-" id "-rhy-"])
+        mellen (get-in @melody-map [id :pitches])
+        rhylen (get-in @melody-map [id :beats])
+        mellist (map #(keyword (s/join [melprefix (str %)])) (range mellen))
+        rhylist (map #(keyword (s/join [rhyprefix (str %)])) (range rhylen))
+        idlist (vec (concat mellist rhylist))
+        circhash (atom {})]
+    (doseq [k idlist]
+      (let [w (select panel [k])]
+        (swap! circhash assoc k (value w))))
+    @circhash))
+
+(defn replace-in-key [key old new]
+  (let [oldkeystr (str key)
+        newkeystr (s/replace oldkeystr old new)]
+    (keyword (subs newkeystr 1))))
+        
+(defn restore-circles [id oldid panel circhash]
+  (let [oldstr (s/join ["-" (str oldid) "-"])
+        newstr (s/join ["-" (str id) "-"])]
+    (doseq [[k v] circhash]
+      (let [newk (replace-in-key k oldstr newstr)]
+        (value! (select panel [newk]) v)))))
+
+(defn restore-panel [oldid panhash circhash]
+  (let [newroot (new-panel nil)
+        id @counter
+        panel (select newroot [:#kb])
+        newpanhash (assoc panhash :root newroot)
+        numpitbut (select newroot [(keyword (s/join ["#kb-" (str id) "-numpitch"]))])
+        numbeabut (select newroot [(keyword (s/join ["#kb-" (str id) "-numbeats"]))])
+        ]
+    (value! numpitbut (get panhash :pitches))
+    (value! numbeabut (get panhash :beats))
+    (restore-circles id oldid panel circhash)
+    (swap! melody-map assoc id newpanhash)))
+
+(defn make-dump []
+  (let [dump (atom {})]
+    (doseq [[k v] @melody-map]
+      (let [val {:panhash (dissoc v :root),
+                 :circhash (make-circles-hash k)}]
+        (swap! dump assoc k val)))
+    @dump))
+        
+(defn restore-dump [dump]
+  (doseq [[oldid {:keys [panhash circhash]}] dump]
+    (restore-panel oldid panhash circhash)))
+
+(defn save-pattern [e]
+  (let [dump (make-dump)
+        savefile (choose-file :type :save)]
+    (if savefile
+      (spit savefile dump))))
+
+(defn load-pattern [e]
+  (let [loadfile (choose-file :type :open)]
+    (if loadfile
+      (let [dumpstr (slurp loadfile)
+            dump (clojure.edn/read-string dumpstr)]
+        (restore-dump dump)))))
 
 (def control-frame
   (frame :title "Tuning Demo"

        seesaw.graphics
        ;seesaw.color
        ;seesaw.keystroke
        ;seesaw.chooser
        clojure.math.numeric-tower)
  (:require [overtone.core :as o]
            [clojure.string :as s])

                           (button :id :newPanel
                                   :text "New Panel"
                                   :listen [:action new-panel])
                           (button :id :saveButt
                                   :text "Save"
                                   :listen [:action save-pattern])
                           (button :id :loadButt
                                   :text "Load"
                                   :listen [:action load-pattern])
                           ;; spacer
                           (label :text ""
                                  :size [100 :by 30])
