Skip to content

Commit

Permalink
Add ability to save and load files. Release 0.1.1.
Browse files Browse the repository at this point in the history
  • Loading branch information
brunchboy committed May 20, 2016
1 parent a99da4e commit 9a7deeb
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 22 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ This change log follows the conventions of

## [Unreleased][unreleased]

Nothing so far.

## [0.1.1] - 2016-05-20

### Added

- A status summary of the selected player is shown after the menu.
Expand All @@ -17,6 +21,9 @@ This change log follows the conventions of
- Support for offline operation when no DJ Link device can be found.
- The list of triggers is saved when the application exits and
restored when it starts.
- You can save or load the configuration to a text file of your choice.
- Triggers can be deleted by right-clicking on them as long as there
is more than one in the list.
- A first-stage loading process which checks the Java version and
presents an error dialog if it is too old to successfully load the
rest of the application, offering to open the download page.
Expand Down Expand Up @@ -48,4 +55,5 @@ This change log follows the conventions of
- Set up initial project structure.
- Selector to choose MIDI output as trigger destination.

[unreleased]: https://github.com/brunchboy/beat-link/compare/v0.1.0...HEAD
[unreleased]: https://github.com/brunchboy/beat-link-trigger/compare/v0.1.1...HEAD
[0.1.1]: https://github.com/brunchboy/beat-link-trigger/compare/v0.1.0...v0.1.1
3 changes: 2 additions & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
(defproject beat-link-trigger "0.1.1-SNAPSHOT"
(defproject beat-link-trigger "0.1.1"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.8.0"]
[environ "1.0.3"]
[fipp "0.6.5"]
[org.deepsymmetry/beat-link "0.1.4"]
[overtone/midi-clj "0.5.0"]
[overtone/osc-clj "0.9.0"]
Expand Down
81 changes: 61 additions & 20 deletions src/beat_link_trigger/core.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(ns beat-link-trigger.core
"Send MIDI or OSC events when a CDJ starts playing."
(:require [overtone.midi :as midi]
[seesaw.cells]
[seesaw.chooser :as chooser]
[seesaw.core :as seesaw]
[seesaw.mig :as mig]
[beat-link-trigger.about :as about]
Expand Down Expand Up @@ -179,6 +179,19 @@
(when-let [frame @trigger-frame]
(seesaw/config (seesaw/select frame [:#triggers]) :items)))

(defn- adjust-to-new-trigger
"Called when a trigger is added or removed to restore the proper
alternation of background colors, expand the window if it still fits
the screen, and update any other user interface elements that might
be affected."
[]
(doall (map (fn [trigger color]
(seesaw/config! trigger :background color))
(get-triggers) (cycle ["#eee" "#ddd"])))
(when (< 100 (- (.height (.getBounds (.getGraphicsConfiguration @trigger-frame)))
(.height (.getBounds @trigger-frame))))
(.pack @trigger-frame)))

(defn- create-trigger-row
"Create a row for watching a player in the trigger window. If `m` is
supplied, it is a map containing values to recreate the row from a
Expand Down Expand Up @@ -209,7 +222,15 @@

[(seesaw/label :id :enabled-label :text "Enabled:") "gap unrelated"]
[(seesaw/checkbox :id :enabled) "wrap"]]
:user-data (atom {:playing false}))]

:user-data (atom {:playing false}))
delete-action (seesaw/action :handler (fn [e]
(seesaw/config! (seesaw/select @trigger-frame [:#triggers])
:items (remove #(= % panel) (get-triggers)))
(adjust-to-new-trigger)
(.pack @trigger-frame))
:name "Delete Trigger")]
(seesaw/config! panel :popup (fn [e] (when (> (count (get-triggers)) 1) [delete-action])))
(seesaw/listen (seesaw/select panel [:#players])
:item-state-changed (fn [e] ; Update player status when selection changes
(show-device-status panel)))
Expand All @@ -221,24 +242,12 @@
(show-device-status panel)
panel)))

(defn- adjust-to-new-trigger
"Called when a trigger is added or removed to restore the proper
alternation of background colors, and update any other user
interface elements that might be affected."
[]
(doall (map (fn [trigger color]
(seesaw/config! trigger :background color))
(get-triggers) (cycle ["#eee" "#ddd"]))))

(def ^:private new-trigger-action
"The menu action which adds a new Trigger to the end of the list"
"The menu action which adds a new Trigger to the end of the list."
(seesaw/action :handler (fn [e]
(seesaw/config! (seesaw/select @trigger-frame [:#triggers])
:items (concat (get-triggers) [(create-trigger-row)]))
(adjust-to-new-trigger)
(when (< 100 (- (.height (.getBounds (.getGraphicsConfiguration @trigger-frame)))
(.height (.getBounds @trigger-frame))))1
(.pack @trigger-frame)))
(adjust-to-new-trigger))
:name "New Trigger"
:key "menu T"))

Expand All @@ -259,6 +268,38 @@
(prefs/add-reader 'beat_link_trigger.core.PlayerChoice map->PlayerChoice)
(prefs/add-reader 'beat_link_trigger.core.MidiChoice map->MidiChoice)

(def ^:private save-action
"The menu action which saves the configuration to a user-specified file."
(seesaw/action :handler (fn [e]
(save-triggers-to-preferences)
(when-let [file (chooser/choose-file :type :save)]
(try
(prefs/save-to-file file)
(catch Exception e
(seesaw/alert (str "<html>Unable to Save.<br><br>" e)
:title "Problem Writing File" :type :error)))))
:name "Save"
:key "menu S"))

(declare recreate-trigger-rows)

(def ^:private load-action
"The menu action which loads the configuration from a user-specified file."
(seesaw/action :handler (fn [e]
(when-let [file (chooser/choose-file
:filters [(chooser/file-filter "BeatLinkTrigger Files"
prefs/valid-file?)])]
(try
(prefs/load-from-file file)
(seesaw/config! (seesaw/select @trigger-frame [:#triggers])
:items (recreate-trigger-rows))
(adjust-to-new-trigger)
(catch Exception e
(seesaw/alert (str "<html>Unable to Load.<br><br>" e)
:title "Problem Reading File" :type :error)))))
:name "Load"
:key "menu L"))

(defn- midi-environment-changed
"Called when CoreMidi4J reports a change to the MIDI environment, so we can update the menu of
available MIDI outputs."
Expand Down Expand Up @@ -327,15 +368,15 @@
"Create and show the trigger window."
[]
(let [root (seesaw/frame :title "Beat Link Triggers" :on-close :exit
:menubar (seesaw/menubar :items [(seesaw/menu :text "Window" :items [new-trigger-action])]))
:menubar (seesaw/menubar :items [(seesaw/menu :text "File" :items [load-action save-action])
(seesaw/menu :text "Window" :items [new-trigger-action])]))
panel (seesaw/scrollable (seesaw/vertical-panel
:id :triggers
:items (recreate-trigger-rows)))]
(seesaw/config! root :content panel)
(seesaw/pack! root)
(seesaw/show! root)
(reset! trigger-frame root)
(adjust-to-new-trigger)))
(adjust-to-new-trigger)
(seesaw/show! root)))

(defn- install-mac-about-handler
"If we are running on a Mac, load the namespace that only works
Expand Down
28 changes: 28 additions & 0 deletions src/beat_link_trigger/prefs.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns beat-link-trigger.prefs
"Functions for managing application preferences"
(:require [clojure.edn :as edn]
[fipp.edn :as fipp]
[beat-link-trigger.about :as about])
(:import java.util.prefs.Preferences))

Expand Down Expand Up @@ -34,3 +35,30 @@
(let [prefs (prefs-node)]
(.put prefs "prefs" (prn-str m))
(.flush prefs)))

(defn save-to-file
"Saves the preferences to a text file."
[file]
(spit file (with-out-str (fipp/pprint (get-preferences)))))

(defn valid-file?
"Checks whether the specified file seems to be a valid save file. If
so, returns it; otherwiser returns nil."
[file]
(try
(with-open [in (java.io.PushbackReader. (clojure.java.io/reader file))]
(let [m (edn/read {:readers @prefs-readers} in)]
(when (some? (:beat-link-trigger-version m))
m)))
(catch Exception e
nil)))

(defn load-from-file
"Read the preferences from a text file."
[file]
(if (valid-file? file)
(with-open [in (java.io.PushbackReader. (clojure.java.io/reader file))]
(let [m (edn/read {:readers @prefs-readers} in)]
(put-preferences m)
m))
(throw (IllegalArgumentException. (str "Unreadable file: " file)))))

0 comments on commit 9a7deeb

Please sign in to comment.