Class AbstractMultiSlider<T>

java.lang.Object
java.awt.Component
java.awt.Container
javax.swing.JComponent
org.djutils.swing.multislider.AbstractMultiSlider<T>
Type Parameters:
T - the type of values that the AbstractMultiSlider stores and returns
All Implemented Interfaces:
ImageObserver, MenuContainer, Serializable
Direct Known Subclasses:
CategorialMultiSlider, LinearMultiSlider, MultiSlider

public abstract class AbstractMultiSlider<T> extends JComponent
The AbstractMultiSlider forms the base of implementing a slider with multiple thumbs. The AbstractMultiSlider is implemented by drawing a number of sliders on top of each other using an Swing OverlayManager, and passing the mouse events from a glass pane on top to the correct slider(s). The class is a ChangeListener to listen to the changes of individual sliders underneath.

Several models exist to indicate whether thumbs can pass each other or not, or be on top of each other or not.

The AbstractMultiSlider stores all values internally as int. Only when getting or setting values (or, e.g., the minimum or maximum), the generic type T is used.

Copyright (c) 2024-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See for project information https://djutils.org. The DJUTILS project is distributed under a three-clause BSD-style license, which can be found at https://djutils.org/docs/license.html.

Author:
Alexander Verbraeck
See Also:
  • Constructor Details

    • AbstractMultiSlider

      public AbstractMultiSlider(int minIndex, int maxIndex, boolean horizontal, int... initialIndexValues)
      Creates a slider with the specified orientation and the specified minimum, maximum, and initial values. The orientation can be either horizontal or vertical.
      Parameters:
      minIndex - the minimum index value of the slider
      maxIndex - the maximum index value of the slider
      horizontal - the orientation of the slider; true for horizontal, false for vertical
      initialIndexValues - the initial index values of the thumbs of the slider
      Throws:
      IllegalArgumentException - if initial values are outside the min-max range, or if the number of thumbs is 0, or when the values are not in increasing order (which is important for restricting passing and overlap)
  • Method Details

    • getSliders

      protected JSlider[] getSliders()
      Return the individual sliders, where slider[0] contains the formatting.
      Returns:
      the individual sliders
    • getSlider

      public JSlider getSlider(int i)
      Return an individual slider with index i.
      Parameters:
      i - the index for which to retrieve the slider
      Returns:
      the individual slider with index i
    • getNumberOfThumbs

      public int getNumberOfThumbs()
      Return the number of thumbs on this multislider.
      Returns:
      the number of thumbs on this multislider
    • setBusySlider

      protected void setBusySlider(int i)
      Indicate that slider i is busy (e.g., mouse-down or a drag operation).
      Parameters:
      i - the slider number that is busy
    • isBusySlider

      protected boolean isBusySlider(int i)
      Return whether slider i is busy (e.g., mouse-down or a drag operation).
      Parameters:
      i - the slider number to check
      Returns:
      whether slier i is busy or not
    • getBusySlider

      protected int getBusySlider()
      Return which slider is busy (e.g., mouse-down or a drag operation). The function returns -1 if no slider is busy.
      Returns:
      the slider number of the busy slider, or -1 if no slider is busy with an action
    • isBusy

      public boolean isBusy()
      Return whether one of the sliders is busy (e.g., mouse-down or a drag operation). Note that the 'busy' flag is set BEFORE the mouse event (e.g., mouse released, mouse exited) is handled. This means that when 'busy' is true, no operation is taking place.
      Returns:
      whether one of the sliders is busy or not
    • setBusy

      protected void setBusy(boolean busy)
      Set whether one of the sliders is busy (e.g., mouse-down or a drag operation). Note that the 'busy' flag has to be set BEFORE the mouse event (e.g., mouse released, mouse exited) is handled. This means that when 'busy' is true, no operation is taking place.
      Parameters:
      busy - set whether one of the sliders is busy or not
    • resetToInitialValues

      public void resetToInitialValues()
      Reset the slider values to the initial values.
    • getLabelPanel

      protected AbstractMultiSlider.LabelPanel getLabelPanel()
      Return the label panel in which thumb labels can be drawn. The labels move with the thumbs.
      Returns:
      the label panel in which thumb labels can be drawn
    • setThumbLabel

      public void setThumbLabel(int i, String label)
      Set the thumb label for thumb i to the given label.
      Parameters:
      i - the thumb number
      label - the label to display
      Throws:
      IndexOutOfBoundsException - when thumb number is out of bounds
    • getThumbLabel

      public String getThumbLabel(int i)
      Get the thumb label for thumb i.
      Parameters:
      i - the thumb number
      Returns:
      the label to display
      Throws:
      IndexOutOfBoundsException - when thumb number is out of bounds
    • setDrawThumbLabels

      public void setDrawThumbLabels(boolean b, int sizePx)
      Turn the thumb label display on or off.
      Parameters:
      b - whether the thumbs are displayed or not
      sizePx - the height (for a horizontal slider) or width (for a vertical slider) of the label panel in pixels
    • isDrawThumbLabels

      public boolean isDrawThumbLabels()
      Return whether thumb label display on or off.
      Returns:
      whether the thumbs are displayed or not
    • calculateTrackSize

      protected void calculateTrackSize()
      Recalculate the track size (width for horizontal slider; height for vertical slider) after a resize operation.
    • trackSize

      protected int trackSize()
      Calculate the track size (width for a horizontal slider; height for a vertical slider).
      Returns:
      the track size (width for a horizontal slider; height for a vertical slider
    • getTrackSizeLoPx

      public int getTrackSizeLoPx()
      Return the track size lowest pixel (to calculate width for horizontal slider; height for vertical slider).
      Returns:
      the track size lowest pixel
    • getTrackSizeHiPx

      public int getTrackSizeHiPx()
      Return the track size highest pixel (to calculate width for horizontal slider; height for vertical slider).
      Returns:
      the track size highest pixel
    • thumbPositionPx

      protected int thumbPositionPx(int i)
      Calculate x pixel (horizontal) or y pixel (vertical) of thumb[i], relative to the panel of the JSlider.
      Parameters:
      i - the slider number
      Returns:
      the x pixel (horizontal) or y pixel (vertical) of thumb[i], relative to the panel of the JSlider
    • getDispatcherPane

      protected AbstractMultiSlider.DispatcherPane getDispatcherPane()
      Return the glass pane on top of the multislider.
      Returns:
      the glass pane on top of the multislider
    • getUI

      public SliderUI getUI()
      Gets the UI object which implements the L&F for this component.
      Overrides:
      getUI in class JComponent
      Returns:
      the SliderUI object that implements the Slider L&F
    • setUI

      public void setUI(SliderUI ui)
      Sets the UI object which implements the L&F for all underlying sliders.
      Parameters:
      ui - the SliderUI L&F object
    • updateUI

      public void updateUI()
      Resets the UI property to a value from the current look and feel for all underlying sliders.
      Overrides:
      updateUI in class JComponent
    • getUIClassID

      public String getUIClassID()
      Returns the name of the L&F class that renders this component.
      Overrides:
      getUIClassID in class JComponent
      Returns:
      the string "SliderUI"
    • addChangeListener

      public void addChangeListener(ChangeListener listener)
      Adds a ChangeListener to the multislider.
      Parameters:
      listener - the ChangeListener to add
    • removeChangeListener

      public void removeChangeListener(ChangeListener listener)
      Removes a ChangeListener from the multislider.
      Parameters:
      listener - the ChangeListener to remove
    • getChangeListeners

      public ChangeListener[] getChangeListeners()
      Returns an array of all the ChangeListeners added to this MultiSlider with addChangeListener().
      Returns:
      all of the ChangeListeners added or an empty array if no listeners have been added
    • fireStateChanged

      protected void fireStateChanged()
      Send a ChangeEvent, whose source is this MultiSlider, to all ChangeListeners that have registered interest in ChangeEvents. This method is called each time a ChangeEvent is received from the model of one of the underlying sliders.

      The event instance is created if necessary, and stored in changeEvent.

    • addFinalValueChangeListener

      public void addFinalValueChangeListener(AbstractMultiSlider.FinalValueChangeListener listener)
      Adds a FinalValueChangeListener to the multislider.
      Parameters:
      listener - the FinalValueChangeListener to add
    • removeFinalValueChangeListener

      public void removeFinalValueChangeListener(AbstractMultiSlider.FinalValueChangeListener listener)
      Removes a FinalValueChangeListener from the multislider.
      Parameters:
      listener - the FinalValueChangeListener to remove
    • getFinalValueChangeListeners

      public AbstractMultiSlider.FinalValueChangeListener[] getFinalValueChangeListeners()
      Returns an array of all the FinalValueChangeListeners added to this MultiSlider with addFinalValueChangeListener().
      Returns:
      all of the FinalValueChangeListeners added or an empty array if no listeners have been added
    • fireFinalValueChanged

      protected void fireFinalValueChanged()
      Send a ChangeEvent, whose source is this MultiSlider, to all FinalValueChangeListeners that have registered interest in ChangeEvents. This method is called when a change is final, e.g., after setValue(...), setInitialValues(), mouse up, and leaving the slider window after a drag event. Note that the ChangeEvents are NOT fired when a value of an underlying slider is changed directly. The regular ChangeListener does fire these changes.

      The event instance is created if necessary, and stored in changeEvent.

    • mapIndexToValue

      protected abstract T mapIndexToValue(int index)
      Translate an index to a value.
      Parameters:
      index - the index on the slider scale to convert
      Returns:
      the corresponding value
    • mapValueToIndex

      protected abstract int mapValueToIndex(T value)
      Translate a value to an index.
      Parameters:
      value - the value to convert to an index
      Returns:
      the corresponding index
      Throws:
      IllegalArgumentException - when value cannot be mapped onto an index
    • getValue

      public T getValue(int i)
      Returns the slider's current value for slider[i].
      Parameters:
      i - the thumb to retrieve the value from
      Returns:
      the current value of slider[i]
      Throws:
      IllegalArgumentException - when no value is present for the index
    • setValue

      public void setValue(int i, T value)
      Sets the slider's current value to value. This method forwards the new value to the model. If the new value is different from the previous value, all change listeners are notified.
      Parameters:
      i - the thumb to set the value for
      value - the new value
    • getIndexValue

      public int getIndexValue(int i)
      Returns the slider's current index value for slider[i] from the BoundedRangeModel.
      Parameters:
      i - the thumb to retrieve the value from
      Returns:
      the current index value of slider[i]
    • setIndexValue

      protected void setIndexValue(int i, int n)
      Sets the slider's current index value to n. This method forwards the new value to the model.

      The data model (an instance of BoundedRangeModel) handles any mathematical issues arising from assigning faulty values. See the BoundedRangeModel documentation for details.

      If the new value is different from the previous value, all change listeners are notified.
      Parameters:
      i - the thumb to set the value for
      n - the new index value
    • getIndexMinimum

      protected int getIndexMinimum()
      Returns the minimum value supported by the slider from the BoundedRangeModel.
      Returns:
      the value of the model's minimum property
    • setIndexMinimum

      protected void setIndexMinimum(int minimum)
      Sets the slider's minimum value to minimum. This method forwards the new minimum value to the models of all underlying sliders.

      The data model (an instance of BoundedRangeModel) handles any mathematical issues arising from assigning faulty values. See the BoundedRangeModel documentation for details.

      If the new minimum value is different from the previous minimum value, all change listeners are notified.
      Parameters:
      minimum - the new minimum
    • getMinimum

      public T getMinimum()
      Return the minimum value supported by the multislider.
      Returns:
      the minimum typed value of the multislider
    • setMinimum

      public void setMinimum(T minimum)
      Set the minimum value supported by the multislider.
      Parameters:
      minimum - the new minimum typed value of the multislider
    • getIndexMaximum

      protected int getIndexMaximum()
      Returns the maximum value supported by the slider from the BoundedRangeModel.
      Returns:
      the value of the model's maximum property
    • setIndexMaximum

      protected void setIndexMaximum(int maximum)
      Sets the slider's maximum value to maximum. This method forwards the new maximum value to the models of all underlying sliders.

      The data model (an instance of BoundedRangeModel) handles any mathematical issues arising from assigning faulty values. See the BoundedRangeModel documentation for details.

      If the new maximum value is different from the previous maximum value, all change listeners are notified.
      Parameters:
      maximum - the new maximum
    • getMaximum

      public T getMaximum()
      Return the maximum value supported by the multislider.
      Returns:
      the maximum typed value of the multislider
    • setMaximum

      public void setMaximum(T maximum)
      Set the maximum value supported by the multislider.
      Parameters:
      maximum - the new maximum typed value of the multislider
    • getExtent

      public int getExtent()
      Returns the "extent" from the BoundedRangeModel. This represents the range of values "covered" by the thumb.
      Returns:
      an int representing the extent
    • setExtent

      public void setExtent(int extent)
      Sets the size of the range "covered" by the thumb for all underlying slider objects. Most look and feel implementations will change the value by this amount if the user clicks on either side of the thumb. This method just forwards the new extent value to the model.

      The data model (an instance of BoundedRangeModel) handles any mathematical issues arising from assigning faulty values. See the BoundedRangeModel documentation for details.

      If the new extent value is different from the previous extent value, all change listeners are notified.
      Parameters:
      extent - the new extent
    • getOrientation

      public int getOrientation()
      Return this multislider's vertical or horizontal orientation.
      Returns:
      SwingConstants.VERTICAL or SwingConstants.HORIZONTAL
    • isHorizontal

      public boolean isHorizontal()
      Return whether the orientation of the multislider is horizontal or not.
      Returns:
      true if the orientation of the multislider is horizontal, false when not.
    • isVertical

      public boolean isVertical()
      Return whether the orientation of the multislider is vertical or not.
      Returns:
      true if the orientation of the multislider is vertical , false when not.
    • setOrientation

      public void setOrientation(int orientation)
      Set the slider's orientation to either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
      Parameters:
      orientation - HORIZONTAL or VERTICAL
      Throws:
      IllegalArgumentException - if orientation is not one of VERTICAL, HORIZONTAL
    • setFont

      public void setFont(Font font)
      Overrides:
      setFont in class JComponent
    • getLabelTable

      public Dictionary getLabelTable()
      Returns the dictionary of what labels to draw at which values.
      Returns:
      the Dictionary containing labels and where to draw them
    • setLabelTable

      public void setLabelTable(Dictionary labels)
      Specify what label will be drawn at any given value. The key-value pairs are of this format: { Integer value, java.swing.JComponent label }. An easy way to generate a standard table of value labels is by using the createStandardLabels method.
      Parameters:
      labels - new Dictionary of labels, or null to remove all labels
    • createStandardLabels

      public Hashtable<Integer,JComponent> createStandardLabels(int increment)
      Creates a Hashtable of numerical text labels, starting at the slider minimum, and using the increment specified. For example, if you call createStandardLabels( 10 ) and the slider minimum is zero, then labels will be created for the values 0, 10, 20, 30, and so on.

      For the labels to be drawn on the slider, the returned Hashtable must be passed into setLabelTable, and setPaintLabels must be set to true.

      For further details on the makeup of the returned Hashtable, see the setLabelTable documentation.

      Parameters:
      increment - distance between labels in the generated hashtable
      Returns:
      a new Hashtable of labels
      Throws:
      IllegalArgumentException - if increment is less than or equal to zero
    • createStandardLabels

      public Hashtable<Integer,JComponent> createStandardLabels(int increment, int startIndex)
      Creates a Hashtable of text labels, starting at the starting point specified, and using the increment specified. For example, if you call createStandardLabels( 10, 2 ), then labels will be created for the index values 2, 12, 22, 32, and so on.

      For the labels to be drawn on the slider, the returned Hashtable must be passed into setLabelTable, and setPaintLabels must be set to true.

      For further details on the makeup of the returned Hashtable, see the setLabelTable documentation.

      Parameters:
      increment - distance between labels in the generated hashtable
      startIndex - value at which the labels will begin
      Returns:
      a new Hashtable of labels
      Throws:
      IllegalArgumentException - if start is out of range, or if increment is less than or equal to zero
    • format

      protected String format(T value)
      Format a value for e.g., the labels of the slider. By default, the formatting is done with toString(), but this can be overridden.
      Parameters:
      value - the value to format
      Returns:
      a formatted string representation of the value
    • getInverted

      public boolean getInverted()
      Returns true if the value-range shown for the slider is reversed.
      Returns:
      true if the slider values are reversed from their normal order
    • setInverted

      public void setInverted(boolean b)
      Specify true to reverse the value-range shown for the slider and false to put the value range in the normal order. The order depends on the slider's ComponentOrientation property. Normal (non-inverted) horizontal sliders with a ComponentOrientation value of LEFT_TO_RIGHT have their maximum on the right. Normal horizontal sliders with a ComponentOrientation value of RIGHT_TO_LEFT have their maximum on the left. Normal vertical sliders have their maximum on the top. These labels are reversed when the slider is inverted.

      By default, the value of this property is false.

      Parameters:
      b - true to reverse the slider values from their normal order
    • getMajorTickSpacing

      public int getMajorTickSpacing()
      This method returns the major tick spacing. The number that is returned represents the distance, measured in values, between each major tick mark. If you have a slider with a range from 0 to 50 and the major tick spacing is set to 10, you will get major ticks next to the following values: 0, 10, 20, 30, 40, 50.
      Returns:
      the number of values between major ticks
    • setMajorTickSpacing

      public void setMajorTickSpacing(int n)
      This method sets the major tick spacing. The number that is passed in represents the distance, measured in values, between each major tick mark. If you have a slider with a range from 0 to 50 and the major tick spacing is set to 10, you will get major ticks next to the following values: 0, 10, 20, 30, 40, 50.

      In order for major ticks to be painted, setPaintTicks must be set to true.

      This method will also set up a label table for you. If there is not already a label table, and the major tick spacing is > 0, and getPaintLabels returns true, a standard label table will be generated (by calling createStandardLabels) with labels at the major tick marks. For the example above, you would get text labels: "0", "10", "20", "30", "40", "50". The label table is then set on the slider by calling setLabelTable.
      Parameters:
      n - new value for the majorTickSpacing property
    • getMinorTickSpacing

      public int getMinorTickSpacing()
      This method returns the minor tick spacing. The number that is returned represents the distance, measured in values, between each minor tick mark. If you have a slider with a range from 0 to 50 and the minor tick spacing is set to 10, you will get minor ticks next to the following values: 0, 10, 20, 30, 40, 50.
      Returns:
      the number of values between minor ticks
    • setMinorTickSpacing

      public void setMinorTickSpacing(int n)
      This method sets the minor tick spacing. The number that is passed in represents the distance, measured in values, between each minor tick mark. If you have a slider with a range from 0 to 50 and the minor tick spacing is set to 10, you will get minor ticks next to the following values: 0, 10, 20, 30, 40, 50.

      In order for minor ticks to be painted, setPaintTicks must be set to true.

      Parameters:
      n - new value for the minorTickSpacing property
      See Also:
    • getSnapToTicks

      public boolean getSnapToTicks()
      Returns true if the thumb (and the data value it represents) resolve to the closest tick mark next to where the user positioned the thumb.
      Returns:
      true if the value snaps to the nearest tick mark, else false
    • setSnapToTicks

      public void setSnapToTicks(boolean b)
      Specifying true makes the thumb (and the data value it represents) resolve to the closest tick mark next to where the user positioned the thumb. By default, this property is false.
      Parameters:
      b - true to snap the thumb to the nearest tick mark
    • getPaintTicks

      public boolean getPaintTicks()
      Tells if tick marks are to be painted.
      Returns:
      true if tick marks are painted, else false
    • setPaintTicks

      public void setPaintTicks(boolean b)
      Determines whether tick marks are painted on the slider. By default, this property is false.
      Parameters:
      b - whether or not tick marks should be painted
    • getPaintTrack

      public boolean getPaintTrack()
      Tells if the track (area the slider slides in) is to be painted.
      Returns:
      true if track is painted, else false
    • setPaintTrack

      public void setPaintTrack(boolean b)
      Determines whether the track is painted on the slider. By default, this property is true. It is up to the look and feel to honor this property, some may choose to ignore it.
      Parameters:
      b - whether or not to paint the slider track
      See Also:
    • getPaintLabels

      public boolean getPaintLabels()
      Tells if labels are to be painted.
      Returns:
      true if labels are painted, else false
    • setPaintLabels

      public void setPaintLabels(boolean b)
      Determines whether labels are painted on the slider.

      This method will also set up a label table for you. If there is not already a label table, and the major tick spacing is > 0, a standard label table will be generated (by calling createStandardLabels) with labels at the major tick marks. The label table is then set on the slider by calling setLabelTable.

      By default, this property is false.
      Parameters:
      b - whether or not to paint labels
    • setPassing

      public void setPassing(boolean b)
      Set whether passing of the thumbs is allowed, and check whether thumb values are in line with restrictions.
      Parameters:
      b - whether passing of the thumbs is allowed or not
    • getPassing

      public boolean getPassing()
      Return whether passing of the thumbs is allowed.
      Returns:
      whether passing of the thumbs is allowed or not
    • setOverlap

      public void setOverlap(boolean b)
      Set whether overlap of the thumbs is allowed, and check whether thumb values are in line with restrictions.
      Parameters:
      b - whether overlap of the thumbs is allowed or not
    • getOverlap

      public boolean getOverlap()
      Return whether overlap of the thumbs is allowed.
      Returns:
      whether overlap of the thumbs is allowed or not
    • checkRestrictions

      protected boolean checkRestrictions()
      Check restrictions on all thumb values and correct values where necessary.
      Returns:
      whether compliance with the restrictions is ok; false means violation
    • checkRestrictions

      protected boolean checkRestrictions(int index)
      Check restrictions on the thumb values of thumb 'index' and correct values where necessary.
      Parameters:
      index - the slider for which to check (the only one whose value should change)
      Returns:
      whether compliance with the restrictions is ok; false means violation