Wednesday, December 29, 2010

Frustrating swing...DocumentEvent

I spent a good 2 hours trying to figure out why after I clicked on the Browse button what I expected to happen wasn't happening.  Clicking on the Browse button calls up a JFileChooser dialog.  Once the user selects the desired directory or file and clicks the Ok button, this will set the text of the same row's JTextField.  However, the same text wasn't showing in the Command row JTextField which it should have.

The strange thing is that typing in the field belonging to any of the first 3 rows WILL update the text in the fourth (which I call the command row, since it belongs to a map like {:command {:label lbl :field fld :button btn}}).  So my suspicion is that filling in the text via calling the JTextField.setText function did NOT fire an event that the DocumentListener was listening for.

I was correct and learned that setText is not a bound property and thus doesn't trigger event.  I then learned that I needed to make a change to the JTextField's underlying Document to trigger an event that insertUpdate would be called on.  I tried doing this, and my code now looks like this:



  36 ;; Create a proxy for clicking on the Browse button
  37 (defn on-click 
  38   "Returns a proxy handler for a given button"
  39   [ field ]
  40   ;; closures are cool. 
  41   (proxy [ActionListener] []
  42     (actionPerformed [ event ]
  43       (let [text (.getText field) 
  44             selection (get-file-selection) 
  45             fld-doc (.getDocument field)
  46             fld-len (.getLength fld-doc)]
  47         (if (empty? text) 
  48           (.insertString fld-doc 0 selection nil)
  49           (let [ tx (apply str text ":" selection) ]
  50             (doto fld-doc
  51               (.remove 0 fld-len)
  52               (.insertString 0 tx nil))))))))  
  53 
  54 (defn set-fld 
  55   [ field fld-string ]
  56   (.setText field fld-string))
  57 
  58 
  59 ;; When any of the textfields (except the cmd-fld) changes, we
  60 ;; need to add those changes to cmd-fld.  cmd-fld should always
  61 ;; hold the concatenation of: ext-jar row's fld, classpath fld
  62 ;; and clojars fld
  63 (defn cp-string [ src flds ]
  64   (if (.isFocusOwner src)
  65     (let [ ext-jar-txt (.getText (flds :ext-jar-row))
  66            cp-txt (.getText (flds :classpath-row))
  67            clj-txt (.getText (flds :clojars-row))
  68            cmd-fld (flds :cmd-row)
  69            all-flds (str-join ":" [ext-jar-txt cp-txt clj-txt])]
  70       (println "external jar = " ext-jar-txt)
  71       (println "classpath = "cp-txt)
  72       (println "clojars = " clj-txt)
  73       (set-fld cmd-fld (str "java -cp " all-flds)))))
  74 
  75 
  76 ;; We need a proxy that will be a TextHandler for the cmd-fld.
  77 ;; As text is entered into any of the first three rows (either
  78 ;; manually or via the FileChooser), the command JTextField
  79 ;; should reflect those changes 
  80 (defn text-handler
  81   "Returns a proxy for a given JTextField"
  82   [ src txt-flds ]
  83   (.. src getDocument 
  84     (addDocumentListener 
  85       (proxy [DocumentListener] []
  86         (insertUpdate [e] (cp-string src txt-flds))
  87         (removeUpdate [e] (cp-string src txt-flds))
  88         (changedUpdate [e] (cp-string src txt-flds))))))


The insertString is supposed to alter the Document object which in turn should fire a DocumentEvent.  So the basic flow is like this:

1.  user selects file
2.  this causes actionPerformed to be called
3.  this causes the JTextField Document to change
4.  this should cause a DocumentEvent to be fired
5.  if that happened, insertUpdate should be called from the DocumentListener
6. which would call cp-string to concatenate all the JTextFields and return the result in the command JTextField

I know the DocumentListener works, because if you manually type into the field...the command row's JTextField immediately updates with whatever is typed in.  So I'm not sure what I'm doing wrong here.

Here's the latest screenshot:

No comments:

Post a Comment