/*- * @(#)ExtendedJSlider.java * * Copyright: Yokogawa, All rights reserved. * YOKOGAWA PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * Author: C. Horevoorts * * ---------------------------------------------------------------------------- * Changes .... * ---------------------------------------------------------------------------- * Who When Change What * ---------------------------------------------------------------------------- * HVS Jan-05 e9435 First version documented * HVS Feb-09 i10167 Make slider more accurate * BNL Feb-09 i10167 Make slider even more accurate * BNL Feb-09 e10104 Correct update tick spacing on min and max change * HVS Feb-12 e10575 For runtime shore settings until validate() is called * HVS Jun-20 e12272 Port to JViews 2017.2 * */ package fasttools.jwui.ilv.graphic; import fasttools.jtools.common.Format; import fasttools.jtools.common.FtException; import fasttools.jtools.common.Utils; import fasttools.jwui.ilv.swing.INonRectangularJComponent; import fasttools.jwui.ilv.swing.IJComponentTagger; import fasttools.jwui.JwuiConfiguration; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.geom.Path2D; import java.awt.geom.Point2D; import java.beans.PropertyChangeListener; import java.text.DecimalFormat; import java.util.Dictionary; import java.util.Hashtable; /** * This class extends the swing JSlider to be used in the ExtendedJComponent container. * The ExtendedJComponent container extends IlvGraphic to swing components * can be used in views. */ public class ExtendedJSlider extends JSlider implements IJComponentTagger, ChangeListener, IAction, INonRectangularJComponent { private double cMaximum; private double cMinimum; private double cMajorTickSpacing; private double cMinorTickSpacing; private String cFormat = ""; private static double PHYS_HIGH = Integer.MAX_VALUE / 2; private String cLabel = ""; private int cOldValue; private double cOldDoubleValue; private boolean cInitializing; private boolean cTrackDrag; private boolean cValidated; /** * Constructor. */ public ExtendedJSlider() { try { cInitializing = true; if (JwuiConfiguration.isEditor()) { cValidated = true; } setMaximumAsDouble(100.0); setMajorTickSpacingAsDouble(10.); setMaximum((int) PHYS_HIGH); setOpaque(false); } finally { cInitializing = false; } } /** * If becomes visable then correct min/max/tick and redraw labels */ public void validate() { if (!cValidated) { cValidated = true; if (cMinimum >= cMaximum) { cMinimum = cMaximum - (cMaximum - cMinimum); } setMinorTickSpacingAsDouble(cMinorTickSpacing); setMajorTickSpacingAsDouble(cMajorTickSpacing); setValueAsDouble(cOldDoubleValue); } } /** * Sets the maximum range of the slider. * * @param maximum The maximum range */ public void setMaximumAsDouble(double maximum) { if (cValidated) { if (cMaximum != maximum) { if (cMinimum >= maximum) { cMinimum = maximum - (cMaximum - cMinimum); } cMaximum = maximum; reCreateLabels(); } } else cMaximum = maximum; } /** * Returns the maximum range. * * @return The maximum range */ public double getMaximumAsDouble() { return cMaximum; } /** * Sets the minimum range. * * @param minimum The minimum range */ public void setMinimumAsDouble(double minimum) { if (cValidated) { if (cMinimum != minimum) { if (cMaximum <= minimum) { cMaximum = minimum + (cMaximum - cMinimum); } cMinimum = minimum; reCreateLabels(); } } else cMinimum = minimum; } /** * Returns the minimum range. * * @return The minimum range */ public double getMinimumAsDouble() { return cMinimum; } /** * Sets the slider position. * * @param value The slider position */ public void setValueAsDouble(double value) { cOldDoubleValue = value; if (cValidated) { int mValue = (int) Math.floor((value - cMinimum) / (cMaximum - cMinimum) * PHYS_HIGH); cOldValue = mValue; setValue(mValue); } } /** * Returns the slider position. * * @return The slider psoition */ public double getValueAsDouble() { return cnvToDouble(getValue()); } /** * Sets the major tick spacing. * * @param spacing The major tick spacing */ public void setMajorTickSpacingAsDouble(double spacing) { cMajorTickSpacing = spacing; if (cValidated) { setMajorTickSpacing((int) Math.floor(spacing / (cMaximum - cMinimum) * PHYS_HIGH)); reCreateLabels(); } } /** * Returns the major tick spacing. * * @return The major tick spacing */ public double getMajorTickSpacingAsDouble() { return cMajorTickSpacing; } /** * Sets the major tick spacing. * * @param spacing The major tick spacing */ public void setMinorTickSpacingAsDouble(double spacing) { if (cValidated) { setMinorTickSpacing((int) Math.floor(spacing / (cMaximum - cMinimum) * PHYS_HIGH)); } cMinorTickSpacing = spacing; } /** * Returns the minor tick spacing. * * @return The minor tick spacing */ public double getMinorTickSpacingAsDouble() { return cMinorTickSpacing; } /** * Sets the slider orientation. * * @param vertical True for vertical, false for horizontal */ public void setVertical(boolean vertical) { setOrientation(vertical ? JSlider.VERTICAL : JSlider.HORIZONTAL); } /** * Returns the slider orientation. * * @return True for vertical, false for horizontal */ public boolean isVertical() { return getOrientation() == JSlider.VERTICAL; } /** * Overriiden to create labels * * @param paintLabels True if painting labels */ public void setPaintLabels(boolean paintLabels) { super.setPaintLabels(paintLabels); reCreateLabels(); } /** * If true then the value changes while dragging the slider, else * the value is set when the mouse is released. * * @param track True to track drags */ public void setTrackDrag(boolean track) { cTrackDrag = track; } /** * Returns the track drag. * * @return True to track drags */ public boolean isTrackDrag() { return cTrackDrag; } /** * Internally called to recreate the slider labels. */ private void reCreateLabels() { if (getPaintLabels()) { DecimalFormat mFormat; Dictionary mLabelTable = new Hashtable(20); String[] mLabels; if (cLabel != null && cLabel.length() > 0) { mLabels = Utils.split(cLabel, ';'); } else { mLabels = new String[0]; } double mIncrement = cMajorTickSpacing; if (mIncrement <= 0) { mIncrement = cMaximum - cMinimum; } else if ((cMaximum - cMinimum) / mIncrement > 200.) { mIncrement = cMaximum - cMinimum; } int mIndex = 0; for (double mLabelMajorTick = cMinimum; mLabelMajorTick <= cMaximum; mLabelMajorTick += mIncrement, mIndex++) { JLabel mLabel; String mLabelText; if (mLabels.length > mIndex) { mLabelText = mLabels[mIndex]; } else { try { mLabelText = Format.format(mLabelMajorTick, cFormat); } catch (FtException e) { mLabelText = "" + mLabelMajorTick; } } mLabel = new JLabel(mLabelText, JLabel.CENTER); mLabel.setFont(getFont()); mLabel.setForeground(getForeground()); mLabelTable.put((int) Math.floor((mLabelMajorTick - cMinimum) / (cMaximum - cMinimum) * PHYS_HIGH), mLabel); } setLabelTable(mLabelTable); } setMajorTickSpacing((int) Math.floor(cMajorTickSpacing / (cMaximum - cMinimum) * PHYS_HIGH)); setMinorTickSpacing((int) Math.floor(cMinorTickSpacing / (cMaximum - cMinimum) * PHYS_HIGH)); } /** * Sets the font. * * @param font The font */ public void setFont(Font font) { super.setFont(font); reCreateLabels(); } /** * Sets the foreground color. * * @param color The foreground color */ public void setForeground(Color color) { super.setForeground(color); reCreateLabels(); } /** * Sets the format for the labels. * * @param format The format string */ public void setFormat(String format) { if (format == null) { format = ""; } if (!cFormat.equals(format)) { String mOldFormat = cFormat; try { cFormat = format; reCreateLabels(); } catch (Exception e) { cFormat = mOldFormat; reCreateLabels(); } } } /** * Returns the format of the labels. * * @return The format string */ public String getFormat() { return cFormat; } /** * Sets the label. * * @param label The label */ public void setLabel(String label) { String mOldLabel = cLabel; try { cLabel = label; reCreateLabels(); } catch (Exception e) { cLabel = mOldLabel; reCreateLabels(); } } /** * Returns the label. * * @return The label */ public String getLabel() { return cLabel; } /** * Add a property listener. * * @param listener The property listener */ public void addPropertyChangeListener(PropertyChangeListener listener) { removeChangeListener(this); addChangeListener(this); super.addPropertyChangeListener(listener); } /** * Invoked when the target of the listener has changed its state. * * @param e a ChangeEvent object */ public void stateChanged(ChangeEvent e) { if (!getModel().getValueIsAdjusting() || cTrackDrag) { int mValue = getValue(); if (mValue != cOldValue && !cInitializing) { int mOldValue = cOldValue; cOldValue = mValue; firePropertyChange("ValueAsDouble", cnvToDouble(mOldValue), cnvToDouble(mValue)); fireActionEvent(); } cOldValue = mValue; } } /** * Converts the physical slider value to the double value. * If snapping is enabled, the value is also snapped to the nearest tick. * * @param value The physical slider value * @return The double value */ private double cnvToDouble(int value) { double mValue = value * (cMaximum - cMinimum) / PHYS_HIGH + cMinimum; if (getSnapToTicks()) { int mMajorTickSpacing = getMajorTickSpacing(); int mMinorTickSpacing = getMinorTickSpacing(); int mTickSpacing = 0; if (mMinorTickSpacing > 0) { mTickSpacing = mMinorTickSpacing; } else if (mMajorTickSpacing > 0) { mTickSpacing = mMajorTickSpacing; } if (mTickSpacing != 0) { double mDoubleTickSpacing = cMinorTickSpacing > 0 ? cMinorTickSpacing : cMajorTickSpacing; int mWhichTick = Math.round((float) value / (float) mTickSpacing); mValue = mWhichTick * mDoubleTickSpacing + cMinimum; } } return mValue; } /** * Add an action listener. * * @param actionListener The action listener */ public void addActionListener(IActionListener actionListener) { listenerList.add(IActionListener.class, actionListener); } /** * Removes an action listener. * * @param actionListener The action listener */ public void removeActionListener(IActionListener actionListener) { listenerList.remove(IActionListener.class, actionListener); } /** * Fires an action event. */ void fireActionEvent() { Object[] mListeners = listenerList.getListenerList(); for (int i = mListeners.length - 2; i >= 0; i -= 2) { if (mListeners[i] == IActionListener.class) { ((IActionListener) mListeners[i + 1]).actionPerformed(this); } } } public void setBounds(Rectangle r) { super.setBounds(r); updateUI(); } public boolean contains(Point2D p, AffineTransform t, GraphicHelper graphicHelper, float zoomFactor, boolean zoomEnabled) { if (isOpaque()) { Shape mShape = graphicHelper.getShape(); return mShape.contains(p); } float mFontSize = getFont().getSize2D(); float mComponentHeight = 24; Point2D mTopLeft = graphicHelper.getXYNull(); Point2D mBottomLeft = graphicHelper.getXYHeight(); Point2D mTopRight = graphicHelper.getXYWidth(); Point2D mBottomRight = graphicHelper.getXYWidthHeight(); float mDefinitionHeight = isVertical() ? (float) mTopLeft.distance(mTopRight) : (float) mTopLeft.distance(mBottomLeft); float mComponentToDefinitionRatio = ((mComponentHeight / mDefinitionHeight) / 2f) * zoomFactor; float mHeightOffSet = getPaintLabels() ? mFontSize / 2.0f + 4f : 0f; if (!zoomEnabled && t != null) { float mViewZoom = (float) t.getScaleX(); mComponentToDefinitionRatio /= mViewZoom; mHeightOffSet /= mViewZoom; } mComponentToDefinitionRatio = Math.min(mComponentToDefinitionRatio, 0.5f); float mHigh = (0.5f + mComponentToDefinitionRatio); float mLow = (0.5f - mComponentToDefinitionRatio); Point2D mNewTopLeft; Point2D mNewBottomLeft; Point2D mNewTopRight; Point2D mNewBottomRight; if (isVertical()) { mNewTopLeft = new Point2D.Double(mTopLeft.getX() + ((mTopRight.getX() - mTopLeft.getX()) * mLow) - mHeightOffSet, mTopLeft.getY() + ((mTopRight.getY() - mTopLeft.getY()) * mLow)); mNewTopRight = new Point2D.Double(mTopLeft.getX() + ((mTopRight.getX() - mTopLeft.getX()) * mHigh) - mHeightOffSet, mTopLeft.getY() + ((mTopRight.getY() - mTopLeft.getY()) * mHigh)); mNewBottomLeft = new Point2D.Double(mBottomLeft.getX() + ((mBottomRight.getX() - mBottomLeft.getX()) * mLow) - mHeightOffSet, mBottomLeft.getY() + ((mBottomRight.getY() - mBottomLeft.getY()) * mLow)); mNewBottomRight = new Point2D.Double(mBottomLeft.getX() + ((mBottomRight.getX() - mBottomLeft.getX()) * mHigh) - mHeightOffSet, mBottomLeft.getY() + ((mBottomRight.getY() - mBottomLeft.getY()) * mHigh)); } else { mNewTopLeft = new Point2D.Double(mTopLeft.getX() + ((mBottomLeft.getX() - mTopLeft.getX()) * mLow), mTopLeft.getY() + ((mBottomLeft.getY() - mTopLeft.getY()) * mLow) - mHeightOffSet); mNewBottomLeft = new Point2D.Double(mTopLeft.getX() + ((mBottomLeft.getX() - mTopLeft.getX()) * mHigh), mTopLeft.getY() + ((mBottomLeft.getY() - mTopLeft.getY()) * mHigh) - mHeightOffSet); mNewTopRight = new Point2D.Double(mTopRight.getX() + ((mBottomRight.getX() - mTopRight.getX()) * mLow), mTopRight.getY() + ((mBottomRight.getY() - mTopRight.getY()) * mLow) - mHeightOffSet); mNewBottomRight = new Point2D.Double(mTopRight.getX() + ((mBottomRight.getX() - mTopRight.getX()) * mHigh), mTopRight.getY() + ((mBottomRight.getY() - mTopRight.getY()) * mHigh) - mHeightOffSet); } Path2D.Double mShape = new Path2D.Double(Path2D.WIND_NON_ZERO, 4); mShape.moveTo(mNewTopLeft.getX(), mNewTopLeft.getY()); mShape.lineTo(mNewTopRight.getX(), mNewTopRight.getY()); mShape.lineTo(mNewBottomRight.getX(), mNewBottomRight.getY()); mShape.lineTo(mNewBottomLeft.getX(), mNewBottomLeft.getY()); return mShape.contains(p); } }