001 package jigcell.compare.compare2; 002 003 import java.awt.event.ActionEvent; 004 import java.awt.event.ActionListener; 005 import java.awt.event.ItemEvent; 006 import java.awt.event.ItemListener; 007 import java.beans.PropertyChangeEvent; 008 import java.beans.PropertyChangeListener; 009 import java.text.DecimalFormat; 010 import java.util.ArrayList; 011 import java.util.Hashtable; 012 import java.util.List; 013 import java.util.Map; 014 import javax.swing.DefaultBoundedRangeModel; 015 import javax.swing.JDialog; 016 import javax.swing.JLabel; 017 import javax.swing.JSlider; 018 import javax.swing.event.ChangeEvent; 019 import javax.swing.event.ChangeListener; 020 import javax.swing.text.Document; 021 import javax.swing.text.PlainDocument; 022 import jigcell.compare.data.IDataGeneratorCollection; 023 import jigcell.compare.impl.Compare; 024 import jigcell.compare.ui.ListComboBoxModel; 025 import jigcell.compare.ui.InterfaceBuilder; 026 027 /** 028 * Editor for comparison thresholds. 029 * 030 * <p> 031 * This code is licensed under the DARPA BioCOMP Open Source License. See LICENSE for more details. 032 * </p> 033 * 034 * @author Nicholas Allen 035 */ 036 037 public class ThresholdEditor extends JDialog implements ActionListener, ChangeListener, ItemListener, PropertyChangeListener { 038 039 /** 040 * Base point for the threshold slider 041 */ 042 043 protected final static int SLIDER_BASE = 100; 044 045 /** 046 * Maximum point for the threshold slider 047 */ 048 049 protected final static int SLIDER_MAX = SLIDER_BASE << 1; 050 051 /** 052 * Label for the experiment name 053 */ 054 055 protected final static String LABEL_EXPERIMENT = "Experiment:"; 056 057 /** 058 * Name for experiment threshold components 059 */ 060 061 protected final static String NAME_THRESHOLD = "threshold"; 062 063 /** 064 * Comparator of parent view 065 */ 066 067 protected Compare compare; 068 069 /** 070 * View model 071 */ 072 073 protected Compare2Model model; 074 075 /** 076 * Document storing the threshold value 077 */ 078 079 protected Document thresholdDocument; 080 081 /** 082 * Threshold value before any adjustments were made 083 */ 084 085 protected double originalThreshold; 086 087 /** 088 * Experiment currently being edited 089 */ 090 091 protected IDataGeneratorCollection.Key currentExperiment; 092 093 /** 094 * Manages the interface for this editor 095 */ 096 097 protected InterfaceBuilder manager; 098 099 /** 100 * Slider for adjusting the threshold value 101 */ 102 103 protected JSlider thresholdSlider; 104 105 /** 106 * Model for the list of experiments 107 */ 108 109 protected ListComboBoxModel experimentBoxModel; 110 111 /** 112 * Map[IDataGeneratorCollection.Key,String] of the keys of experiments to the threshold 113 */ 114 115 protected Map experimentThresholds; 116 117 /** 118 * Formatter for displaying threshold values 119 */ 120 121 protected DecimalFormat thresholdFormat; 122 123 /** 124 * Creates a new display for editing thresholds. 125 * 126 * @param compare Comparator to use 127 * @param model View model 128 * @param experimentThresholds Map from experiment names to thresholds 129 */ 130 131 public ThresholdEditor (Compare compare, Compare2Model model, Map experimentThresholds) { 132 super (compare.getDisplayFrame (), Compare.getString ("ThresholdEditor.title"), false); 133 this.compare = compare; 134 this.model = model; 135 this.experimentThresholds = experimentThresholds; 136 createUI (); 137 compare.addPropertyChangeListener (this); 138 indexExperiments (); 139 setVisible (true); 140 } 141 142 public void createUI () { 143 thresholdFormat = new DecimalFormat ("##0.#####E0"); 144 thresholdFormat.setDecimalSeparatorAlwaysShown (true); 145 setDefaultCloseOperation (HIDE_ON_CLOSE); 146 setLocationRelativeTo (null); 147 manager = new InterfaceBuilder (this); 148 manager.addLabel (LABEL_EXPERIMENT); 149 experimentBoxModel = new ListComboBoxModel (); 150 manager.addComboBox (LABEL_EXPERIMENT, experimentBoxModel); 151 manager.endRow (true); 152 thresholdSlider = manager.addSlider (NAME_THRESHOLD, new DefaultBoundedRangeModel (SLIDER_BASE, 0, 0, SLIDER_MAX)); 153 thresholdSlider.setMajorTickSpacing (SLIDER_MAX / 10); 154 thresholdSlider.setMinorTickSpacing (SLIDER_MAX / 20); 155 thresholdSlider.setPaintLabels (true); 156 thresholdSlider.setPaintTicks (true); 157 manager.endBlock (); 158 thresholdDocument = new PlainDocument (); 159 manager.addTextField (NAME_THRESHOLD, thresholdDocument, 15); 160 getContentPane ().add (manager.endInterface ()); 161 pack (); 162 } 163 164 public void actionPerformed (ActionEvent e) { 165 if (NAME_THRESHOLD.equals (manager.getComponentName (e.getSource ()))) 166 setThreshold (InterfaceBuilder.readDocument (thresholdDocument), true); 167 } 168 169 public void itemStateChanged (ItemEvent e) { 170 if (LABEL_EXPERIMENT.equals (manager.getComponentName (e.getSource ()))) 171 setExperiment ((IDataGeneratorCollection.Key) e.getItem ()); 172 } 173 174 public void propertyChange (PropertyChangeEvent e) { 175 String name = e.getPropertyName (); 176 if (Compare2Model.PROPERTY_EXPERIMENTTHRESHOLDS_EDIT.equals (name)) { 177 try { 178 String documentThreshold = InterfaceBuilder.readDocument (thresholdDocument); 179 String actualThreshold = (String) experimentThresholds.get (currentExperiment); 180 if (documentThreshold == actualThreshold || documentThreshold != null && documentThreshold.equals (actualThreshold)) 181 return; 182 } catch (Exception _e) {} 183 updateDisplay (true); 184 return; 185 } 186 if (Compare2Model.RESOURCE_EXPERIMENTTHRESHOLDS.equals (name)) { 187 indexExperiments (); 188 } 189 } 190 191 /** 192 * Sets the experiment which this editor is working with. 193 * 194 * @param experiment Experiment name 195 */ 196 197 public void setExperiment (IDataGeneratorCollection.Key experiment) { 198 if (experiment == null) 199 throw new IllegalArgumentException (); 200 if (currentExperiment == experiment) 201 return; 202 currentExperiment = experiment; 203 experimentBoxModel.setSelectedItem (experiment); 204 updateDisplay (true); 205 } 206 207 /** 208 * Sets the threshold for the experiment this editor is working with. 209 * 210 * @param value Threshold value 211 * @param reset Reset the base threshold value 212 */ 213 214 public void setThreshold (String value, boolean reset) { 215 try { 216 setThreshold (Double.parseDouble (value), reset); 217 } catch (Exception e) { 218 compare.shellHandleException (Compare.MESSAGE_ERROR, Compare.getString ("ThresholdEditor.editError"), e); 219 } 220 } 221 222 /** 223 * Sets the threshold for the experiment this editor is working with. 224 * 225 * @param threshold Threshold 226 * @param reset Reset the base threshold value 227 */ 228 229 public void setThreshold (double threshold, boolean reset) { 230 if (currentExperiment == null) 231 throw new IllegalStateException (); 232 if (threshold < 0.0) 233 throw new IllegalArgumentException (Compare.getString ("ThresholdEditor.negativeThresholdError")); 234 experimentThresholds.put (currentExperiment, thresholdFormat.format (threshold)); 235 updateDisplay (reset); 236 compare.firePropertyChange (Compare2Model.PROPERTY_EXPERIMENTTHRESHOLDS_EDIT); 237 model.fireTableDataChanged (); 238 } 239 240 public void stateChanged (ChangeEvent e) { 241 if (NAME_THRESHOLD.equals (manager.getComponentName (e.getSource ()))) 242 setThreshold (originalThreshold * (thresholdSlider.getValue () / (double) SLIDER_BASE), false); 243 } 244 245 /** 246 * Creates a list of the known experiments. 247 */ 248 249 protected void indexExperiments () { 250 List experiments = new ArrayList (experimentThresholds.keySet ()); 251 experimentBoxModel.replaceAll (experiments); 252 if (experiments.isEmpty ()) 253 setVisible (false); 254 else if (experimentBoxModel.getSelectedItem () == null) 255 setExperiment ((IDataGeneratorCollection.Key) experiments.get (0)); 256 } 257 258 /** 259 * Resets the editing controls. 260 */ 261 262 protected void resetControls () { 263 thresholdSlider.setValue (SLIDER_BASE); 264 boolean enabled = originalThreshold != 0.0; 265 thresholdSlider.setEnabled (enabled); 266 thresholdSlider.setPaintLabels (enabled); 267 thresholdSlider.setPaintTicks (enabled); 268 if (!enabled) 269 return; 270 String value = thresholdFormat.format (originalThreshold); 271 Hashtable labels = new Hashtable (); 272 labels.put (new Integer (0), new JLabel ("0")); 273 labels.put (new Integer (SLIDER_BASE), new JLabel (value)); 274 labels.put (new Integer (SLIDER_MAX), new JLabel (thresholdFormat.format (originalThreshold * 2))); 275 thresholdSlider.setLabelTable (labels); 276 } 277 278 /** 279 * Updates the display with the latest threshold value. 280 * 281 * @param reset Reset the base threshold value 282 */ 283 284 protected void updateDisplay (boolean reset) { 285 double threshold; 286 try { 287 threshold = Double.parseDouble ((String) experimentThresholds.get (currentExperiment)); 288 } catch (Exception e) { 289 threshold = 0.0; 290 } 291 InterfaceBuilder.replaceDocument (thresholdDocument, thresholdFormat.format (threshold)); 292 if (!reset) 293 return; 294 originalThreshold = threshold; 295 resetControls (); 296 } 297 }