2

I'm working on a GUI app in Clojure using Seesaw and am having trouble getting a listbox (JList in Java) to update when my custom ListModel gets updated.

Here's some of my code:

(deftype ActionHistoryListModel
  [^{:unsynchronized-mutable true} listeners
   ^{:unsynchronized-mutable true} listening-to]

  ListModel
  (addListDataListener [this listener]
    (set! listeners (conj listeners listener)))
  (removeListDataListener [this listener]
    (set! listeners (remove #(= % listener) listeners)))
  (getSize [this] 
    (get-in (deref listening-to) [:count]))
  (getElementAt [this index]
    (get-in (deref listening-to) [:actions index]))

  ActionHistoryListModelProtocol
  (listen-to [this r]
    (do
      (set! listening-to r)
      (add-watch r this (fn [_ _ _ new-state] (.notify this new-state)))))
  (notify [this new-state]
    (let [action ((meta new-state) :last-action)
          const  (cond
            (= action :create) INTERVAL_ADDED
            (= action :update) CONTENTS_CHANGED)
          index  (last ((meta new-state) :action-target))
          event  (ListDataEvent. this const index index)
          notification (cond
            (= action :create) #(.intervalAdded % event)
            (= action :update) #(.contentsChanged % event))
          ]
      (. (.. System out) print (str "Index: " index "\n" "Event: " event "\n"))
      (map #(invoke-later (notification %)) listeners)))
  )

(defn make-action-history-list-model []
  (ActionHistoryListModel. #{} nil))

(def ahlm (make-action-history-list-model))
(.listen-to ahlm action-history)

(def undo-list (listbox :model ahlm))

; then put list in frame...

where action-history is a ref.

It goes to the point where the list should be updated because the System.out.print is happening, but the listbox doesn't want to update

Any ideas on what might be going wrong? Is it something with the mix of using the EDT and watch callback?

Let me know if more code is needed.

paul
  • 1,655
  • 11
  • 23

1 Answers1

1

Custom models are always tricky especially around event notification so it's hard to say how well this will work. That said, my best guess as to why nothing is being notified is that your using map which is lazy, i.e. the last form in your notify method doesn't actually do anything. Try this instead:

(doseq [listener listeners] 
  (invoke-later (notification listener)))

Good luck.

Dave Ray
  • 39,616
  • 7
  • 83
  • 82
  • That did the trick, thanks Dave! I hadn't known that `map` was lazy; that'll be good to keep in mind. I'm not sure if I'll end up using this fix in the final code -- I came up with another version that uses the `DefaultListModel` that's 1/4th the size (so far), but I haven't committed to it yet. Thanks for all your work on Seesaw; I haven't used it much yet but so far I love it! – paul Aug 22 '12 at 22:55