[Schmitzm-commits] r2176 - in trunk/schmitzm-core/src/main/java/de/schmitzm/swing: . event

scm-commit at wald.intevation.org scm-commit at wald.intevation.org
Fri Dec 21 16:19:06 CET 2012


Author: mojays
Date: 2012-12-21 16:19:06 +0100 (Fri, 21 Dec 2012)
New Revision: 2176

Added:
   trunk/schmitzm-core/src/main/java/de/schmitzm/swing/JSpinner.java
   trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingKeyListener.java
   trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingMouseListener.java
   trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingSettable.java
Modified:
   trunk/schmitzm-core/src/main/java/de/schmitzm/swing/SliderSpinnerPanel.java
Log:
new JSpinner with "value is adjusting" functionality.

Added: trunk/schmitzm-core/src/main/java/de/schmitzm/swing/JSpinner.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/swing/JSpinner.java	                        (rev 0)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/swing/JSpinner.java	2012-12-21 15:19:06 UTC (rev 2176)
@@ -0,0 +1,126 @@
+package de.schmitzm.swing;
+
+import java.awt.Component;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseListener;
+import java.util.Arrays;
+
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.SpinnerModel;
+import javax.swing.SpinnerNumberModel;
+
+import de.schmitzm.swing.event.ValueAdjustingKeyListener;
+import de.schmitzm.swing.event.ValueAdjustingMouseListener;
+import de.schmitzm.swing.event.ValueAdjustingSettable;
+
+/**
+ * {@link javax.swing.JSpinner} enhanded by "value is adjusting" functionality as long as
+ * up/down button or up/down key is pressed.<br>
+ * <b>Note:</b>: The "value is adjusting" property does <u>not</u> avoid listener calls
+ * automatically (by {@link #fireStateChanged()}. Each listener can/must decide individually
+ * whether to react even on "value adjusting" changes or not.
+ * @author Martin O.J. Schmitz
+ */
+public class JSpinner extends javax.swing.JSpinner implements ValueAdjustingSettable {
+
+  /** Flag indicating whether spinner value currently is adjusting. */
+  protected boolean valueIsAdjusting = false;
+  /** MouseListener for up/down buttons to set "valueIsAdjusting" flag
+   *  as long as button is pressed */
+  protected MouseListener valueAdjustingMouseListener;
+  /** KeyListener for up/down keys to set "valueIsAdjusting" flag
+   *  as long as key is pressed */
+  protected KeyListener valueAdjustingKeyListener;
+  
+  /**
+   * Creates new spinner with {@link SpinnerNumberModel}.
+   */
+  public JSpinner() {
+    super();
+    init();
+  }
+
+  /**
+   * Creates a new spinner.
+   */
+  public JSpinner(SpinnerModel model) {
+    super(model);
+    init();
+  }
+
+  /**
+   * Called for every constructor.
+   */
+  protected void init() {
+    // MouseListener for up/down buttons to set "valueIsAdjusting"
+    // flag as long as button is pressed
+    valueAdjustingMouseListener = new ValueAdjustingMouseListener() {
+      @Override
+      public void setValueIsAdjusting(boolean valueIsAdjusting) {
+        JSpinner.this.setValueIsAdjusting(valueIsAdjusting);
+      }
+    };
+    // KeyListener for up/down keys to set "valueIsAdjusting"
+    // flag as long as key is pressed
+    valueAdjustingKeyListener =  new ValueAdjustingKeyListener(true,false) {
+      @Override
+      public void setValueIsAdjusting(boolean valueIsAdjusting) {
+        JSpinner.this.setValueIsAdjusting(valueIsAdjusting);
+      }
+    };
+    
+    // Dirty, but does its work!
+    // Add listener to up/down buttons...
+    for (Component child : getComponents()) {
+      if ("Spinner.nextButton".equals(child.getName())) {
+        ((JButton) child).addMouseListener(valueAdjustingMouseListener);
+      }
+      if ("Spinner.previousButton".equals(child.getName())) {
+        ((JButton) child).addMouseListener(valueAdjustingMouseListener);
+      }
+    }
+  }
+
+  /**
+   * If not already added, this method adds the {@link #valueAdjustingKeyListener}
+   * to the current editor component.
+   */
+  protected void addValueAdjustingKeyListenerToEditor() {
+    JComponent comp = getEditor();
+    if ( comp instanceof DefaultEditor )
+     comp = ((DefaultEditor)comp).getTextField();
+    if ( !Arrays.asList( comp.getKeyListeners() ).contains(valueAdjustingKeyListener) )
+      comp.addKeyListener(valueAdjustingKeyListener);
+  }
+  
+  
+  /**
+   * After calling the super method, the {@link #valueAdjustingKeyListener}
+   * is added to the new editor component.
+   * @see #addValueAdjustingKeyListenerToEditor()
+   */
+  @Override
+  public void setEditor(JComponent comp) {
+    super.setEditor(comp);
+    addValueAdjustingKeyListenerToEditor();
+  }
+
+  /**
+   * Returns whether current value change is part of multiple changes.
+   */
+  public boolean getValueIsAdjusting() {
+    return valueIsAdjusting;
+  }
+  
+  /**
+   * Sets whether (following) value changes are part of multiple changes.
+   * If set to {@code false}, {@link #fireStateChanged()} is called.
+   */
+  @Override
+  public void setValueIsAdjusting(boolean valueIsAdjusting) {
+    this.valueIsAdjusting = valueIsAdjusting;
+    if ( !valueIsAdjusting )
+      fireStateChanged();
+  }
+}
\ No newline at end of file

Modified: trunk/schmitzm-core/src/main/java/de/schmitzm/swing/SliderSpinnerPanel.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/swing/SliderSpinnerPanel.java	2012-12-21 11:44:35 UTC (rev 2175)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/swing/SliderSpinnerPanel.java	2012-12-21 15:19:06 UTC (rev 2176)
@@ -36,11 +36,15 @@
 
 import javax.swing.JLabel;
 import javax.swing.JSlider;
-import javax.swing.JSpinner;
 import javax.swing.SpinnerNumberModel;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 
+import org.apache.log4j.Logger;
+
+import de.schmitzm.lang.LangUtil;
+import de.schmitzm.swing.event.ValueAdjustingKeyListener;
+
 /**
  * Dieses Panel enthaelt einen Slider, der mit einem Spinner verknuepft ist.
  * Zusaetzlich wird eine Ueberschrift ausgewiesen.
@@ -48,6 +52,9 @@
  * @version 1.0
  */
 public class SliderSpinnerPanel extends JPanel {
+  /** For debugging */
+  protected Logger LOGGER = LangUtil.createLogger(this);
+  
   /** Konstante fuer die <b>horizontale</b> Orientierung des Sliders.
    *  @see JSlider#HORIZONTAL */
   public static final int HORIZONTAL = JSlider.HORIZONTAL;
@@ -61,7 +68,16 @@
   protected JLabel headerLabel = null;
   /** Spinner des Panels. */
   protected JSpinner spinner = null;
+  /** Value when {@link #fireChangeEvent(ChangeEvent)} was called the last time. */
+  protected Double lastValue = null;
 
+  /** {@link JSlider} can only maintain {@link Integer} values so we have to convert
+   *  the value. */
+  protected double sliderValueConversionFactor = 1.0;
+  
+  /** Flag during update of slider and spinner */
+  protected boolean sliderSpinnerAdjusting = false;
+  
   /**
    * Erzeugt ein neues Panel.
    * @param orientation Orientierung des Sliders
@@ -83,6 +99,12 @@
     }
     // Slider
     this.slider = new JSlider(orientation);
+    slider.addKeyListener( new ValueAdjustingKeyListener() {
+      @Override
+      public void setValueIsAdjusting(boolean valueIsAdjusting) {
+        slider.setValueIsAdjusting(valueIsAdjusting);
+      }
+    });
     this.add( slider, new GridBagConstraints(
       0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0) );
     // Spinner
@@ -94,21 +116,37 @@
     // ActionListener auf Spinner, um Slider zu aktualisieren
     this.spinner.addChangeListener(new ChangeListener() {
       public void stateChanged(ChangeEvent e) {
-        double newValue = ((Number)spinner.getValue()).doubleValue();
-        if ( Math.round(newValue) != slider.getValue())
-          slider.setValue( (int)Math.round(newValue) );
+        if ( sliderSpinnerAdjusting )
+          return;
+        sliderSpinnerAdjusting = true;
+        double newValue = ((Number)spinner.getValue()).doubleValue() * sliderValueConversionFactor;
+        slider.setValue( (int)Math.round(newValue) );
+        sliderSpinnerAdjusting = false;
       }
     });
 
     // ActionListener auf Slider, um Spinner zu aktualisieren
     this.slider.addChangeListener(new ChangeListener() {
       public void stateChanged(ChangeEvent e) {
-        int newValue = slider.getValue();
-        if (newValue != Math.round( ((Number)spinner.getValue()).doubleValue() ))
-          spinner.setValue(newValue);
+        if ( sliderSpinnerAdjusting )
+          return;
+        sliderSpinnerAdjusting = true;
+        double newValue = slider.getValue() / sliderValueConversionFactor;
+        spinner.setValue(newValue);
+        sliderSpinnerAdjusting = false;
       }
     });
 
+    // ActionListener auf Slider, um ChangeEvent an Listener weiterzugeben
+    ChangeListener changeListener = new ChangeListener() {
+      @Override
+      public void stateChanged(ChangeEvent e) {
+        fireChangeEvent(e);
+      }
+    };
+    this.slider.addChangeListener(changeListener);
+    this.spinner.addChangeListener(changeListener);
+    
     // Minimum, Maximum, StepSize und initalalier Wert einstellen
     setRestrictions(value,min,max,step);
   }
@@ -215,8 +253,9 @@
     ) );
     spinner.setEditor( new JSpinner.NumberEditor(this.spinner,SwingUtil.getNumberFormatPattern(stepSize)) );
     // Slider einstellen
+    sliderValueConversionFactor = (stepSize < 1) ? 1/stepSize : 1;
     slider.setMinimum( (int)Math.floor(min) );
-    slider.setMaximum( (int)Math.ceil(max) );
+    slider.setMaximum( (int)(Math.ceil(max) * sliderValueConversionFactor) );
     setValue( value );
   }
 
@@ -240,4 +279,41 @@
   public JSpinner getSpinner() {
     return this.spinner;
   }
+  
+
+  
+  /**
+   * Adds a listener to spinner.
+   */
+  public void addChangeListener(ChangeListener l) {
+    this.listenerList.add(ChangeListener.class, l);
+  }
+
+  /**
+   * Removes a listener from component 
+   */
+  public void removesChangeListener(ChangeListener l) {
+    this.listenerList.remove(ChangeListener.class, l);
+  }
+  
+  /**
+   * Pipes the event to all connected {@link ChangeListener ChangeListeners},
+   * but only if value is not adjusting anymore!
+   */
+  protected void fireChangeEvent(ChangeEvent e) {
+    if ( slider.getValueIsAdjusting() || spinner.getValueIsAdjusting() )
+      return;
+    // on manual input in spinner text field 2 events are created, on for
+    // spinner and one for the slider!
+    // To avoid this we double-check for last value
+    double value = getValue();
+    if ( lastValue != null && value == lastValue )
+      return;
+    LOGGER.debug("value changed: "+lastValue+" -> "+value);
+    lastValue = value;
+    for (ChangeListener l : getListeners(ChangeListener.class))
+      l.stateChanged(e);
+  }
+  
+
 }

Added: trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingKeyListener.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingKeyListener.java	                        (rev 0)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingKeyListener.java	2012-12-21 15:19:06 UTC (rev 2176)
@@ -0,0 +1,88 @@
+package de.schmitzm.swing.event;
+
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
+/**
+ * {@link KeyListener} which sets "value adjusting" flag to {@code true} as
+ * long as an arrow key (up/down/left/right) is pressed.
+ * @author Martin O.J. Schmitz
+ */
+public abstract class ValueAdjustingKeyListener extends KeyAdapter implements ValueAdjustingSettable {
+  protected boolean handleUpDown = true;
+  protected boolean handleLeftRight = true;
+  
+  /**
+   * Creates new listener, which reacts on up, down, left and right
+   * key.
+   */
+  public ValueAdjustingKeyListener() {
+    this(true,true);
+  }
+  
+  /**
+   * Creates new listener 
+   * @param handleUpDown indicates whether "value is adjusting" is set on 
+   *                     up and down key
+   * @param handleLeftRight indicates whether "value is adjusting" is set
+   *                        on left and right key
+   */
+  public ValueAdjustingKeyListener(boolean handleUpDown, boolean handleLeftRight) {
+    super();
+    this.handleLeftRight = handleLeftRight;
+    this.handleUpDown    = handleUpDown;
+  }
+
+  /**
+   * Sets "value adjusting" flag to {@code true}.
+   * @see #setValueIsAdjusting(boolean) 
+   */
+  @Override
+  public void keyPressed(KeyEvent e) {
+    if ( isArrowKeyEvent(e) )
+      setValueIsAdjusting(true);
+  }
+
+  /**
+   * Sets "value adjusting" flag to {@code false}. 
+   * @see #setValueIsAdjusting(boolean) 
+   */
+  @Override
+  public void keyReleased(KeyEvent e) {
+    if ( isArrowKeyEvent(e) )
+      setValueIsAdjusting(false);
+  }
+
+  /**
+   * Sets the "value adjusting" flag. Has to be implemented individually to
+   * call the {@code setValueIsAdjusting(.)} method of the individual
+   * component. 
+   */
+  @Override
+  public abstract void setValueIsAdjusting(boolean valueIsAdjusting);
+  
+  /**
+   * Checks whether {@link KeyEvent} represents arrow key. 
+   */
+  public boolean isArrowKeyEvent(KeyEvent e) {
+    int id = e.getExtendedKeyCode();
+    switch ( id ) {
+      case KeyEvent.VK_UP:
+      case KeyEvent.VK_DOWN:
+      case KeyEvent.VK_KP_DOWN:
+      case KeyEvent.VK_KP_LEFT:
+        if ( handleUpDown )
+          return true;
+        break;
+      case KeyEvent.VK_LEFT:
+      case KeyEvent.VK_RIGHT:
+      case KeyEvent.VK_KP_UP:
+      case KeyEvent.VK_KP_RIGHT:
+        if ( handleLeftRight )
+          return true;
+        break;
+    }
+    return false;
+  }
+}
\ No newline at end of file

Added: trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingMouseListener.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingMouseListener.java	                        (rev 0)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingMouseListener.java	2012-12-21 15:19:06 UTC (rev 2176)
@@ -0,0 +1,38 @@
+package de.schmitzm.swing.event;
+
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+/**
+ * {@link MouseListener} which sets "value adjusting" flag to {@code true} as
+ * long as mouse button is pressed.
+ * @author Martin O.J. Schmitz
+ */
+public abstract class ValueAdjustingMouseListener extends MouseAdapter implements ValueAdjustingSettable {
+  /**
+   * Sets "value adjusting" flag to {@code false}.
+   * @see #setValueIsAdjusting(boolean) 
+   */
+  @Override
+  public void mouseReleased(MouseEvent e) {
+    setValueIsAdjusting(false);
+  }
+  
+  /**
+   * Sets "value adjusting" flag to {@code true}.
+   * @see #setValueIsAdjusting(boolean) 
+   */
+  @Override
+  public void mousePressed(MouseEvent e) {
+    setValueIsAdjusting(true);
+  }
+  
+  /**
+   * Sets the "value adjusting" flag. Has to be implemented individually to
+   * call the {@code setValueIsAdjusting(.)} method of the individual
+   * component. 
+   */
+  @Override
+  public abstract void setValueIsAdjusting(boolean valueIsAdjusting);
+}
\ No newline at end of file

Added: trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingSettable.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingSettable.java	                        (rev 0)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/swing/event/ValueAdjustingSettable.java	2012-12-21 15:19:06 UTC (rev 2176)
@@ -0,0 +1,14 @@
+package de.schmitzm.swing.event;
+
+/**
+ * Interface for component for which the "value adjusting" flag can be
+ * set.
+ * @author Martin O.J. Schmitz
+ *
+ */
+public interface ValueAdjustingSettable {
+  /**
+   * Sets the "value adjusting" flag.
+   */
+  public void setValueIsAdjusting(boolean valueIsAdjusting);
+}
\ No newline at end of file



More information about the Schmitzm-commits mailing list