/******************************************************************************* * Copyright (c) 2011 Laurent CARON. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Laurent CARON (laurent.caron@gmail.com) - initial API and implementation * Generoso Pagano - improvements (grads number displaying) *******************************************************************************/ package fr.inria.soctrace.framesoc.ui.utils; import java.util.ArrayList; import java.util.List; import org.eclipse.jface.action.IStatusLineManager; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.mihalis.opal.utils.SWTGraphicUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import fr.inria.soctrace.lib.model.utils.ModelConstants.TimeUnit; import fr.inria.soctrace.lib.model.utils.TimestampFormat.TickDescriptor; import fr.inria.soctrace.lib.model.utils.TimestampFormat; /** * Instances of this class provide a slider with 2 buttons (min value, max value). * <p> * <dl> * <dt><b>Styles:</b></dt> * <dd>BORDER</dd> * <dd>HORIZONTAL</dd> * <dd>VERTICAL</dd> * * <dt><b>Events:</b></dt> * <dd>Selection</dd> * </dl> * </p> */ public class RangeSlider extends Canvas { private final static Logger logger = LoggerFactory.getLogger(RangeSlider.class); private static final int MIN_WIDTH = 100; private static final int MIN_HEIGHT = 32; private static final int BARHEIGHT = 10; private static final int BOTTOM = 5; private static final int BARSIZE = 5; private static final int NO_STATUS = -1; /** * Size in pixel of the bigger timestamp */ private static final int TIMESTAMP_MAX_SIZE = 100; private enum SELECTED_KNOB { NONE, UPPER, LOWER }; private long minimum; private long maximum; private long selectionLowerValue; private long selectionUpperValue; private final List<SelectionListener> listeners; private final Image slider, sliderHover, sliderDrag, sliderSelected; private final Image vSlider, vSliderHover, vSliderDrag, vSliderSelected; private int orientation; private long increment; private long pageIncrement; private SELECTED_KNOB lastSelected; private boolean dragInProgress; private Point coordUpper; private boolean upperHover; private Point coordLower; private boolean lowerHover; private long previousUpperValue; private long previousLowerValue; private boolean showGrads = false; private long cursorValue; private boolean mayShowTooltip; private IStatusLineManager statusLineManager; private TimeUnit unit; private TimestampFormat formatter = new TimestampFormat(); private long displayLowerValue; private long displayUpperValue; /** * Constructs a new instance of this class given its parent and a style value describing its * behavior and appearance. * <p> * The style value is either one of the style constants defined in class <code>SWT</code> which * is applicable to instances of this class, or must be built by <em>bitwise OR</em>'ing * together (that is, using the <code>int</code> "|" operator) two or more of those * <code>SWT</code> style constants. The class description lists the style constants that are * applicable to the class. Style bits are also inherited from superclasses. * </p> * * @param parent * a composite control which will be the parent of the new instance (cannot be null) * @param style * the style of control to construct * * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> * </ul> * @exception SWTException * <ul> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the parent</li> * </ul> * */ public RangeSlider(final Composite parent, final int style) { super(parent, SWT.DOUBLE_BUFFERED | ((style & SWT.BORDER) == SWT.BORDER ? SWT.BORDER : SWT.NONE)); this.unit = TimeUnit.UNKNOWN; this.cursorValue = 0; this.mayShowTooltip = false; this.minimum = this.selectionLowerValue = this.displayLowerValue = 0; this.maximum = this.selectionUpperValue = this.displayUpperValue = 100; this.listeners = new ArrayList<>(); this.increment = 1; this.pageIncrement = 10; this.lastSelected = SELECTED_KNOB.NONE; this.slider = new Image(getDisplay(), this.getClass().getClassLoader() .getResourceAsStream("images/slider-normal.png")); this.sliderHover = new Image(getDisplay(), this.getClass().getClassLoader() .getResourceAsStream("images/slider-hover.png")); this.sliderDrag = new Image(getDisplay(), this.getClass().getClassLoader() .getResourceAsStream("images/slider-drag.png")); this.sliderSelected = new Image(getDisplay(), this.getClass().getClassLoader() .getResourceAsStream("images/slider-selected.png")); this.vSlider = new Image(getDisplay(), this.getClass().getClassLoader() .getResourceAsStream("images/h-slider-normal.png")); this.vSliderHover = new Image(getDisplay(), this.getClass().getClassLoader() .getResourceAsStream("images/h-slider-hover.png")); this.vSliderDrag = new Image(getDisplay(), this.getClass().getClassLoader() .getResourceAsStream("images/h-slider-drag.png")); this.vSliderSelected = new Image(getDisplay(), this.getClass().getClassLoader() .getResourceAsStream("images/h-slider-selected.png")); if ((style & SWT.VERTICAL) == SWT.VERTICAL) { this.orientation = SWT.VERTICAL; } else { this.orientation = SWT.HORIZONTAL; } addListener(SWT.Dispose, new Listener() { @Override public void handleEvent(final Event event) { SWTGraphicUtil.dispose(RangeSlider.this.slider); SWTGraphicUtil.dispose(RangeSlider.this.sliderHover); SWTGraphicUtil.dispose(RangeSlider.this.sliderDrag); SWTGraphicUtil.dispose(RangeSlider.this.sliderSelected); SWTGraphicUtil.dispose(RangeSlider.this.vSlider); SWTGraphicUtil.dispose(RangeSlider.this.vSliderHover); SWTGraphicUtil.dispose(RangeSlider.this.vSliderDrag); SWTGraphicUtil.dispose(RangeSlider.this.vSliderSelected); } }); addMouseListeners(); addListener(SWT.KeyDown, new Listener() { @Override public void handleEvent(final Event event) { handleKeyDown(event); } }); addPaintListener(new PaintListener() { @Override public void paintControl(final PaintEvent e) { drawWidget(e); } }); } /** * Add the mouse listeners (mouse up, mouse down, mouse move, mouse wheel) */ private void addMouseListeners() { addListener(SWT.MouseDown, new Listener() { @Override public void handleEvent(final Event e) { handleMouseDown(e); } }); addListener(SWT.MouseUp, new Listener() { @Override public void handleEvent(final Event e) { handleMouseUp(e); } }); addListener(SWT.MouseMove, new Listener() { @Override public void handleEvent(final Event e) { handleMouseMove(e); } }); addListener(SWT.MouseWheel, new Listener() { @Override public void handleEvent(final Event e) { handleMouseWheel(e); } }); addListener(SWT.MouseEnter, new Listener() { @Override public void handleEvent(final Event e) { updateStatusLine(e.x); } }); addListener(SWT.MouseExit, new Listener() { @Override public void handleEvent(final Event e) { updateStatusLine(NO_STATUS); } }); } /** * Code executed when the mouse is down * * @param e * event */ private void handleMouseDown(final Event e) { logger.debug("mouse down {}", this); if (this.upperHover) { logger.debug("mouse down upper value {}", this.selectionUpperValue); this.dragInProgress = true; this.lastSelected = SELECTED_KNOB.UPPER; this.previousUpperValue = this.selectionUpperValue; return; } if (this.lowerHover) { logger.debug("mouse down lower value {}", this.selectionLowerValue); this.dragInProgress = true; this.lastSelected = SELECTED_KNOB.LOWER; this.previousLowerValue = this.selectionLowerValue; return; } int x = e.x; // compute distances to both knobs int lowerDist = Math.abs(x - coordLower.x); int upperDist = Math.abs(x - coordUpper.x); // compute new value long newValue = (long) ((x - 9f) / computePixelSizeForHorizonalSlider()) + this.minimum; // select minimal distance and update position of the corresponding // value if (lowerDist < upperDist) { selectionLowerValue = newValue; checkLowerValue(); } else { selectionUpperValue = newValue; checkUpperValue(); } redraw(); // Notify views fireSelectionListeners(e); this.dragInProgress = false; this.lastSelected = SELECTED_KNOB.NONE; } /** * Code executed when the mouse is up * * @param e * event */ private void handleMouseUp(final Event e) { if (!this.dragInProgress) { return; } this.dragInProgress = false; if (!fireSelectionListeners(e)) { if (this.lastSelected == SELECTED_KNOB.UPPER) { logger.debug("mouse up upper value before {}", this.selectionUpperValue); this.selectionUpperValue = this.previousUpperValue; logger.debug("mouse up upper value after {}", this.selectionUpperValue); } else { logger.debug("mouse up lower value before {}", this.selectionLowerValue); this.selectionLowerValue = this.previousLowerValue; logger.debug("mouse up lower value after {}", this.selectionLowerValue); } redraw(); } } /** * Fire all selection listeners * * @param event * selection event * @return <code>true</code> if no listener cancels the selection, <code>false</code> otherwise */ private boolean fireSelectionListeners(final Event event) { for (final SelectionListener selectionListener : this.listeners) { final SelectionEvent selectionEvent = new SelectionEvent(event); selectionListener.widgetSelected(selectionEvent); if (!selectionEvent.doit) { return false; } } return true; } /** * Code executed when the mouse pointer is moving * * @param e * event */ private void handleMouseMove(final Event e) { final int x = e.x, y = e.y; final Image img = this.orientation == SWT.HORIZONTAL ? this.slider : this.vSlider; this.upperHover = x >= this.coordUpper.x && x <= this.coordUpper.x + img.getBounds().width && y >= this.coordUpper.y && y <= this.coordUpper.y + img.getBounds().height; this.lowerHover = x >= this.coordLower.x && x <= this.coordLower.x + img.getBounds().width && y >= this.coordLower.y && y <= this.coordLower.y + img.getBounds().height; if (this.orientation == SWT.HORIZONTAL) { this.cursorValue = (long) ((x - 9f) / computePixelSizeForHorizonalSlider()) + this.minimum; } else { this.cursorValue = (long) ((y - 9f) / computePixelSizeForHorizonalSlider()) + this.minimum; } if (cursorValue <= this.maximum && cursorValue >= this.minimum) { this.mayShowTooltip = true; } if (this.dragInProgress) { if (this.orientation == SWT.HORIZONTAL) { final long mouseValue = (long) ((x - 9f) / computePixelSizeForHorizonalSlider()) + this.minimum; logger.debug("mouse value {}", mouseValue); if (this.lastSelected == SELECTED_KNOB.UPPER) { logger.debug("upper value before {}", this.selectionUpperValue); this.selectionUpperValue = (long) (Math.ceil(mouseValue / this.increment) * this.increment); logger.debug("upper value after {}", this.selectionUpperValue); checkUpperValue(); } else { logger.debug("lower value before {}", this.selectionLowerValue); this.selectionLowerValue = (long) (Math.ceil(mouseValue / this.increment) * this.increment); logger.debug("lower value after {}", this.selectionLowerValue); checkLowerValue(); } } else { final long mouseValue = (long) ((y - 9f) / computePixelSizeForVerticalSlider()) + this.minimum; if (this.lastSelected == SELECTED_KNOB.UPPER) { logger.debug("upper value before {}", this.selectionUpperValue); this.selectionUpperValue = (long) (Math.ceil(mouseValue / this.increment) * this.increment); logger.debug("upper value after {}", this.selectionUpperValue); checkUpperValue(); } else { logger.debug("lower value before {}", this.selectionLowerValue); this.selectionLowerValue = (long) (Math.ceil(mouseValue / this.increment) * this.increment); logger.debug("lower value after {}", this.selectionLowerValue); checkLowerValue(); } } fireSelectionListeners(e); } updateStatusLine(x); redraw(); } /** * Code executed when the mouse wheel is activated * * @param e * event */ private void handleMouseWheel(final Event e) { if (this.lastSelected == SELECTED_KNOB.NONE) { return; } if (this.lastSelected == SELECTED_KNOB.LOWER) { this.selectionLowerValue += e.count * this.increment; checkLowerValue(); redraw(); } else { this.selectionUpperValue += e.count * this.increment; checkUpperValue(); redraw(); } } /** * Check if the lower value is in ranges */ private void checkLowerValue() { logger.debug("to check: " + this.selectionLowerValue); if (this.selectionLowerValue < this.minimum) { this.selectionLowerValue = this.minimum; } if (this.selectionLowerValue > this.maximum) { this.selectionLowerValue = this.maximum; } if (this.selectionLowerValue > this.selectionUpperValue) { this.selectionLowerValue = this.selectionUpperValue; } logger.debug("checked: " + this.selectionLowerValue); } /** * Check if the upper value is in ranges */ private void checkUpperValue() { logger.debug("to check: " + this.selectionUpperValue); if (this.selectionUpperValue < this.minimum) { this.selectionUpperValue = this.minimum; } if (this.selectionUpperValue > this.maximum) { this.selectionUpperValue = this.maximum; } if (this.selectionUpperValue < this.selectionLowerValue) { this.selectionUpperValue = this.selectionLowerValue; } logger.debug("checked: " + this.selectionUpperValue); } /** * Draws the widget * * @param e * paint event */ private void drawWidget(final PaintEvent e) { final Rectangle rect = this.getClientArea(); if (rect.width == 0 || rect.height == 0) { return; } e.gc.setAdvanced(true); e.gc.setAntialias(SWT.ON); if (this.orientation == SWT.HORIZONTAL) { drawHorizontalRangeSlider(e.gc); } else { drawVerticalRangeSlider(e.gc); } if (this.mayShowTooltip) { // TODO: show tooltip (you have to manually print the rectangle and // the inner text) logger.debug("value under cursor: {}", cursorValue); } } /** * Draw the range slider (horizontal) * * @param gc * graphic context */ private void drawHorizontalRangeSlider(final GC gc) { drawBackgroundHorizontal(gc); drawBarsHorizontal(gc); this.coordUpper = drawHorizontalKnob(gc, this.selectionUpperValue - this.minimum, true); this.coordLower = drawHorizontalKnob(gc, this.selectionLowerValue - this.minimum, false); } /** * Draw the background * * TODO fix bugs in start x and similar * * @param gc * graphic context */ private void drawBackgroundHorizontal(final GC gc) { final Rectangle clientArea = this.getClientArea(); int ybar = clientArea.height - BARHEIGHT - BOTTOM; gc.setBackground(getBackground()); gc.fillRectangle(clientArea); if (isEnabled()) { gc.setForeground(getForeground()); } else { gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_GRAY)); } gc.drawRoundRectangle(9, ybar, clientArea.width - 20, BARHEIGHT, 3, 3); // Draw selection rectangle final double pixelSize = computePixelSizeForHorizonalSlider(); final int startDisplayX = (int) (pixelSize * (this.displayLowerValue - this.minimum)); final int endDisplayX = (int) (pixelSize * (this.displayUpperValue - this.minimum)); if (isEnabled()) { gc.setBackground(getForeground()); } else { gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GRAY)); } gc.fillRectangle(10 + startDisplayX, ybar, endDisplayX - startDisplayX, BARHEIGHT); // Display only if different from actual selection if (selectionLowerValue != displayLowerValue || selectionUpperValue != displayUpperValue) { final int startX = (int) (pixelSize * (this.selectionLowerValue - this.minimum)); final int endX = (int) (pixelSize * (this.selectionUpperValue - this.minimum)); if (isEnabled()) { gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY)); } else { gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GRAY)); } gc.setAlpha(150); gc.fillRectangle(6 + startX, ybar, endX - startX, BARHEIGHT); gc.setAlpha(255); } } /** * @return how many pixels corresponds to 1 point of value */ private double computePixelSizeForHorizonalSlider() { int width = getClientArea().width; double d = (width - 20f) / (this.maximum - this.minimum); return d; } /** * Draw the bars * * @param gc * graphic context */ private void drawBarsHorizontal(final GC gc) { if (isEnabled()) { gc.setForeground(getForeground()); } else { gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_GRAY)); } // to see the text gc.setBackground(getBackground()); // to have the same time unit formatter.setContext(this.minimum, this.maximum); final int numberOfTicksHint = Math.max(getSize().x / TIMESTAMP_MAX_SIZE, 1); final double pixelSize = computePixelSizeForHorizonalSlider(); TickDescriptor des = formatter.getTickDescriptor(this.minimum, this.maximum, numberOfTicksHint); long v = des.first; while (v < this.maximum) { final int x = (int) (9 + pixelSize * (v - this.minimum)); if (showGrads) { String value = formatter.format(v); gc.setFont(new Font(getDisplay(), gc.getFont().getFontData()[0].getName(), 8, SWT.NONE)); Point textSize = gc.textExtent(value); gc.drawText(value, x - textSize.x / 2, -2); } gc.drawLine(x, getClientArea().height - BARHEIGHT - BARSIZE - BOTTOM, x, getClientArea().height - BARHEIGHT - BOTTOM); v += des.delta; } } /** * Draws an horizontal knob * * @param gc * graphic context * @param value * corresponding value * @param upper * if <code>true</code>, draws the upper knob. If <code>false</code>, draws the lower * knob * @return the coordinate of the upper left corner of the knob */ private Point drawHorizontalKnob(final GC gc, final long value, final boolean upper) { final double pixelSize = computePixelSizeForHorizonalSlider(); final int x = (int) (pixelSize * value); Image image; if (upper) { if (this.upperHover) { image = this.dragInProgress ? this.sliderDrag : this.sliderHover; } else if (this.lastSelected == SELECTED_KNOB.UPPER) { image = this.sliderSelected; } else { image = this.slider; } } else { if (this.lowerHover) { image = this.dragInProgress ? this.sliderDrag : this.sliderHover; } else if (this.lastSelected == SELECTED_KNOB.LOWER) { image = this.sliderSelected; } else { image = this.slider; } } int yknob = getClientArea().height - BOTTOM + 1 - BARHEIGHT / 2 - this.slider.getBounds().height / 2; if (isEnabled()) { gc.drawImage(image, x + 5, yknob); } else { final Image temp = new Image(getDisplay(), image, SWT.IMAGE_DISABLE); gc.drawImage(temp, x + 5, yknob); temp.dispose(); } return new Point(x + 5, yknob); } /** * Draw the range slider (vertical) * * @param gc * graphic context */ private void drawVerticalRangeSlider(final GC gc) { drawBackgroundVertical(gc); drawBarsVertical(gc); this.coordUpper = drawVerticalKnob(gc, this.selectionUpperValue - this.minimum, true); this.coordLower = drawVerticalKnob(gc, this.selectionLowerValue - this.minimum, false); } /** * Draws the background * * @param gc * graphic context */ private void drawBackgroundVertical(final GC gc) { final Rectangle clientArea = this.getClientArea(); gc.setBackground(getBackground()); gc.fillRectangle(clientArea); if (isEnabled()) { gc.setForeground(getForeground()); } else { gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_GRAY)); } gc.drawRoundRectangle(9, 9, clientArea.width - 20, clientArea.height - 20, 3, 3); final double pixelSize = computePixelSizeForVerticalSlider(); final int startDisplayY = (int) (pixelSize * (this.displayLowerValue - this.minimum)); final int endDisplayY = (int) (pixelSize * (this.displayUpperValue - this.minimum)); if (isEnabled()) { gc.setBackground(getForeground()); } else { gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GRAY)); } gc.fillRectangle(12 + startDisplayY, 12 + startDisplayY, clientArea.width - 20, endDisplayY - startDisplayY - 6); // Display only if different from actual selection if (selectionLowerValue != displayLowerValue || selectionUpperValue != displayUpperValue) { final int startY = (int) (pixelSize * (this.selectionLowerValue - this.minimum)); final int endY = (int) (pixelSize * (this.selectionUpperValue - this.minimum)); if (isEnabled()) { gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY)); } else { gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GRAY)); } gc.setAlpha(150); gc.fillRectangle(9, 12 + startY, clientArea.width - 20, endY - startY - 6); gc.setAlpha(255); } } /** * @return how many pixels corresponds to 1 point of value */ private double computePixelSizeForVerticalSlider() { return (getClientArea().height - 20f) / (this.maximum - this.minimum); } /** * Draws the bars TODO: grads * * @param gc * graphic context */ private void drawBarsVertical(final GC gc) { final Rectangle clientArea = this.getClientArea(); if (isEnabled()) { gc.setForeground(getForeground()); } else { gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_GRAY)); } final double pixelSize = computePixelSizeForVerticalSlider(); for (int i = 1; i < 10; i++) { final int y = (int) (9 + pixelSize * (this.maximum - this.minimum) / 10f * i); gc.drawLine(4, y, 7, y); gc.drawLine(clientArea.width - 6, y, clientArea.width - 9, y); } } /** * Draws a vertical knob * * @param gc * graphic context * @param value * corresponding value * @param upper * if <code>true</code>, draws the upper knob. If <code>false</code>, draws the lower * knob * @return the coordinate of the upper left corner of the knob */ private Point drawVerticalKnob(final GC gc, final long value, final boolean upper) { final double pixelSize = computePixelSizeForVerticalSlider(); final int y = (int) (pixelSize * value); // XXX Image image; if (upper) { if (this.upperHover) { image = this.dragInProgress ? this.vSliderDrag : this.vSliderHover; } else if (this.lastSelected == SELECTED_KNOB.UPPER) { image = this.vSliderSelected; } else { image = this.vSlider; } } else { if (this.lowerHover) { image = this.dragInProgress ? this.vSliderDrag : this.vSliderHover; } else if (this.lastSelected == SELECTED_KNOB.LOWER) { image = this.vSliderSelected; } else { image = this.vSlider; } } if (isEnabled()) { gc.drawImage(image, getClientArea().width / 2 - 8, y + 2); } else { final Image temp = new Image(getDisplay(), image, SWT.IMAGE_DISABLE); gc.drawImage(temp, getClientArea().width / 2 - 8, y + 2); temp.dispose(); } return new Point(getClientArea().width / 2 - 8, y + 2); } /** * Code executed when a key is typed * * @param event * event */ private void handleKeyDown(final Event event) { boolean needRedraw = false; if (this.lastSelected == SELECTED_KNOB.NONE) { this.lastSelected = SELECTED_KNOB.LOWER; } logger.debug("upper value before {}", this.selectionUpperValue); logger.debug("lower value before {}", this.selectionLowerValue); switch (event.keyCode) { case SWT.HOME: if (this.lastSelected == SELECTED_KNOB.UPPER) { this.selectionUpperValue = this.minimum; } else { this.selectionLowerValue = this.minimum; } needRedraw = true; break; case SWT.END: if (this.lastSelected == SELECTED_KNOB.UPPER) { this.selectionUpperValue = this.maximum; } else { this.selectionLowerValue = this.maximum; } needRedraw = true; break; case SWT.PAGE_UP: if (this.lastSelected == SELECTED_KNOB.UPPER) { this.selectionUpperValue += this.pageIncrement; } else { this.selectionLowerValue += this.pageIncrement; } needRedraw = true; break; case SWT.PAGE_DOWN: if (this.lastSelected == SELECTED_KNOB.UPPER) { this.selectionUpperValue -= this.pageIncrement; } else { this.selectionLowerValue -= this.pageIncrement; } needRedraw = true; break; case SWT.ARROW_LEFT: case SWT.ARROW_UP: if (this.lastSelected == SELECTED_KNOB.UPPER) { this.selectionUpperValue -= this.increment; } else { this.selectionLowerValue -= this.increment; } needRedraw = true; break; case SWT.ARROW_RIGHT: case SWT.ARROW_DOWN: if (this.lastSelected == SELECTED_KNOB.UPPER) { this.selectionUpperValue += this.increment; } else { this.selectionLowerValue += this.increment; } needRedraw = true; break; } logger.debug("upper value after {}", this.selectionUpperValue); logger.debug("lower value after {}", this.selectionLowerValue); if (needRedraw) { if (this.lastSelected == SELECTED_KNOB.UPPER) { checkUpperValue(); } else { checkLowerValue(); } redraw(); } } /** * Get the time unit * * @return the time unit */ public TimeUnit getTimeUnit() { return unit; } /** * Set the time unit * * @param unit * unit to set */ public void setTimeUnit(TimeUnit unit) { this.unit = unit; this.formatter.setTimeUnit(unit); } /** * Adds the listener to the collection of listeners who will be notified when the user changes * the receiver's value, by sending it one of the messages defined in the * <code>SelectionListener</code> interface. * <p> * <code>widgetSelected</code> is called when the user changes the receiver's value. * <code>widgetDefaultSelected</code> is not called. * </p> * * @param listener * the listener which should be notified * * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> * * @see SelectionListener * @see #removeSelectionListener */ public void addSelectionListener(final SelectionListener listener) { checkWidget(); this.listeners.add(listener); } /** * @see org.eclipse.swt.widgets.Composite#computeSize(int, int, boolean) */ @Override public Point computeSize(final int wHint, final int hHint, final boolean changed) { // XXX final int width, height; checkWidget(); if (this.orientation == SWT.HORIZONTAL) { if (wHint < MIN_WIDTH) { width = MIN_WIDTH; } else { width = wHint; } if (hHint < MIN_HEIGHT) { height = MIN_HEIGHT; } else { height = hHint; } } else { if (wHint < MIN_HEIGHT) { width = MIN_HEIGHT; } else { width = wHint; } if (hHint < MIN_WIDTH) { height = MIN_WIDTH; } else { height = hHint; } } return new Point(width, height); } /** * Returns the amount that the selected receiver's value will be modified by when the up/down * (or right/left) arrows are pressed. * * @return the increment * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public long getIncrement() { checkWidget(); return this.increment; } /** * Returns the 'lower selection', which is the lower receiver's position. * * @return the selection * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public long getLowerValue() { checkWidget(); return this.selectionLowerValue; } /** * Returns the maximum value which the receiver will allow. * * @return the maximum * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public long getMaximum() { checkWidget(); return this.maximum; } /** * Returns the minimum value which the receiver will allow. * * @return the minimum * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public long getMinimum() { checkWidget(); return this.minimum; } /** * Returns the amount that the selected receiver's value will be modified by when the page * increment/decrement areas are selected. * * @return the page increment * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public long getPageIncrement() { checkWidget(); return this.pageIncrement; } /** * Returns the 'selection', which is an array where the first element is the lower selection, * and the second element is the upper selection * * @return the selection * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public long[] getSelection() { checkWidget(); final long[] selection = new long[2]; selection[0] = this.selectionLowerValue; selection[1] = this.selectionUpperValue; return selection; } /** * Returns the 'upper selection', which is the upper receiver's position. * * @return the selection * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public long getUpperValue() { checkWidget(); return this.selectionUpperValue; } /** * Removes the listener from the collection of listeners who will be notified when the user * changes the receiver's value. * * @param listener * the listener which should no longer be notified * * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> * </ul> * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> * * @see SelectionListener * @see #addSelectionListener */ public void removeSelectionListener(final SelectionListener listener) { checkWidget(); this.listeners.remove(listener); } /** * Sets the amount that the selected receiver's value will be modified by when the up/down (or * right/left) arrows are pressed to the argument, which must be at least one. * * @param increment * the new increment (must be greater than zero) * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public void setIncrement(final long increment) { checkWidget(); this.increment = increment; redraw(); } /** * Sets the 'lower selection', which is the receiver's lower value, to the argument which must * be greater than or equal to zero. * * @param value * the new selection (must be zero or greater) * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public void setLowerValue(final long value) { checkWidget(); if (this.minimum <= value && value <= this.maximum && value <= this.selectionUpperValue) { logger.debug("lower value before {}", this.selectionLowerValue); this.selectionLowerValue = value; logger.debug("lower value after {}", this.selectionLowerValue); } redraw(); } /** * Sets the maximum value that the receiver will allow. This new value will be ignored if it is * not greater than the receiver's current minimum value. If the new maximum is applied then the * receiver's selection value will be adjusted if necessary to fall within its new range. * * @param value * the new maximum, which must be greater than the current minimum * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public void setMaximum(final long value) { checkWidget(); if (this.minimum <= value) { this.maximum = value; if (this.selectionLowerValue >= this.maximum) { logger.debug("lower value before {}", this.selectionLowerValue); this.selectionLowerValue = this.maximum; logger.debug("lower value after {}", this.selectionLowerValue); } if (this.selectionUpperValue >= this.maximum) { logger.debug("upper value before {}", this.selectionUpperValue); this.selectionUpperValue = this.maximum; logger.debug("upper value after {}", this.selectionUpperValue); } } redraw(); } /** * Sets the minimum value that the receiver will allow. This new value will be ignored if it is * negative or is not less than the receiver's current maximum value. If the new minimum is * applied then the receiver's selection value will be adjusted if necessary to fall within its * new range. * * @param value * the new minimum, which must be nonnegative and less than the current maximum * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public void setMinimum(final long value) { checkWidget(); if (this.maximum >= value) { this.minimum = value; if (this.selectionLowerValue <= this.minimum) { logger.debug("lower value before {}", this.selectionLowerValue); this.selectionLowerValue = this.minimum; logger.debug("lower value after {}", this.selectionLowerValue); } if (this.selectionUpperValue <= this.minimum) { logger.debug("upper value before {}", this.selectionUpperValue); this.selectionUpperValue = this.minimum; logger.debug("upper value after {}", this.selectionUpperValue); } } redraw(); } /** * Sets the minimum and maximum values that the receiver will allow. This new values will be * ignored if the minimum is greater than the maximum. If the new values are applied then the * receiver's selection value will be adjusted if necessary to fall within its new range. * * @param min * the new minimum, which must be nonnegative and less than the maximum * @param max * the new maximum, which must be nonnegative and greater than the minimum * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public void setExtrema(final long min, final long max) { logger.debug("set extrema before {}", this); if (min < 0) return; if (max < min) return; checkWidget(); this.minimum = min; this.maximum = max; if (this.selectionLowerValue <= this.minimum) { logger.debug("lower value before {}", this.selectionLowerValue); this.selectionLowerValue = this.minimum; logger.debug("lower value after {}", this.selectionLowerValue); } if (this.selectionUpperValue <= this.minimum) { logger.debug("upper value before {}", this.selectionUpperValue); this.selectionUpperValue = this.minimum; logger.debug("upper value after {}", this.selectionUpperValue); } if (this.selectionLowerValue >= this.maximum) { logger.debug("lower value before {}", this.selectionLowerValue); this.selectionLowerValue = this.maximum; logger.debug("lower value after {}", this.selectionLowerValue); } if (this.selectionUpperValue >= this.maximum) { logger.debug("upper value before {}", this.selectionUpperValue); this.selectionUpperValue = this.maximum; logger.debug("upper value after {}", this.selectionUpperValue); } logger.debug("set extrema after {}", this); redraw(); } /** * Sets the amount that the receiver's value will be modified by when the page * increment/decrement areas are selected to the argument, which must be at least one. * * @param pageIncrement * the page increment (must be greater than zero) * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public void setPageIncrement(final long pageIncrement) { checkWidget(); this.pageIncrement = pageIncrement; } /** * Sets the 'selection', which is the receiver's value, to the argument which must be greater * than or equal to zero. * * @param values * the new selection (first value is lower value, second value is upper value) * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public void setSelection(final long[] values) { checkWidget(); setLowerValue(values[0]); setUpperValue(values[1]); checkUpperValue(); checkLowerValue(); redraw(); } /** * Sets the 'selection', which is the receiver's value, argument which must be greater than or * equal to zero. * * @param lowerValue * the new lower selection (must be zero or greater) * @param upperValue * the new upper selection (must be zero or greater) * @param notifyListeners * notify the selection listeners or not * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public void setSelection(final long lowerValue, final long upperValue, boolean notifyListeners) { checkWidget(); if (lowerValue >= upperValue) { logger.error("Invalid selection. Lower: " + lowerValue + ", Upper: " + upperValue); return; } if (this.minimum <= lowerValue && lowerValue <= this.maximum && this.minimum <= upperValue && upperValue <= this.maximum) { logger.debug("upper value before {}", this.selectionUpperValue); logger.debug("lower value before {}", this.selectionLowerValue); this.selectionLowerValue = lowerValue; this.selectionUpperValue = upperValue; logger.debug("upper value after {}", this.selectionUpperValue); logger.debug("lower value after {}", this.selectionLowerValue); } else { logger.error("Invalid selection. Lower: " + lowerValue + ", Upper: " + upperValue); return; } redraw(); if (notifyListeners) { Event e = new Event(); e.doit = true; e.widget = this; fireSelectionListeners(e); } } /** * Sets the 'upper selection', which is the upper receiver's value, argument which must be * greater than or equal to zero. * * @param value * the new selection (must be zero or greater) * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created * the receiver</li> * </ul> */ public void setUpperValue(final long value) { checkWidget(); if (this.minimum <= value && value <= this.maximum && value >= this.selectionLowerValue) { logger.debug("upper value before {}", this.selectionUpperValue); this.selectionUpperValue = value; logger.debug("upper value after {}", this.selectionUpperValue); } redraw(); } /** * XXX only implemented for HORIZONTAL slider * * @return the showGrads */ public boolean isShowGrads() { return showGrads; } /** * XXX only implemented for HORIZONTAL slider * * @param showGrads * the showGrads to set */ public void setShowGrads(boolean showGrads) { this.showGrads = showGrads; } @Override public String toString() { return "RangeSlider [minimum=" + minimum + ", maximum=" + maximum + ", lowerValue=" + selectionLowerValue + ", upperValue=" + selectionUpperValue + ", displayLowerValue=" + displayLowerValue + ", displayUpperValue=" + displayUpperValue + "]"; } public void setStatusLineManager(IStatusLineManager manager) { if (statusLineManager != null && manager == null) { statusLineManager.setMessage(""); //$NON-NLS-1$ } statusLineManager = manager; } private void updateStatusLine(int x) { if (statusLineManager == null) { return; } if (x == NO_STATUS) { statusLineManager.setMessage(""); return; } StringBuilder message = new StringBuilder(); if (!dragInProgress) { final long mouseValue = (long) ((x - 9f) / computePixelSizeForHorizonalSlider()) + this.minimum; message.append("T: "); //$NON-NLS-1$ message.append(formatter.format(mouseValue)); message.append(" "); } message.append("T1: "); //$NON-NLS-1$ message.append(formatter.format(this.selectionLowerValue)); message.append(" T2: "); //$NON-NLS-1$ message.append(formatter.format(this.selectionUpperValue)); message.append(" \u0394: "); //$NON-NLS-1$ message.append(formatter.format(Math.abs(this.selectionUpperValue - this.selectionLowerValue))); statusLineManager.setMessage(message.toString()); } /** * Set the display time interval, in order to display the currently display interval. Display * time bound are updated only if they are compliant with condition set in their setter * * @param startTimestamp * the starting timestamp of what is the currently display in the view * @param endTimestamp * the ending timestamps of what is currently display in the view */ public void setDisplayInterval(long startTimestamp, long endTimestamp) { setDisplayLowerValue(startTimestamp); setDisplayUpperValue(endTimestamp); redraw(); } public long getDisplayLowerValue() { return displayLowerValue; } public void setDisplayLowerValue(long value) { checkWidget(); if (this.minimum <= value && value <= this.maximum) { logger.debug("display lower value before {}", this.displayLowerValue); this.displayLowerValue = value; logger.debug("display lower value after {}", this.displayLowerValue); } redraw(); } public long getDisplayUpperValue() { return displayUpperValue; } public void setDisplayUpperValue(long value) { checkWidget(); if (this.minimum <= value && value <= this.maximum) { logger.debug("display upper value before {}", this.displayUpperValue); this.displayUpperValue = value; logger.debug("display upper value after {}", this.displayUpperValue); } redraw(); } }