Package org.openquark.gems.client.valueentry

Source Code of org.openquark.gems.client.valueentry.SliderNumberValueEntryPanel$SliderChangeListener

/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Business Objects nor the names of its contributors
*       may be used to endorse or promote products derived from this software
*       without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/


/*
* SliderNumberValueEntryPanel.java
* Creation date:  Sept 30, 2004
* By: Richard Webster
*/
package org.openquark.gems.client.valueentry;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.text.NumberFormat;
import java.util.Hashtable;

import javax.swing.JLabel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.openquark.cal.compiler.TypeExpr;
import org.openquark.cal.valuenode.LiteralValueNode;
import org.openquark.cal.valuenode.ValueNode;
import org.openquark.util.OrientationType;
import org.openquark.util.ui.NumericTextField;


/**
* A value entry panel displaying a slider for setting and displaying the value.
* @author Richard Webster
*/
public class SliderNumberValueEntryPanel extends ValueEntryPanel {

    private static final long serialVersionUID = 6895591427785034118L;

    /**
     * When the state of the slider changes, we have to update the ValueNode and
     * the label's text.
     * It also notifies the ValueEditorListeners of the value change.
     */
    private class SliderChangeListener implements ChangeListener {
        public void stateChanged(ChangeEvent evt) {
            if (!ignoreChangeEvents) {
                double newValue = sliderValueToRealValue(slider.getValue());
                changeValue(newValue);

                // Update the label.
                ignoreChangeEvents = true;
                label.setText(getCurrentValueString());
                ignoreChangeEvents = false;

                // Commit the value when the user lets go of the slider.
                if (!slider.getValueIsAdjusting()) {
                    handleCommitGesture();
                }
            }
        }
    }

    /** The number of slider positions to use (when using fractional values). */
    private static final int N_SLIDER_POSITIONS = 100;

    /** The slider control. */
    private final JSlider slider;

    /** A text entry field for showing the current value. */
    private JTextField label;

    /** Whether the values must be integers or whether the values can be fractional. */
    private final boolean integerValues;

    /** The scaling factor between the real values and the slider integer values. */
    private final double scalingFactor;

    /** The current value displayed in the controls. */
    private double currentValue;

    /** This flag will be true when changing the value of the slider or label
     *  programatically in order to avoid extra events from being fired. */
    private boolean ignoreChangeEvents = false;

    /**
     * SliderNumberValueEntryPanel constructor.
     * @param valueEditorHierarchyManager  the value editor hierarchy manager
     * @param valueNode                    the initial value for the VEP
     * @param realLowerBound               the lower bound value for the slider
     * @param realUpperBound               the upper bound value for the slider
     * @param orientation                  the orientation for the slider
     * @param integerValues                True if working with integral values
     */
    public SliderNumberValueEntryPanel(ValueEditorHierarchyManager valueEditorHierarchyManager,
                                       ValueNode valueNode,
                                       double realLowerBound,
                                       double realUpperBound,
                                       OrientationType orientation,
                                       boolean integerValues) {
        super(valueEditorHierarchyManager, valueNode);
        this.integerValues = integerValues;

        // Make sure that the upper bound is higher than the lower bound.
        realUpperBound = (realUpperBound > realLowerBound) ? realUpperBound : (realLowerBound + 1.0);
       
        // Update the displayed values.
        currentValue = 0;
        Object value = getValueNode().getValue();
        if (value instanceof Number) {
            currentValue = ((Number) value).doubleValue();
        }

        // Make sure that the current value is within the slider bounds.
        realLowerBound = Math.min(realLowerBound, currentValue);
        realUpperBound = Math.max(realUpperBound, currentValue);

        // Determine the scaling factor between the real values and the slider integer values.
        this.scalingFactor = integerValues ? 1.0 : (realUpperBound - realLowerBound) / N_SLIDER_POSITIONS;

        this.slider = new JSlider(orientation.isHorizontal() ? SwingConstants.HORIZONTAL : SwingConstants.VERTICAL,
                                  realValueToSliderValue(realLowerBound),
                                  realValueToSliderValue(realUpperBound),
                                  realValueToSliderValue(currentValue));

        initializeUI(realLowerBound, realUpperBound, orientation);
    }

    /**
     * Initialize the UI components for this Value Entry Panel
     */
    private void initializeUI(final double lowerBound, final double upperBound, OrientationType orientation) {
        // We don't need the launch editor button, nor the value text field.
        this.remove(getLaunchEditorButton());
        this.remove(this.getValueField());

        // Configure the slider.
        slider.addChangeListener(new SliderChangeListener());

        // Show tick marks and labels in the slider.
        final int nMajorTicks = 5;
        int majorTickSpacing;
        if (integerValues) {
            majorTickSpacing = ((int) upperBound - (int) lowerBound + 2) / nMajorTicks;
        }
        else {
            majorTickSpacing = N_SLIDER_POSITIONS / nMajorTicks;
        }
        int minorTickSpacing = majorTickSpacing / 2;

        slider.setMajorTickSpacing(majorTickSpacing);
        slider.setMinorTickSpacing(minorTickSpacing);
        slider.setPaintTicks(true);
        slider.setPaintLabels(true);
        slider.setBackground(Color.white);
        slider.setInverted(orientation.isReversed());

        // Create the label table.
        // Note that a Hashtable is required by slider.setLabelTable().
        Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>();
        for (int i = 0; i <= nMajorTicks; ++i) {
            double dblVal = lowerBound + sliderValueToRealValue(i * majorTickSpacing);
            String labelVal = formatDouble(dblVal);
            JLabel label = new JLabel(labelVal);

            // TODO: can the formatting of the labels be set to match the rest of the component?
            labelTable.put(new Integer(realValueToSliderValue(lowerBound) + i * majorTickSpacing), label);
        }
        slider.setLabelTable(labelTable);


        // Cancel the sliding action if the Esc key is pressed.
        slider.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    handleCancelGesture();

                    currentValue = 0;
                    Object value = getValueNode().getValue();
                    if (value instanceof Number) {
                        currentValue = ((Number) value).doubleValue();
                    }

                    ignoreChangeEvents = true;
                    slider.setValue(realValueToSliderValue(currentValue));
                    label.setText(getCurrentValueString());
                    ignoreChangeEvents = false;

                    // TODO: find a proper way to reset the UI...
                    slider.updateUI();
                }
            }
        });

        this.add(slider, BorderLayout.CENTER);

        // Create the value label.
        label = integerValues ? NumericTextField.createIntegerTextField() : NumericTextField.createDecimalTextField();
        label.setText(getCurrentValueString());
        label.setBackground(Color.white);
        label.setBorder(null);
        label.setHorizontalAlignment(orientation.isHorizontal() ? SwingConstants.RIGHT : SwingConstants.CENTER);

        // TODO: do this properly for the fractional values case...
        int nColumns = integerValues ? Math.max(nCharsForValue((int) lowerBound), nCharsForValue((int) upperBound)) : 4;
        label.setColumns(nColumns);

        label.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (!ignoreChangeEvents) {
                        double newValue = Double.parseDouble(label.getText());
                        if (newValue >= lowerBound && newValue <= upperBound) {
                            changeValue(newValue);
                           
                            // Update the slider.
                            ignoreChangeEvents = true;
                            slider.setValue(realValueToSliderValue(currentValue));
                            ignoreChangeEvents = false;
   
                            // Commit the change.
                            handleCommitGesture();
                        }
                    }
                }
            });

        this.add(label, orientation.isHorizontal() ?
                            orientation.isReversed() ? BorderLayout.WEST  : BorderLayout.EAST :
                            orientation.isReversed() ? BorderLayout.NORTH : BorderLayout.SOUTH);
    }

    /**
     * Returns the slider value corresponding to a real value.
     * @param realValue  a real value
     * @return the slider value corresponding to a real value
     */
    private int realValueToSliderValue(double realValue) {
        return (int) Math.round(realValue / scalingFactor);
    }

    /**
     * Returns the real value corresponding to a slider value.
     * @param sliderValue  a slider value
     * @return the real value corresponding to a slider value
     */
    private double sliderValueToRealValue(int sliderValue) {
        return sliderValue * scalingFactor;
    }

    /**
     * Sets the current value to the specified value.
     * @param newValue  the new current value
     */
    private void changeValue(double newValue) {
        currentValue = newValue;

        ValueNode oldValue = getValueNode();

        Object newValueObj = integerValues
                                ? (Object) new Integer((int) newValue)
                                : (Object) new Double(newValue);

        TypeExpr typeExpr = getValueNode().getTypeExpr();
        replaceValueNode(new LiteralValueNode(newValueObj, typeExpr), true);

        notifyValueChanged(oldValue);
    }

    /**
     * Returns a string representation of the current value.
     */
    private String getCurrentValueString() {
        if (integerValues) {
            return String.valueOf((int) currentValue);
        }
        else {
            return formatDouble(currentValue);
        }
    }

    /**
     * Formats a double value as a string.
     * @param dblVal  a double value
     * @return the formatted double value
     */
    private String formatDouble(double dblVal) {
        // TODO: figure this out automatically...
        final int nDecimalPlaces = 2;

        // TODO: perhaps this should use a NumberFormatter...
        NumberFormat numberFormat = NumberFormat.getInstance ();
        numberFormat.setMinimumFractionDigits (0);
        numberFormat.setMaximumFractionDigits (nDecimalPlaces);

        return numberFormat.format(dblVal);
    }

    /**
     * Returns the number of chars needed to represent the specified integer value.
     * @param value  the integer value to be represented.
     * @return the number of chars needed to represent the integer value
     */
    private int nCharsForValue(int value) {
        int nDigits = (value == 0) ? 1 : (int) (Math.log(Math.abs(value)) / Math.log(10.0)) + 1;
        return (value >= 0) ? nDigits : nDigits + 1;
    }

    /**
     * @see org.openquark.gems.client.valueentry.ValueEditor#setEditable(boolean)
     */
    @Override
    public void setEditable(boolean isEditable) {
        super.setEditable(isEditable);
       
        slider.setEnabled(isEditable);
        label.setEditable(isEditable);
    }
}
TOP

Related Classes of org.openquark.gems.client.valueentry.SliderNumberValueEntryPanel$SliderChangeListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.