All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bidib.jbidibc.ui.LogarithmicJSlider Maven / Gradle / Ivy

/*
 * Copyright 2002-2004 Greg Hinkle
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bidib.jbidibc.ui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.swing.BoundedRangeModel;
import javax.swing.JLabel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.plaf.SliderUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicSliderUI;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This JSlider subclass uses a custom UI to allow a slider to work in logarithmic scale. Major and minor ticks are
 * drawn for logarithmic scale as well.
 * 
 * @author Greg Hinkle ([email protected]), Apr 10, 2004
 * @version $Revision: 480 $($Author: ghinkl $ / $Date: 2004-10-05 05:17:41 +0000 (Tue, 05 Oct 2004) $)
 */
public class LogarithmicJSlider extends JSlider {
    private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = LoggerFactory.getLogger(LogarithmicJSlider.class);

    public static final String UI_CLASS_WINDOWS = "org.bidib.jbidibc.ui.plaf.windows.LogWindowsSliderUI";

    public static final String UI_CLASS_METAL = "org.bidib.jbidibc.ui.plaf.metal.LogMetalSliderUI";

    public static final String UI_CLASS_SYNTH = "org.bidib.jbidibc.ui.plaf.synth.LogSynthSliderUI";

    public static final String UI_CLASS_DEFAULT = "org.bidib.jbidibc.ui.LogarithmicJSlider$LogSliderUI";

    public LogarithmicJSlider(int orientation) {
        super(orientation);
        prepareUiClass();
    }

    public LogarithmicJSlider(int min, int max) {
        super(min, max);
        prepareUiClass();
    }

    public LogarithmicJSlider(int min, int max, int value) {
        super(min, max, value);
        prepareUiClass();
    }

    public LogarithmicJSlider(int orientation, int min, int max, int value) {
        super(orientation, min, max, value);
        prepareUiClass();
    }

    public LogarithmicJSlider(BoundedRangeModel brm) {
        super(brm);
        prepareUiClass();
    }

    public LogarithmicJSlider() {
        prepareUiClass();
    }

    private void prepareUiClass() {
        SliderUI ui = null;
        if (OS.isWindows()) {
            try {
                Class clazz = Class.forName(UI_CLASS_WINDOWS);
                ui = (SliderUI) clazz.getConstructor(JSlider.class).newInstance(this);
            }
            catch (Exception ex) {
                LOGGER.warn("Create special sliderUI for windows failed, fallback to default LogSliderUI.", ex);
            }
        }
        else if (OS.isLinux()) {
            if ("Metal".equals(UIManager.getLookAndFeel().getName())) {
                LOGGER.trace("Linux is running with metal L&F.");
                try {
                    Class clazz = Class.forName(UI_CLASS_METAL);
                    ui = (SliderUI) clazz.getConstructor().newInstance();
                }
                catch (Exception ex) {
                    LOGGER.warn("Create special sliderUI for metal failed, fallback to default LogSliderUI.", ex);
                }
            }
            else if ("Synth".equals(UIManager.getLookAndFeel().getID())
                || "Nimbus".equals(UIManager.getLookAndFeel().getID())) {
                LOGGER.trace("Linux is running with synth L&F.");
                try {
                    Class clazz = Class.forName(UI_CLASS_SYNTH);
                    ui = (SliderUI) clazz.getConstructor(JSlider.class).newInstance(this);
                }
                catch (Exception ex) {
                    LOGGER.warn("Create special sliderUI for synth failed, fallback to default LogSliderUI.", ex);
                }
            }
        }

        if (ui == null) {
            ui = new LogSliderUI(this);
        }
        this.setUI(ui);
    }

    @Override
    public void updateUI() {

        prepareUiClass();

        // The labels preferred size may be derived from the font
        // of the slider, so we must update the UI of the slider first, then
        // that of labels. This way when setSize is called the right
        // font is used.
        updateLabelUIs();
    }

    public static class LogSliderUI extends BasicSliderUI {

        public LogSliderUI(JSlider b) {
            super(b);
        }

        @Override
        public int xPositionForValue(int value) {
            int min = slider.getMinimum();
            int max = slider.getMaximum();
            int trackLength = trackRect.width;
            double valueRange = Math.log(max) - Math.log(min);
            double pixelsPerValue = trackLength / valueRange;
            int trackLeft = trackRect.x;
            int trackRight = trackRect.x + (trackRect.width - 1);
            int xPosition;

            if (!drawInverted()) {
                xPosition = trackLeft;
                xPosition += Math.round(pixelsPerValue * (Math.log(value) - Math.log(min)));
            }
            else {
                xPosition = trackRight;
                xPosition -= Math.round(pixelsPerValue * (Math.log(value) - Math.log(min)));
            }

            xPosition = Math.max(trackLeft, xPosition);
            xPosition = Math.min(trackRight, xPosition);

            return xPosition;

        }

        @Override
        public int yPositionForValue(int value) {
            // TODO GH: Implement to support vertical log sliders
            return super.yPositionForValue(value);
        }

        @Override
        public int valueForYPosition(int yPos) {
            // TODO GH: Implement to support vertical log sliders
            return super.valueForYPosition(yPos);
        }

        @Override
        public int valueForXPosition(int xPos) {
            int value;
            final int minValue = slider.getMinimum();
            final int maxValue = slider.getMaximum();
            final int trackLength = trackRect.width;
            final int trackLeft = trackRect.x;
            final int trackRight = trackRect.x + (trackRect.width - 1);

            if (xPos <= trackLeft) {
                value = drawInverted() ? maxValue : minValue;
            }
            else if (xPos >= trackRight) {
                value = drawInverted() ? minValue : maxValue;
            }
            else {
                int distanceFromTrackLeft = xPos - trackLeft;
                double valueRange = Math.log(maxValue) - Math.log(minValue);
                // double valuePerPixel = (double)valueRange / (double)trackLength;
                // int valueFromTrackLeft =
                // (int)Math.round( Math.pow(3.5,(double)distanceFromTrackLeft * (double)valuePerPixel));

                int valueFromTrackLeft =
                    (int) Math
                        .round(Math
                            .pow(Math.E, Math.log(minValue) + (((distanceFromTrackLeft) * valueRange) / trackLength)));

                value = drawInverted() ? maxValue - valueFromTrackLeft : (int) Math.log(minValue) + valueFromTrackLeft;
            }

            return value;

        }

        @Override
        public void paintTicks(Graphics g) {
            Rectangle tickBounds = tickRect;

            g.setColor(Color.black);

            int maj = slider.getMajorTickSpacing();

            if (slider.getOrientation() == JSlider.HORIZONTAL) {
                g.translate(0, tickBounds.y);

                int value = slider.getMinimum();
                int xPos = 0;

                if (slider.getMinorTickSpacing() > 0) {
                    int majorValue = slider.getMinimum();

                    while (value <= slider.getMaximum()) {
                        if (value >= majorValue) {
                            value = majorValue;
                            majorValue *= maj;
                        }
                        value += (majorValue / 10.0);

                        xPos = xPositionForValue(value);
                        paintMinorTickForHorizSlider(g, tickBounds, xPos);

                    }
                }

                if (slider.getMajorTickSpacing() > 0) {
                    value = slider.getMinimum();

                    while (value <= slider.getMaximum()) {
                        xPos = xPositionForValue(value);
                        paintMajorTickForHorizSlider(g, tickBounds, xPos);
                        value *= slider.getMajorTickSpacing();
                    }
                }

                g.translate(0, -tickBounds.y);
            }
            else {

                g.translate(tickBounds.x, 0);

                int value = slider.getMinimum();
                int yPos = 0;

                if (slider.getMinorTickSpacing() > 0) {
                    int majorValue = slider.getMinimum();
                    int offset = 0;
                    if (!slider.getComponentOrientation().isLeftToRight()) {
                        offset = tickBounds.width - tickBounds.width / 2;
                        g.translate(offset, 0);
                    }

                    while (value <= slider.getMaximum()) {
                        if (value >= majorValue) {
                            value = majorValue;
                            majorValue *= maj;
                        }

                        yPos = yPositionForValue(value);
                        paintMinorTickForVertSlider(g, tickBounds, yPos);
                        value += (majorValue / 10.0);
                    }

                    if (!slider.getComponentOrientation().isLeftToRight()) {
                        g.translate(-offset, 0);
                    }
                }

                if (slider.getMajorTickSpacing() > 0) {
                    value = slider.getMinimum();
                    if (!slider.getComponentOrientation().isLeftToRight()) {
                        g.translate(2, 0);
                    }

                    while (value <= slider.getMaximum()) {
                        yPos = yPositionForValue(value);
                        paintMajorTickForVertSlider(g, tickBounds, yPos);
                        value *= slider.getMajorTickSpacing();
                    }

                    if (!slider.getComponentOrientation().isLeftToRight()) {
                        g.translate(-2, 0);
                    }
                }
                g.translate(-tickBounds.x, 0);
            }
        }

    }

    /**
     * Creates a hashtable holding the text labels for this slider. This implementation uses the increment as a
     * log-base.
     * 
     */
    @Override
    public Hashtable createStandardLabels(int increment, int start) {
        if (start > getMaximum() || start < getMinimum()) {
            throw new IllegalArgumentException("Slider label start point out of range.");
        }

        if (increment <= 0) {
            throw new IllegalArgumentException("Label incremement must be > 0");
        }

        class LabelHashtable extends Hashtable implements PropertyChangeListener {
            int increment = 0;

            int start = 0;

            boolean startAtMin = false;

            public LabelHashtable(int increment, int start) {
                super();
                this.increment = increment;
                this.start = start;
                startAtMin = start == getMinimum();
                createLabels(this, increment, start);
            }

            @Override
            public void propertyChange(PropertyChangeEvent e) {
                if (e.getPropertyName().equals("minimum") && startAtMin) {
                    start = getMinimum();
                }

                if (e.getPropertyName().equals("minimum") || e.getPropertyName().equals("maximum")) {

                    Enumeration keys = getLabelTable().keys();
                    Object key = null;
                    Hashtable hashtable = new Hashtable<>();

                    // Save the labels that were added by the developer
                    while (keys.hasMoreElements()) {
                        key = keys.nextElement();
                        Object value = getLabelTable().get(key);
                        if (!(value instanceof LabelUIResource)) {
                            hashtable.put(key, value);
                        }
                    }

                    clear();
                    createLabels(this, increment, start);

                    // Add the saved labels
                    keys = hashtable.keys();
                    while (keys.hasMoreElements()) {
                        key = keys.nextElement();
                        put(key, hashtable.get(key));
                    }
                    ((JSlider) e.getSource()).setLabelTable(this);
                }
            }
        }

        LabelHashtable table = new LabelHashtable(increment, start);

        if (getLabelTable() != null && (getLabelTable() instanceof PropertyChangeListener)) {
            removePropertyChangeListener((PropertyChangeListener) getLabelTable());
        }

        addPropertyChangeListener(table);

        return table;
    }

    /**
     * This method creates the table of labels that are used to label major ticks on the slider.
     * 
     * @param table
     * @param increment
     * @param start
     */
    protected void createLabels(Hashtable table, int increment, int start) {
        for (int labelIndex = start; labelIndex <= getMaximum(); labelIndex *= increment) {

            table.put(Integer.valueOf(labelIndex), new LabelUIResource("" + labelIndex, JLabel.CENTER));
        }
    }

    protected class LabelUIResource extends JLabel implements UIResource {
        public LabelUIResource(String text, int alignment) {
            super(text, alignment);
            setName("Slider.label");
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy