diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/WaveformView.java b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/WaveformView.java
index bbd9525..0deaa44 100644
--- a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/WaveformView.java
+++ b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/WaveformView.java
@@ -56,13 +56,13 @@ import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
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.graphics.TextLayout;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
@@ -71,7 +71,6 @@ import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.ScrollBar;
-import org.eclipse.swt.widgets.Slider;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.wb.swt.SWTResourceManager;
@@ -94,6 +93,8 @@ import com.minres.scviewer.database.ui.IWaveformStyleProvider;
import com.minres.scviewer.database.ui.IWaveformView;
import com.minres.scviewer.database.ui.IWaveformZoom;
import com.minres.scviewer.database.ui.TrackEntry;
+import com.minres.scviewer.database.ui.swt.internal.slider.ImageButton;
+import com.minres.scviewer.database.ui.swt.internal.slider.RangeSlider;
import com.minres.scviewer.database.ui.swt.sb.FlatScrollBar;
public class WaveformView implements IWaveformView {
@@ -345,11 +346,37 @@ public class WaveformView implements IWaveformView {
waveformCanvas = new WaveformCanvas(waveformPane, SWT.NONE | SWT.V_SCROLL /*| SWT.H_SCROLL*/, styleProvider);
waveformCanvas.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
- Composite timeSliderPane = new WaveformSlider(waveformPane, SWT.NONE);
- GridData gd_timeSlider = new GridData(SWT.FILL, SWT.BOTTOM, false, false, 1, 1);
- gd_timeSlider.heightHint = 18;
- timeSliderPane.setLayoutData(gd_timeSlider);
+ //Composite timeSliderPane = new WaveformSlider(waveformPane, SWT.NONE);
+ Composite timeSliderPane = new Composite(waveformPane, SWT.NONE);
+// timeSliderPane.setBackground(SWTResourceManager.getColor(SWT.COLOR_RED));
+ GridData gd_timeSliderPane = new GridData(SWT.FILL, SWT.BOTTOM, false, false, 1, 1);
+// gd_timeSliderPane.heightHint = 22;
+ timeSliderPane.setLayoutData(gd_timeSliderPane);
+ GridLayout gl_timeSliderPane = new GridLayout(3, false);
+ gl_timeSliderPane.marginHeight=0;
+ gl_timeSliderPane.marginWidth=0;
+ gl_timeSliderPane.horizontalSpacing=0;
+ gl_timeSliderPane.verticalSpacing=0;
+ timeSliderPane.setLayout(gl_timeSliderPane);
+ ImageButton b1 = new ImageButton(timeSliderPane, SWT.NONE);
+ GridData gd_b1 = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
+ gd_b1.widthHint=18;
+ gd_b1.heightHint=18;
+ b1.setLayoutData(gd_b1);
+ b1.setImage(SWTResourceManager.getImage(RangeSlider.class, "bullet_left.png"));
+
+ Composite timeSlider = new RangeSlider(timeSliderPane, SWT.ON|SWT.HIGH|SWT.SMOOTH|SWT.CONTROL);
+ GridData gd_timeSlide = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
+ timeSlider.setLayoutData(gd_timeSlide);
+
+ ImageButton b2 = new ImageButton(timeSliderPane, SWT.NONE);
+ GridData gd_b2 = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
+ gd_b2.widthHint=18;
+ gd_b2.heightHint=18;
+ b2.setLayoutData(gd_b2);
+ b2.setImage(SWTResourceManager.getImage(RangeSlider.class, "bullet_right.png"));
+
// create the name pane
createTextPane(namePane, "Name");
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/ImageButton.java b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/ImageButton.java
new file mode 100644
index 0000000..21483d6
--- /dev/null
+++ b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/ImageButton.java
@@ -0,0 +1,129 @@
+package com.minres.scviewer.database.ui.swt.internal.slider;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.graphics.Transform;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+
+public class ImageButton extends Composite
+{
+ private Color textColor;
+ private Image image;
+ private Image grayImage;
+ private ImageData imageData;
+ private String text = "";
+ private int width;
+ private int height;
+ private boolean hover;
+
+ public ImageButton(Composite parent, int style)
+ {
+ super(parent, style);
+
+ textColor = Display.getDefault().getSystemColor(SWT.COLOR_WHITE);
+
+ /* Add dispose listener for the image */
+ addListener(SWT.Dispose, event -> {
+ if (image != null)
+ image.dispose();
+ });
+
+ /* Add custom paint listener that paints the stars */
+ addListener(SWT.Paint, event -> {
+ paintControl(event);
+ });
+
+ /* Listen for click events */
+ addListener(SWT.MouseDown, event -> {
+ System.out.println("Click");
+ });
+ addListener(SWT.MouseDown, event -> {
+ });
+
+ addListener(SWT.MouseUp, event -> {
+ });
+
+ addListener(SWT.MouseMove, event -> {
+ hover=false;
+ redraw();
+ });
+
+ addListener(SWT.MouseWheel, event -> {
+ });
+
+ addListener(SWT.MouseHover, event -> {
+ hover=true;
+ redraw();
+ });
+
+ addListener(SWT.MouseDoubleClick, event -> {
+ });
+ }
+
+ private void paintControl(Event event) {
+ GC gc = event.gc;
+
+ if (image != null)
+ {
+// gc.drawImage(image, 1, 1);
+// if(hover) {
+// Rectangle rect = image.getBounds ();
+// Transform tr = new Transform (event.display);
+// tr.setElements (1, 0, 0, -1, 1, 2*(1+rect.height));
+// gc.setTransform (tr);
+// gc.drawImage (image, 1, 1);
+// gc.setTransform (null);
+// }
+ if(hover) {
+ gc.drawImage(image, 1, 1);
+ } else {
+ gc.drawImage(grayImage, 1, 1);
+ }
+ Point textSize = gc.textExtent(text);
+ gc.setForeground(textColor);
+ gc.drawText(text, (width - textSize.x) / 2 + 1, (height - textSize.y) / 2 + 1, true);
+ }
+ }
+
+ public void setImage(Image img)
+ {
+ image = new Image(Display.getDefault(), img, SWT.IMAGE_COPY);
+ grayImage = new Image(Display.getDefault(),img,SWT.IMAGE_GRAY);
+ width = img.getBounds().width;
+ height = img.getBounds().height;
+ imageData = img.getImageData();
+ redraw();
+ }
+
+ public void setText(String text)
+ {
+ this.text = text;
+ redraw();
+ }
+
+ @Override
+ public Point computeSize(int wHint, int hHint, boolean changed)
+ {
+ int overallWidth = width;
+ int overallHeight = height;
+
+ /* Consider hints */
+ if (wHint != SWT.DEFAULT && wHint < overallWidth)
+ overallWidth = wHint;
+
+ if (hHint != SWT.DEFAULT && hHint < overallHeight)
+ overallHeight = hHint;
+
+ /* Return computed dimensions plus border */
+ return new Point(overallWidth + 2, overallHeight + 2);
+ }
+
+}
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/RangeSlider.java b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/RangeSlider.java
new file mode 100644
index 0000000..d146b53
--- /dev/null
+++ b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/RangeSlider.java
@@ -0,0 +1,1453 @@
+package com.minres.scviewer.database.ui.swt.internal.slider;
+
+import java.text.Format;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.SelectionListener;
+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.Widget;
+import org.eclipse.wb.swt.SWTResourceManager;
+
+/**
+ * Instances of this class provide a slider with two thumbs to control lower and
+ * upper integer values.
+ *
+ *
+ * - Styles:
+ * - BORDER
+ * - HORIZONTAL
+ * - VERTICAL
+ * - CONTROL - Allow key and mouse manipulation to control both lower and
+ * upper value thumbs simultaneously
+ * - ON - Indicates that selection listeners are notified continuously during
+ * thumb drag events. Otherwise, notification occurs only after the drag event
+ * terminates.
+ * - HIGH - Indicates high quality tick marks are generated dynamically to a
+ * factor of the pageIncrement or increment. Otherwise, tick marks divide the
+ * scale evenly into ten parts.
+ * - SMOOTH - Indicates mouse manipulation of upper and lower values are
+ * computed smoothly from the exact mouse cursor position disregarding the
+ * increment value. Otherwise, values are constrained to an incremental
+ * value.
+ * - Events:
+ * - Selection
+ *
+ *
+ *
+ * Note: Styles HORIZONTAL and VERTICAL are mutually exclusive.
+ *
+ */
+public class RangeSlider extends Canvas {
+
+ private static final byte NONE = 0;
+ private static final byte UPPER = 1 << 0;
+ private static final byte LOWER = 1 << 1;
+ private static final byte BOTH = UPPER | LOWER;
+
+ private static int minWidth = 18;
+ private static int minHeight = 18;
+ private static int imgWidth = 8;
+ private int minimum;
+ private int maximum;
+ private int lowerValue;
+ private int upperValue;
+ private final Image slider, sliderHover, sliderDrag, sliderSelected;
+ private final Image vSlider, vSliderHover, vSliderDrag, vSliderSelected;
+ private int orientation;
+ private int increment;
+ private int pageIncrement;
+ private byte selectedElement, priorSelectedElement;
+ private boolean dragInProgress;
+ private boolean upperHover, lowerHover;
+ private int previousUpperValue, previousLowerValue;
+ private int startDragUpperValue, startDragLowerValue;
+ private Point startDragPoint;
+ private boolean hasFocus;
+ private final boolean isSmooth;
+ private final boolean isFullSelection;
+ private final boolean isHighQuality;
+ private final boolean isOn;
+ private Format toolTipFormatter;
+ private String clientToolTipText;
+
+ /**
+ * Constructs a new instance of this class given its parent and a style value
+ * describing its behavior and appearance.
+ *
+ * The style value is either one of the style constants defined in class
+ * SWT
which is applicable to instances of this class, or must be
+ * built by bitwise OR'ing together (that is, using the
+ * int
"|" operator) two or more of those SWT
style
+ * constants. The class description lists the style constants that are
+ * applicable to the class. Style bits are also inherited from superclasses.
+ *
+ *
+ * @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. Default style is HORIZONTAL
+ *
+ * @exception IllegalArgumentException
+ *
+ * - ERROR_NULL_ARGUMENT - if the parent is null
+ *
+ * @exception SWTException
+ *
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the parent
+ *
+ * @see SWT#HORIZONTAL
+ * @see SWT#VERTICAL
+ * @see Widget#getStyle
+ *
+ */
+ public RangeSlider(final Composite parent, final int style) {
+ super(parent, SWT.DOUBLE_BUFFERED | ((style & SWT.BORDER) == SWT.BORDER ? SWT.BORDER : SWT.NONE));
+ minimum = lowerValue = 0;
+ maximum = upperValue = 100;
+ increment = 1;
+ pageIncrement = 10;
+ slider = SWTResourceManager.getImage(this.getClass(), "marker_r.png");
+ sliderHover = SWTResourceManager.getImage(this.getClass(), "marker_r_lt.png");
+ sliderDrag = SWTResourceManager.getImage(this.getClass(), "marker_r_bl.png");
+ sliderSelected = SWTResourceManager.getImage(this.getClass(), "marker_r_bl_lt.png");
+
+ vSlider = SWTResourceManager.getImage(this.getClass(), "h-slider-normal.png");
+ vSliderHover = SWTResourceManager.getImage(this.getClass(), "h-slider-hover.png");
+ vSliderDrag = SWTResourceManager.getImage(this.getClass(), "h-slider-drag.png");
+ vSliderSelected = SWTResourceManager.getImage(this.getClass(), "h-slider-selected.png");
+
+ if ((style & SWT.VERTICAL) == SWT.VERTICAL) {
+ orientation = SWT.VERTICAL;
+ } else {
+ orientation = SWT.HORIZONTAL;
+ }
+ isSmooth = (style & SWT.SMOOTH) == SWT.SMOOTH;
+ isFullSelection = (style & SWT.CONTROL) == SWT.CONTROL;
+ isHighQuality = (style & SWT.HIGH) == SWT.HIGH;
+ isOn = (style & SWT.ON) == SWT.ON;
+ selectedElement = isFullSelection ? BOTH : LOWER;
+
+// addListener(SWT.Dispose, event -> {
+// SWTResourceManager.dsafeDispose(slider);
+// SWTGraphicUtil.safeDispose(sliderHover);
+// SWTGraphicUtil.safeDispose(sliderDrag);
+// SWTGraphicUtil.safeDispose(sliderSelected);
+//
+// SWTGraphicUtil.safeDispose(vSlider);
+// SWTGraphicUtil.safeDispose(vSliderHover);
+// SWTGraphicUtil.safeDispose(vSliderDrag);
+// SWTGraphicUtil.safeDispose(vSliderSelected);
+// });
+ addMouseListeners();
+ addListener(SWT.Resize, event -> {
+ });
+ addListener(SWT.FocusIn, e -> {
+ hasFocus = true;
+ redraw();
+ });
+ addListener(SWT.FocusOut, e -> {
+ hasFocus = false;
+ redraw();
+ });
+ addListener(SWT.KeyDown, event -> {
+ handleKeyDown(event);
+ });
+ addPaintListener(event -> {
+ drawWidget(event);
+ });
+ }
+
+ @Override
+ public int getStyle() {
+ return super.getStyle() | orientation | (isSmooth ? SWT.SMOOTH : SWT.NONE) | //
+ (isOn ? SWT.ON : SWT.NONE) | //
+ (isFullSelection ? SWT.CONTROL : SWT.NONE) | //
+ (isHighQuality ? SWT.HIGH : SWT.NONE);
+ }
+
+ /**
+ * Add the mouse listeners (mouse up, mouse down, mouse move, mouse wheel)
+ */
+ private void addMouseListeners() {
+ addListener(SWT.MouseDown, event -> {
+ handleMouseDown(event);
+ });
+
+ addListener(SWT.MouseUp, event -> {
+ handleMouseUp(event);
+ });
+
+ addListener(SWT.MouseMove, event -> {
+ handleMouseMove(event);
+ });
+
+ addListener(SWT.MouseWheel, event -> {
+ handleMouseWheel(event);
+ });
+
+ addListener(SWT.MouseHover, event -> {
+ handleMouseHover(event);
+ });
+
+ addListener(SWT.MouseDoubleClick, event -> {
+ handleMouseDoubleClick(event);
+ });
+ }
+
+ /**
+ * Code executed when the mouse is down
+ *
+ * @param e event
+ */
+ private void handleMouseDown(final Event e) {
+ selectKnobs(e);
+ if (e.count == 1) {
+ priorSelectedElement = selectedElement;
+ }
+ if (upperHover || lowerHover) {
+ selectedElement = isFullSelection && lowerHover && upperHover ? BOTH : lowerHover ? LOWER : upperHover ? UPPER : selectedElement;
+ dragInProgress = true;
+ startDragLowerValue = previousLowerValue = lowerValue;
+ startDragUpperValue = previousUpperValue = upperValue;
+ startDragPoint = new Point(e.x, e.y);
+ }
+ }
+
+ /**
+ * Code executed when the mouse is up
+ *
+ * @param e event
+ */
+ private void handleMouseUp(final Event e) {
+ if (dragInProgress) {
+ startDragPoint = null;
+ validateNewValues(e);
+ dragInProgress = false;
+ super.setToolTipText(clientToolTipText);
+ }
+ }
+
+ /**
+ * invoke selection listeners if either upper or lower value has changed. if
+ * listeners reject the change, restore the previous values. redraw if either
+ * upper or lower value has changed.
+ *
+ * @param e event
+ */
+ private void validateNewValues(final Event e) {
+ if (upperValue != previousUpperValue || lowerValue != previousLowerValue) {
+ if (!SelectionListenerUtil.fireSelectionListeners(this,e)) {
+ upperValue = previousUpperValue;
+ lowerValue = previousLowerValue;
+ }
+ previousUpperValue = upperValue;
+ previousLowerValue = lowerValue;
+ redraw();
+ }
+ }
+
+
+ /**
+ * Code executed when the mouse pointer is moving
+ *
+ * @param e event
+ */
+ private void handleMouseMove(final Event e) {
+ if (!dragInProgress) {
+ final boolean wasUpper = upperHover;
+ final boolean wasLower = lowerHover;
+ selectKnobs(e);
+ if (wasUpper != upperHover || wasLower != lowerHover) {
+ redraw();
+ }
+ } else { // dragInProgress
+ final int x = e.x, y = e.y;
+ if (orientation == SWT.HORIZONTAL) {
+ if (selectedElement == BOTH) {
+ final int diff = (int) ((startDragPoint.x - x) / computePixelSizeForHorizontalSlider()) + minimum;
+ int newUpper = startDragUpperValue - diff;
+ int newLower = startDragLowerValue - diff;
+ if (newUpper > maximum) {
+ newUpper = maximum;
+ newLower = maximum - (startDragUpperValue - startDragLowerValue);
+ } else if (newLower < minimum) {
+ newLower = minimum;
+ newUpper = minimum + startDragUpperValue - startDragLowerValue;
+ }
+ upperValue = newUpper;
+ lowerValue = newLower;
+ if (!isSmooth) {
+ lowerValue = (int) (Math.ceil(lowerValue / increment) * increment) - increment;
+ upperValue = (int) (Math.ceil(upperValue / increment) * increment) - increment;
+ }
+ handleToolTip(lowerValue, upperValue);
+ } else if ((selectedElement & UPPER) != 0) {
+ upperValue = (int) Math.round((x - 9d) / computePixelSizeForHorizontalSlider()) + minimum;
+ if (!isSmooth) {
+ upperValue = (int) (Math.ceil(upperValue / increment) * increment) - increment;
+ }
+ checkUpperValue();
+ handleToolTip(upperValue);
+ } else {
+ lowerValue = (int) Math.round((x - 9d) / computePixelSizeForHorizontalSlider()) + minimum;
+ if (!isSmooth) {
+ lowerValue = (int) (Math.ceil(lowerValue / increment) * increment) - increment;
+ }
+ checkLowerValue();
+ handleToolTip(lowerValue);
+ }
+ } else {
+ if (selectedElement == BOTH) {
+ final int diff = (int) ((startDragPoint.y - y) / computePixelSizeForVerticalSlider()) + minimum;
+ int newUpper = startDragUpperValue - diff;
+ int newLower = startDragLowerValue - diff;
+ if (newUpper > maximum) {
+ newUpper = maximum;
+ newLower = maximum - (startDragUpperValue - startDragLowerValue);
+ } else if (newLower < minimum) {
+ newLower = minimum;
+ newUpper = minimum + startDragUpperValue - startDragLowerValue;
+ }
+ upperValue = newUpper;
+ lowerValue = newLower;
+ if (!isSmooth) {
+ lowerValue = (int) (Math.ceil(lowerValue / increment) * increment) - increment;
+ upperValue = (int) (Math.ceil(upperValue / increment) * increment) - increment;
+ }
+ handleToolTip(lowerValue, upperValue);
+ } else if ((selectedElement & UPPER) != 0) {
+ upperValue = (int) Math.round((y - 9d) / computePixelSizeForVerticalSlider()) + minimum;
+ if (!isSmooth) {
+ upperValue = (int) (Math.ceil(upperValue / increment) * increment) - increment;
+ }
+ checkUpperValue();
+ handleToolTip(upperValue);
+ } else {
+ lowerValue = (int) Math.round((y - 9d) / computePixelSizeForVerticalSlider()) + minimum;
+ if (!isSmooth) {
+ lowerValue = (int) (Math.ceil(lowerValue / increment) * increment) - increment;
+ }
+ checkLowerValue();
+ handleToolTip(lowerValue);
+ }
+ }
+ if (isOn) {
+ validateNewValues(e);
+ } else {
+ redraw();
+ }
+ }
+ }
+
+ /**
+ * determine whether the input coordinate is within the scale bounds and between
+ * the current upper and lower values.
+ *
+ * @param x
+ * @param y
+ * @return
+ */
+ private boolean isBetweenKnobs(int x, int y) {
+ return orientation == SWT.HORIZONTAL ? x < coordUpper.x && x > coordLower.x && y >= minHeight/3 && y <= minHeight/3 + getClientArea().height - 2*minHeight/3 : //
+ y < coordUpper.y && y > coordLower.y && x >= minWidth/3 && x <= minWidth/3 + getClientArea().width - 2*minWidth/3;
+ }
+
+ /**
+ * set the upperHover and lowerHover values according to the coordinates of the
+ * input event, the key modifier state, and whether the style allows selection
+ * of both knobs.
+ *
+ * @param e
+ */
+ private void selectKnobs(final Event e) {
+ if (coordLower == null) {
+ return;
+ }
+ final Image img = orientation == SWT.HORIZONTAL ? slider : vSlider;
+ final int x = e.x, y = e.y;
+ lowerHover = x >= coordLower.x && x <= coordLower.x + img.getBounds().width && y >= coordLower.y && y <= coordLower.y + img.getBounds().height;
+ upperHover = ((e.stateMask & (SWT.CTRL | SWT.SHIFT)) != 0 || !lowerHover) && //
+ x >= coordUpper.x && x <= coordUpper.x + img.getBounds().width && //
+ y >= coordUpper.y && y <= coordUpper.y + img.getBounds().height;
+ lowerHover &= (e.stateMask & SWT.CTRL) != 0 || !upperHover;
+ if (!lowerHover && !upperHover && isFullSelection && isBetweenKnobs(x, y)) {
+ lowerHover = upperHover = true;
+ }
+ }
+
+ /**
+ * if the input coordinate is within the scale bounds, return the corresponding
+ * scale value of the coordinate. otherwise return -1.
+ *
+ * @param x x coordinate value
+ * @param y y coordinate value
+ * @return
+ */
+ private int getCursorValue(int x, int y) {
+ int value = -1;
+ final Rectangle clientArea = getClientArea();
+ if (orientation == SWT.HORIZONTAL) {
+ if (x < 9 + clientArea.width - 20 && x >= 9 && y >= 9 && y <= 9 + clientArea.height - 20) {
+ value = (int) Math.round((x - 9d) / computePixelSizeForHorizontalSlider()) + minimum;
+ }
+ } else if (y < 9 + clientArea.height - 20 && y >= 9 && x >= 9 && x <= 9 + clientArea.width - 20) {
+ value = (int) Math.round((y - 9d) / computePixelSizeForVerticalSlider()) + minimum;
+ }
+ return value;
+ }
+
+ /**
+ * Code executed when the mouse double click
+ *
+ * @param e event
+ */
+ private void handleMouseDoubleClick(final Event e) {
+ final int value = getCursorValue(e.x, e.y);
+ if (value >= 0) {
+ selectedElement = priorSelectedElement;
+ if (value > upperValue) {
+ if (selectedElement == BOTH) {
+ lowerValue += value - upperValue;
+ upperValue = value;
+ } else if ((selectedElement & UPPER) != 0) {
+ upperValue = value;
+ } else if ((selectedElement & LOWER) != 0) {
+ final int diff = upperValue - lowerValue;
+ if (value + diff > maximum) {
+ upperValue = maximum;
+ lowerValue = maximum - diff;
+ } else {
+ upperValue = value + diff;
+ lowerValue = value;
+ }
+ }
+ } else if (value < lowerValue) {
+ if (selectedElement == BOTH) {
+ upperValue += value - lowerValue;
+ lowerValue = value;
+ } else if ((selectedElement & LOWER) != 0) {
+ lowerValue = value;
+ } else if ((selectedElement & UPPER) != 0) {
+ final int diff = upperValue - lowerValue;
+ if (value - diff < minimum) {
+ lowerValue = minimum;
+ upperValue = minimum + diff;
+ } else {
+ upperValue = value;
+ lowerValue = value - diff;
+ }
+ }
+ } else if (value > lowerValue && value < upperValue && selectedElement != BOTH) {
+ if ((selectedElement & LOWER) != 0) {
+ lowerValue = value;
+ } else if ((selectedElement & UPPER) != 0) {
+ upperValue = value;
+ }
+ }
+ validateNewValues(e);
+ }
+ }
+
+ private StringBuffer toolTip;
+ private Point coordUpper;
+ private Point coordLower;
+
+ /**
+ * set the tooltip if a toolTipFormatter is present. either one or two values
+ * are accepted.
+ *
+ * @param values
+ */
+ private void handleToolTip(int... values) {
+ if (toolTipFormatter != null) {
+ try {
+ if (values.length == 1) {
+ toolTip.setLength(0);
+ toolTipFormatter.format(values[0], toolTip, null);
+ super.setToolTipText(toolTip.toString());
+ } else if (values.length == 2) {
+ toolTip.setLength(0);
+ toolTipFormatter.format(values[0], toolTip, null);
+ toolTip.append(" \u2194 "); // LEFT RIGHT ARROW
+ toolTipFormatter.format(values[1], toolTip, null);
+ super.setToolTipText(toolTip.toString());
+ }
+ } catch (final IllegalArgumentException ex) {
+ super.setToolTipText(clientToolTipText);
+ }
+ }
+ }
+
+ /**
+ * Code executed on mouse hover
+ *
+ * @param e event
+ */
+ private void handleMouseHover(final Event e) {
+ if (!dragInProgress && toolTipFormatter != null) {
+ final int value = getCursorValue(e.x, e.y);
+ if (value >= 0) {
+ try {
+ toolTip.setLength(0);
+ toolTipFormatter.format(value, toolTip, null);
+ super.setToolTipText(toolTip.toString());
+ } catch (final IllegalArgumentException ex) {
+ super.setToolTipText(clientToolTipText);
+ }
+ } else {
+ super.setToolTipText(clientToolTipText);
+ }
+ }
+ }
+
+ /**
+ * a formatter for displaying a tool tip when hovering over the scale and during
+ * thumb modification events. The
+ * {@link Format#format(Object, StringBuffer, java.text.FieldPosition)} method
+ * is invoked to retrieve the text for the tooltip where the input
+ * {@code Object} is an {@code Integer} with a value within the minimum and
+ * maximum.
+ *
+ * @param formatter
+ */
+ public void setToolTipFormatter(Format formatter) {
+ toolTip = formatter != null ? new StringBuffer() : null;
+ toolTipFormatter = formatter;
+ }
+
+ @Override
+ public void setToolTipText(String string) {
+ super.setToolTipText(clientToolTipText = string);
+ }
+
+ /**
+ * Code executed when the mouse wheel is activated
+ *
+ * @param e event
+ */
+ private void handleMouseWheel(final Event e) {
+ if (selectedElement == NONE || dragInProgress) {
+ e.doit = false; // we are consuming this event
+ return;
+ }
+ previousLowerValue = lowerValue;
+ previousUpperValue = upperValue;
+ final int amount = increment * ((e.stateMask & SWT.SHIFT) != 0 ? 10 : (e.stateMask & SWT.CTRL) != 0 ? 2 : 1);
+ if (selectedElement == BOTH) {
+ int newLower = lowerValue + e.count * amount;
+ int newUpper = upperValue + e.count * amount;
+ if (newUpper > maximum) {
+ newUpper = maximum;
+ newLower = maximum - (upperValue - lowerValue);
+ } else if (newLower < minimum) {
+ newLower = minimum;
+ newUpper = minimum + upperValue - lowerValue;
+ }
+ upperValue = newUpper;
+ lowerValue = newLower;
+ } else if ((selectedElement & LOWER) != 0) {
+ lowerValue += e.count * amount;
+ checkLowerValue();
+ } else {
+ upperValue += e.count * amount;
+ checkUpperValue();
+ }
+ validateNewValues(e);
+ e.doit = false; // we are consuming this event
+ }
+
+ /**
+ * Check if the lower value is in ranges
+ */
+ private void checkLowerValue() {
+ if (lowerValue < minimum) {
+ lowerValue = minimum;
+ }
+ if (lowerValue > maximum) {
+ lowerValue = maximum;
+ }
+ if (lowerValue > upperValue) {
+ lowerValue = upperValue;
+ }
+ }
+
+ /**
+ * Check if the upper value is in ranges
+ */
+ private void checkUpperValue() {
+ if (upperValue < minimum) {
+ upperValue = minimum;
+ }
+ if (upperValue > maximum) {
+ upperValue = maximum;
+ }
+ if (upperValue < lowerValue) {
+ upperValue = lowerValue;
+ }
+ }
+
+ /**
+ * Draws the widget
+ *
+ * @param e paint event
+ */
+ private void drawWidget(final PaintEvent e) {
+ final Rectangle rect = getClientArea();
+ if (rect.width == 0 || rect.height == 0) {
+ return;
+ }
+ e.gc.setAdvanced(true);
+ e.gc.setAntialias(SWT.ON);
+ if (orientation == SWT.HORIZONTAL) {
+ drawHorizontalRangeSlider(e.gc);
+ } else {
+ drawVerticalRangeSlider(e.gc);
+ }
+
+ }
+
+ /**
+ * Draw the range slider (horizontal)
+ *
+ * @param gc graphic context
+ */
+ private void drawHorizontalRangeSlider(final GC gc) {
+ drawBackgroundHorizontal(gc);
+ drawBarsHorizontal(gc);
+ if (lowerHover || (selectedElement & LOWER) != 0) {
+ coordUpper = drawHorizontalKnob(gc, upperValue, true);
+ coordLower = drawHorizontalKnob(gc, lowerValue, false);
+ } else {
+ coordLower = drawHorizontalKnob(gc, lowerValue, false);
+ coordUpper = drawHorizontalKnob(gc, upperValue, true);
+ }
+ }
+
+ /**
+ * Draw the background
+ *
+ * @param gc graphic context
+ */
+ private void drawBackgroundHorizontal(final GC gc) {
+ final Rectangle clientArea = getClientArea();
+
+ gc.setBackground(getBackground());
+ gc.fillRectangle(clientArea);
+
+ if (isEnabled()) {
+ gc.setForeground(getForeground());
+ } else {
+ gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_GRAY));
+ }
+ gc.drawRoundRectangle(minHeight/3+imgWidth, minHeight/3, clientArea.width - 2*(minHeight/3+imgWidth), clientArea.height - 2*minHeight/3+3, 3, 3);
+
+ final float pixelSize = computePixelSizeForHorizontalSlider();
+ final int startX = (int) (pixelSize * lowerValue);
+ final int endX = (int) (pixelSize * upperValue);
+ if (isEnabled()) {
+ gc.setBackground(getForeground());
+ } else {
+ gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GRAY));
+ }
+ gc.fillRectangle(minHeight/3+3 + startX, minHeight/3, endX - startX - 3, clientArea.height - 2*minHeight/3+3);
+
+ }
+
+ /**
+ * @return how many pixels corresponds to 1 point of value
+ */
+ private float computePixelSizeForHorizontalSlider() {
+ return (getClientArea().width - 20f) / (maximum - minimum);
+ }
+
+ /**
+ * Draw the bars
+ *
+ * @param gc graphic context
+ */
+ private void drawBarsHorizontal(final GC gc) {
+ }
+
+ /**
+ * Draws an horizontal knob
+ *
+ * @param gc graphic context
+ * @param value corresponding value
+ * @param upper if true
, draws the upper knob. If
+ * false
, draws the lower knob
+ * @return the coordinate of the upper left corner of the knob
+ */
+ private Point drawHorizontalKnob(final GC gc, final int value, final boolean upper) {
+ final float pixelSize = computePixelSizeForHorizontalSlider();
+ final int x = (int) (pixelSize * value);
+ Image image;
+ if (upper) {
+ if (upperHover) {
+ image = dragInProgress || (selectedElement & UPPER) != 0 ? sliderDrag : sliderHover;
+ } else if ((selectedElement & UPPER) != 0 && !lowerHover) {
+ image = hasFocus ? sliderSelected : sliderHover;
+ } else {
+ image = slider;
+ }
+ } else {
+ if (lowerHover) {
+ image = dragInProgress || (selectedElement & LOWER) != 0 ? sliderDrag : sliderHover;
+ } else if ((selectedElement & LOWER) != 0 && !upperHover) {
+ image = hasFocus ? sliderSelected : sliderHover;
+ } else {
+ image = slider;
+ }
+ }
+ if (isEnabled()) {
+ gc.drawImage(image, x + 5, getClientArea().height / 2 - slider.getBounds().height / 2);
+ } else {
+ final Image temp = new Image(getDisplay(), image, SWT.IMAGE_DISABLE);
+ gc.drawImage(temp, x + 5, getClientArea().height / 2 - slider.getBounds().height / 2);
+ temp.dispose();
+ }
+ return new Point(x + 5, getClientArea().height / 2 - slider.getBounds().height / 2);
+ }
+
+ /**
+ * Draw the range slider (vertical)
+ *
+ * @param gc graphic context
+ */
+ private void drawVerticalRangeSlider(final GC gc) {
+ drawBackgroundVertical(gc);
+ drawBarsVertical(gc);
+ if (lowerHover || (selectedElement & LOWER) != 0) {
+ coordUpper = drawVerticalKnob(gc, upperValue, true);
+ coordLower = drawVerticalKnob(gc, lowerValue, false);
+ } else {
+ coordLower = drawVerticalKnob(gc, lowerValue, false);
+ coordUpper = drawVerticalKnob(gc, upperValue, true);
+ }
+ }
+
+ /**
+ * Draws the background
+ *
+ * @param gc graphic context
+ */
+ private void drawBackgroundVertical(final GC gc) {
+ final Rectangle clientArea = 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 float pixelSize = computePixelSizeForVerticalSlider();
+ final int startY = (int) (pixelSize * lowerValue);
+ final int endY = (int) (pixelSize * upperValue);
+ if (isEnabled()) {
+ gc.setBackground(getForeground());
+ } else {
+ gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GRAY));
+ }
+ gc.fillRectangle(9, 12 + startY, clientArea.width - 20, endY - startY - 6);
+
+ }
+
+ /**
+ * @return how many pixels corresponds to 1 point of value
+ */
+ private float computePixelSizeForVerticalSlider() {
+ return (getClientArea().height - 20f) / (maximum - minimum);
+ }
+
+ /**
+ * Draws the bars
+ *
+ * @param gc graphic context
+ */
+ private void drawBarsVertical(final GC gc) {
+ final Rectangle clientArea = getClientArea();
+ if (isEnabled()) {
+ gc.setForeground(getForeground());
+ } else {
+ gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_GRAY));
+ }
+ }
+
+ /**
+ * Draws a vertical knob
+ *
+ * @param gc graphic context
+ * @param value corresponding value
+ * @param upper if true
, draws the upper knob. If
+ * false
, draws the lower knob
+ * @return the coordinate of the upper left corner of the knob
+ */
+ private Point drawVerticalKnob(final GC gc, final int value, final boolean upper) {
+ final float pixelSize = computePixelSizeForVerticalSlider();
+ final int y = (int) (pixelSize * value);
+
+ Image image;
+ if (upper) {
+ if (upperHover) {
+ image = dragInProgress || (selectedElement & UPPER) != 0 ? vSliderDrag : vSliderHover;
+ } else if ((selectedElement & UPPER) != 0 && !lowerHover) {
+ image = hasFocus ? vSliderSelected : vSliderHover;
+ } else {
+ image = vSlider;
+ }
+ } else {
+ if (lowerHover) {
+ image = dragInProgress || (selectedElement & LOWER) != 0 ? vSliderDrag : vSliderHover;
+ } else if ((selectedElement & LOWER) != 0 && !upperHover) {
+ image = hasFocus ? vSliderSelected : vSliderHover;
+ } else {
+ image = vSlider;
+ }
+ }
+
+ if (isEnabled()) {
+ gc.drawImage(image, getClientArea().width / 2 - 8, y + 4);
+ } else {
+ final Image temp = new Image(getDisplay(), image, SWT.IMAGE_DISABLE);
+ gc.drawImage(temp, getClientArea().width / 2 - 8, y + 4);
+ temp.dispose();
+ }
+ return new Point(getClientArea().width / 2 - 8, y + 4);
+ }
+
+ /**
+ * move the cursor location by the input delta values.
+ *
+ * @param xDelta
+ * @param yDelta
+ */
+ private void moveCursorPosition(int xDelta, int yDelta) {
+ final Point cursorPosition = getDisplay().getCursorLocation();
+ cursorPosition.x += xDelta;
+ cursorPosition.y += yDelta;
+ getDisplay().setCursorLocation(cursorPosition);
+ }
+
+ /**
+ * Code executed when a key is typed
+ *
+ * @param event event
+ */
+ private void handleKeyDown(final Event event) {
+ // TODO consider API for setting accelerator values
+ int accelerator = (event.stateMask & SWT.SHIFT) != 0 ? 10 : (event.stateMask & SWT.CTRL) != 0 ? 2 : 1;
+ if (dragInProgress) {
+ switch (event.keyCode) {
+ case SWT.ESC:
+ startDragPoint = null;
+ upperValue = startDragUpperValue;
+ lowerValue = startDragLowerValue;
+ validateNewValues(event);
+ dragInProgress = false;
+ if (!isOn) {
+ redraw();
+ }
+ event.doit = false;
+ break;
+ case SWT.ARROW_UP:
+ accelerator = orientation == SWT.HORIZONTAL ? -accelerator : accelerator;
+ case SWT.ARROW_LEFT:
+ if (orientation == SWT.VERTICAL) {
+ moveCursorPosition(0, -accelerator);
+ } else {
+ moveCursorPosition(-accelerator, 0);
+ }
+ event.doit = false;
+ break;
+ case SWT.ARROW_DOWN:
+ accelerator = orientation == SWT.HORIZONTAL ? -accelerator : accelerator;
+ case SWT.ARROW_RIGHT:
+ if (orientation == SWT.VERTICAL) {
+ moveCursorPosition(0, accelerator);
+ } else {
+ moveCursorPosition(accelerator, 0);
+ }
+ event.doit = false;
+ break;
+ }
+ return;
+ }
+ previousLowerValue = lowerValue;
+ previousUpperValue = upperValue;
+
+ if (selectedElement == NONE) {
+ selectedElement = LOWER;
+ }
+ switch (event.keyCode) {
+ case SWT.HOME:
+ if (selectedElement == BOTH) {
+ if ((event.stateMask & SWT.SHIFT) != 0) {
+ lowerValue = maximum - (upperValue - lowerValue);
+ upperValue = maximum;
+ } else {
+ upperValue = minimum + upperValue - lowerValue;
+ lowerValue = minimum;
+ }
+ } else if ((selectedElement & UPPER) != 0) {
+ upperValue = maximum;
+ } else {
+ lowerValue = minimum;
+ }
+ break;
+ case SWT.END:
+ if (selectedElement == BOTH) {
+ if ((event.stateMask & SWT.SHIFT) != 0) {
+ upperValue = minimum + upperValue - lowerValue;
+ lowerValue = minimum;
+ } else {
+ lowerValue = maximum - (upperValue - lowerValue);
+ upperValue = maximum;
+ }
+ } else if ((selectedElement & UPPER) != 0) {
+ upperValue = lowerValue;
+ } else {
+ lowerValue = upperValue;
+ }
+ break;
+ case SWT.PAGE_UP:
+ accelerator = orientation == SWT.HORIZONTAL ? -accelerator : accelerator;
+ if (selectedElement == BOTH) {
+ translateValues(pageIncrement * -accelerator);
+ } else if ((selectedElement & UPPER) != 0) {
+ upperValue -= pageIncrement * accelerator;
+ } else {
+ lowerValue -= pageIncrement * accelerator;
+ }
+ break;
+ case SWT.PAGE_DOWN:
+ accelerator = orientation == SWT.HORIZONTAL ? -accelerator : accelerator;
+ if (selectedElement == BOTH) {
+ translateValues(pageIncrement * accelerator);
+ } else if ((selectedElement & UPPER) != 0) {
+ upperValue += pageIncrement * accelerator;
+ } else {
+ lowerValue += pageIncrement * accelerator;
+ }
+ break;
+ case SWT.ARROW_DOWN:
+ accelerator = orientation == SWT.HORIZONTAL ? -accelerator : accelerator;
+ case SWT.ARROW_RIGHT:
+ if (selectedElement == BOTH) {
+ translateValues(accelerator * increment);
+ } else if ((selectedElement & UPPER) != 0) {
+ upperValue += accelerator * increment;
+ } else {
+ lowerValue += accelerator * increment;
+ }
+ break;
+ case SWT.ARROW_UP:
+ accelerator = orientation == SWT.HORIZONTAL ? -accelerator : accelerator;
+ case SWT.ARROW_LEFT:
+ if (selectedElement == BOTH) {
+ translateValues(-accelerator * increment);
+ } else if ((selectedElement & UPPER) != 0) {
+ upperValue -= accelerator * increment;
+ } else {
+ lowerValue -= accelerator * increment;
+ }
+ break;
+ case SWT.TAB:
+ final boolean next = (event.stateMask & SWT.SHIFT) == 0;
+ if (next && (selectedElement & LOWER) != 0) {
+ selectedElement = isFullSelection && selectedElement == LOWER ? BOTH : UPPER;
+ redraw();
+ } else if (!next && (selectedElement & UPPER) != 0) {
+ selectedElement = isFullSelection && selectedElement == UPPER ? BOTH : LOWER;
+ redraw();
+ } else {
+ traverse(next ? SWT.TRAVERSE_TAB_NEXT : SWT.TRAVERSE_TAB_PREVIOUS);
+ }
+ return;
+ }
+ if (previousLowerValue != lowerValue || previousUpperValue != upperValue) {
+ if (selectedElement == BOTH) {
+ checkLowerValue();
+ checkUpperValue();
+ } else if ((selectedElement & UPPER) != 0) {
+ checkUpperValue();
+ } else {
+ checkLowerValue();
+ }
+ validateNewValues(event);
+ }
+ }
+
+ /**
+ * translate both the upper and lower values by the input amount. The updated
+ * values are constrained to be within the minimum and maximum. The difference
+ * between upper and lower values is retained.
+ *
+ * @param amount
+ */
+ private void translateValues(int amount) {
+ int newLower = lowerValue + amount;
+ int newUpper = upperValue + amount;
+ if (newUpper > maximum) {
+ newUpper = maximum;
+ newLower = maximum - (upperValue - lowerValue);
+ } else if (newLower < minimum) {
+ newLower = minimum;
+ newUpper = minimum + upperValue - lowerValue;
+ }
+ upperValue = newUpper;
+ lowerValue = newLower;
+ }
+
+ /**
+ * 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 SelectionListener
interface.
+ *
+ * widgetSelected
is called when the user changes the receiver's
+ * value. widgetDefaultSelected
is not called.
+ *
+ *
+ * @param listener the listener which should be notified
+ *
+ * @exception IllegalArgumentException
+ *
+ * - ERROR_NULL_ARGUMENT - if the listener is null
+ *
+ * @exception SWTException
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ *
+ * @see SelectionListener
+ * @see #removeSelectionListener
+ */
+ public void addSelectionListener(final SelectionListener listener) {
+ checkWidget();
+ SelectionListenerUtil.addSelectionListener(this, listener);
+ }
+
+ /**
+ * @see org.eclipse.swt.widgets.Composite#computeSize(int, int, boolean)
+ */
+ @Override
+ public Point computeSize(final int wHint, final int hHint, final boolean changed) {
+ final int width, height;
+ checkWidget();
+ if (orientation == SWT.HORIZONTAL) {
+ if (wHint < 100) {
+ width = 100;
+ } else {
+ width = wHint;
+ }
+
+ if (hHint < minHeight) {
+ height = minHeight;
+ } else {
+ height = hHint;
+ }
+ } else {
+ if (wHint < minWidth) {
+ width = minWidth;
+ } else {
+ width = wHint;
+ }
+
+ if (hHint < 100) {
+ height = 100;
+ } 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
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ */
+ public int getIncrement() {
+ checkWidget();
+ return increment;
+ }
+
+ /**
+ * Returns the 'lower selection', which is the lower receiver's position.
+ *
+ * @return the selection
+ *
+ * @exception SWTException
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ */
+ public int getLowerValue() {
+ checkWidget();
+ return lowerValue;
+ }
+
+ /**
+ * Returns the maximum value which the receiver will allow.
+ *
+ * @return the maximum
+ *
+ * @exception SWTException
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ */
+ public int getMaximum() {
+ checkWidget();
+ return maximum;
+ }
+
+ /**
+ * Returns the minimum value which the receiver will allow.
+ *
+ * @return the minimum
+ *
+ * @exception SWTException
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ */
+ public int getMinimum() {
+ checkWidget();
+ return 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
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ */
+ public int getPageIncrement() {
+ checkWidget();
+ return 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
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ */
+ public int[] getSelection() {
+ checkWidget();
+ final int[] selection = new int[2];
+ selection[0] = lowerValue;
+ selection[1] = upperValue;
+ return selection;
+ }
+
+ /**
+ * Returns the 'upper selection', which is the upper receiver's position.
+ *
+ * @return the selection
+ *
+ * @exception SWTException
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ */
+ public int getUpperValue() {
+ checkWidget();
+ return upperValue;
+ }
+
+ /**
+ * 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
+ *
+ * - ERROR_NULL_ARGUMENT - if the listener is null
+ *
+ * @exception SWTException
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ *
+ * @see SelectionListener
+ * @see #addSelectionListener
+ */
+ public void removeSelectionListener(final SelectionListener listener) {
+ checkWidget();
+ SelectionListenerUtil.removeSelectionListener(this, 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
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ */
+ public void setIncrement(final int increment) {
+ checkWidget();
+ this.increment = increment;
+ redraw();
+ }
+
+ /**
+ * Sets the 'lower selection', which is the receiver's lower value, to the input
+ * argument which must be less than or equal to the current 'upper selection'
+ * and greater or equal to the minimum. If either condition fails, no action is
+ * taken.
+ *
+ * @param value the new lower selection
+ *
+ * @exception SWTException
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ * @see #getUpperValue()
+ * @see #getMinimum()
+ * @see #setSelection(int, int)
+ */
+ public void setLowerValue(final int value) {
+ setSelection(value, upperValue);
+ }
+
+ /**
+ * 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
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ * @see #setExtrema(int, int)
+ */
+ public void setMaximum(final int value) {
+ setExtrema(minimum, value);
+ }
+
+ /**
+ * 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
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ * @see #setExtrema(int, int)
+ */
+ public void setMinimum(final int value) {
+ setExtrema(value, maximum);
+ }
+
+ /**
+ * Sets the minimum and maximum values that the receiver will allow. The new
+ * values will be ignored if either are negative or the min value is not less
+ * than the max. The receiver's selection values will be adjusted if necessary
+ * to fall within the new range.
+ *
+ * @param min the new minimum, which must be nonnegative and less than the max
+ * @param max the new maximum, which must be greater than the min
+ *
+ * @exception SWTException
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ */
+ public void setExtrema(final int min, final int max) {
+ checkWidget();
+ if (min >= 0 && min < max && (min != minimum || max != maximum)) {
+ minimum = min;
+ maximum = max;
+ if (lowerValue < minimum) {
+ lowerValue = minimum;
+ } else if (lowerValue > maximum) {
+ lowerValue = maximum;
+ }
+ if (upperValue < minimum) {
+ upperValue = minimum;
+ } else if (upperValue > maximum) {
+ upperValue = maximum;
+ }
+ 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
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ */
+ public void setPageIncrement(final int pageIncrement) {
+ checkWidget();
+ this.pageIncrement = pageIncrement;
+ redraw();
+ }
+
+ /**
+ * Sets the 'selection', which is the receiver's value. The lower value must be
+ * less than or equal to the upper value. Additionally, both values must be
+ * inclusively between the slider minimum and maximum. If either condition
+ * fails, no action is taken.
+ *
+ * @param value the new selection (first value is lower value, second value is
+ * upper value)
+ *
+ * @exception SWTException
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ */
+ public void setSelection(final int[] values) {
+ if (values.length == 2) {
+ setSelection(values[0], values[1]);
+ }
+ }
+
+ /**
+ * Sets the 'selection', which is the receiver's value. The lower value must be
+ * less than or equal to the upper value. Additionally, both values must be
+ * inclusively between the slider minimum and maximum. If either condition
+ * fails, no action is taken.
+ *
+ * @param lowerValue the new lower selection
+ * @param upperValue the new upper selection
+ *
+ * @exception SWTException
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ * @see #getMinimum()
+ * @see #getMaximum()
+ */
+ public void setSelection(final int lowerValue, final int upperValue) {
+ checkWidget();
+ if (lowerValue <= upperValue && lowerValue >= minimum && upperValue <= maximum && (this.lowerValue != lowerValue || this.upperValue != upperValue)) {
+ this.lowerValue = lowerValue;
+ this.upperValue = upperValue;
+ redraw();
+ }
+ }
+
+ /**
+ * Sets the 'upper selection', which is the upper receiver's value, to the input
+ * argument which must be greater than or equal to the current 'lower selection'
+ * and less or equal to the maximum. If either condition fails, no action is
+ * taken.
+ *
+ * @param value the new upper selection
+ *
+ * @exception SWTException
+ *
+ * - ERROR_WIDGET_DISPOSED - if the receiver has been
+ * disposed
+ * - ERROR_THREAD_INVALID_ACCESS - if not called from the
+ * thread that created the receiver
+ *
+ * @see #getLowerValue()
+ * @see #getMaximum()
+ * @see #setSelection(int, int)
+ */
+ public void setUpperValue(final int value) {
+ setSelection(lowerValue, value);
+ }
+}
\ No newline at end of file
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/ReflectionUtils.java b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/ReflectionUtils.java
new file mode 100644
index 0000000..93e97c6
--- /dev/null
+++ b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/ReflectionUtils.java
@@ -0,0 +1,48 @@
+package com.minres.scviewer.database.ui.swt.internal.slider;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class ReflectionUtils {
+ /**
+ * Call a method using introspection (so ones can call a private or protected method)
+ * @param object object on which the method will be called
+ * @param methodName method name
+ * @param args arguments of this method (can be null)
+ * @return the value returned by this method (if this method returns a value)
+ */
+ public static Object callMethod(final Object object, final String methodName, final Object... args) {
+ if (object == null) {
+ return null;
+ }
+ final Class>[] array = new Class>[args == null ? 0 : args.length];
+ int index = 0;
+ if (args != null) {
+ for (final Object o : args) {
+ array[index++] = o == null ? Object.class : o.getClass();
+ }
+ }
+
+ return callMethodWithClassType(object, methodName, array, args);
+ }
+
+ private static Object callMethodWithClassType(final Object object, final String methodName, final Class>[] array, final Object... args) {
+ Class> currentClass = object.getClass();
+ Method method = null;
+ while (currentClass != null) {
+ try {
+ method = currentClass.getDeclaredMethod(methodName, array);
+ break;
+ } catch (final NoSuchMethodException nsme) {
+ currentClass = currentClass.getSuperclass();
+ }
+ }
+
+ try {
+ method.setAccessible(true);
+ return method.invoke(object, args);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/SelectionListenerUtil.java b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/SelectionListenerUtil.java
new file mode 100644
index 0000000..273eca2
--- /dev/null
+++ b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/SelectionListenerUtil.java
@@ -0,0 +1,74 @@
+package com.minres.scviewer.database.ui.swt.internal.slider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.TypedListener;
+
+public class SelectionListenerUtil {
+ /**
+ * Add a SelectionListener
to a given Control
+ *
+ * @param control control on which the selection listener is added
+ * @param listener listener to add
+ */
+ public static void addSelectionListener(final Control control, final SelectionListener listener) {
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ TypedListener typedListener = new TypedListener(listener);
+ control.addListener(SWT.Selection, typedListener);
+ }
+
+ /**
+ * Remove a SelectionListener
of a given Control
+ *
+ * @param control control on which the selection listener is removed
+ * @param listener listener to remove
+ */
+ public static void removeSelectionListener(final Control control, final SelectionListener listener) {
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ final Listener[] listeners = control.getListeners(SWT.Selection);
+ for (Listener l : listeners) {
+ if (l instanceof TypedListener) {
+ TypedListener typedListener = (TypedListener) l;
+ if (typedListener.getEventListener() == listener) {
+ ReflectionUtils.callMethod(control, "removeListener", SWT.Selection, ((TypedListener) l).getEventListener());
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Fire the selection listeners of a given control
+ *
+ * @param control the control that fires the event
+ * @param sourceEvent mouse event
+ * @return true if the selection could be changed, false otherwise
+ */
+ public static boolean fireSelectionListeners(final Control control, final Event sourceEvent) {
+ for (final Listener listener : control.getListeners(SWT.Selection)) {
+ final Event event = new Event();
+
+ event.button = sourceEvent==null?1:sourceEvent.button;
+ event.display = control.getDisplay();
+ event.item = null;
+ event.widget = control;
+ event.data = sourceEvent == null ? null : sourceEvent.data;
+ event.time = sourceEvent == null ? 0 : sourceEvent.time;
+ event.x = sourceEvent == null ? 0 : sourceEvent.x;
+ event.y = sourceEvent == null ? 0 : sourceEvent.y;
+ event.type = SWT.Selection;
+
+ listener.handleEvent(event);
+ if (!event.doit) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/bullet_left.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/bullet_left.png
new file mode 100644
index 0000000..96b4466
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/bullet_left.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/bullet_right.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/bullet_right.png
new file mode 100644
index 0000000..e809d37
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/bullet_right.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-drag.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-drag.png
new file mode 100644
index 0000000..2011d9b
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-drag.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-hover.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-hover.png
new file mode 100644
index 0000000..9f0d039
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-hover.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-normal.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-normal.png
new file mode 100644
index 0000000..bdc317f
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-normal.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-selected.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-selected.png
new file mode 100644
index 0000000..b57ffe6
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/h-slider-selected.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r.png
new file mode 100644
index 0000000..ad0e2a3
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r_bl.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r_bl.png
new file mode 100644
index 0000000..558f9bd
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r_bl.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r_bl_lt.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r_bl_lt.png
new file mode 100644
index 0000000..e37e735
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r_bl_lt.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r_lt.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r_lt.png
new file mode 100644
index 0000000..237be7f
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/marker_r_lt.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-drag.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-drag.png
new file mode 100644
index 0000000..2d88c1a
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-drag.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-hover.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-hover.png
new file mode 100644
index 0000000..e8ac5ad
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-hover.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-normal.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-normal.png
new file mode 100644
index 0000000..4c5098c
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-normal.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-selected.png b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-selected.png
new file mode 100644
index 0000000..9d19943
Binary files /dev/null and b/plugins/com.minres.scviewer.database.ui.swt/src/com/minres/scviewer/database/ui/swt/internal/slider/slider-selected.png differ
diff --git a/plugins/com.minres.scviewer.database.ui.swt/src/org/eclipse/wb/swt/ResourceManager.java b/plugins/com.minres.scviewer.database.ui.swt/src/org/eclipse/wb/swt/ResourceManager.java
deleted file mode 100644
index 8e96dfe..0000000
--- a/plugins/com.minres.scviewer.database.ui.swt/src/org/eclipse/wb/swt/ResourceManager.java
+++ /dev/null
@@ -1,438 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2011 Google, Inc. and others
- * 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:
- * Google, Inc. - initial API and implementation
- * Wim Jongman - 1.8 and higher compliance
- *******************************************************************************/
-package org.eclipse.wb.swt;
-
-import java.io.File;
-import java.io.InputStream;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.jface.resource.CompositeImageDescriptor;
-import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.swt.graphics.Image;
-import org.eclipse.swt.graphics.ImageData;
-import org.eclipse.swt.graphics.ImageDataProvider;
-import org.eclipse.swt.graphics.Point;
-import org.eclipse.swt.graphics.Rectangle;
-import org.osgi.framework.Bundle;
-
-/**
- * Utility class for managing OS resources associated with SWT/JFace controls
- * such as colors, fonts, images, etc.
- *
- * This class is created automatically when you fiddle around with images and
- * colors in WB. You might want to prevent your application from using this
- * class and provide your own more effective means of resource caching.
- *
- * Even though this class can be used to manage these resources, if they are
- * here for the duration of the application and not used then you still have an
- * effective resource leak.
- *
- * Application code must explicitly invoke the dispose()
method to
- * release the operating system resources managed by cached objects when those
- * objects and OS resources are no longer needed.
- *
- * This class may be freely distributed as part of any application or plugin.
- *
- *
- * @author scheglov_ke
- * @author Dan Rubel
- * @author Wim Jongman
- */
-public class ResourceManager extends SWTResourceManager {
-
- /**
- * The map where we store our images.
- */
- private static Map m_descriptorImageMap = new HashMap();
-
- /**
- * Returns an {@link ImageDescriptor} stored in the file at the specified path
- * relative to the specified class.
- *
- * @param clazz the {@link Class} relative to which to find the image
- * descriptor.
- * @param path the path to the image file.
- * @return the {@link ImageDescriptor} stored in the file at the specified path.
- */
- public static ImageDescriptor getImageDescriptor(Class> clazz, String path) {
- return ImageDescriptor.createFromFile(clazz, path);
- }
-
- /**
- * Returns an {@link ImageDescriptor} stored in the file at the specified path.
- *
- * @param path the path to the image file.
- * @return the {@link ImageDescriptor} stored in the file at the specified path.
- */
- public static ImageDescriptor getImageDescriptor(String path) {
- try {
- return ImageDescriptor.createFromURL(new File(path).toURI().toURL());
- } catch (MalformedURLException e) {
- return null;
- }
- }
-
- /**
- * Returns an {@link Image} based on the specified {@link ImageDescriptor}.
- *
- * @param descriptor the {@link ImageDescriptor} for the {@link Image}.
- * @return the {@link Image} based on the specified {@link ImageDescriptor}.
- */
- public static Image getImage(ImageDescriptor descriptor) {
- if (descriptor == null) {
- return null;
- }
- Image image = m_descriptorImageMap.get(descriptor);
- if (image == null) {
- image = descriptor.createImage();
- m_descriptorImageMap.put(descriptor, image);
- }
- return image;
- }
-
- /**
- * Maps images to decorated images.
- */
- @SuppressWarnings("unchecked")
- private static Map>[] m_decoratedImageMap = new Map[LAST_CORNER_KEY];
-
- /**
- * Returns an {@link Image} composed of a base image decorated by another image.
- *
- * @param baseImage the base {@link Image} that should be decorated.
- * @param decorator the {@link Image} to decorate the base image.
- * @return {@link Image} The resulting decorated image.
- */
- public static Image decorateImage(Image baseImage, Image decorator) {
- return decorateImage(baseImage, decorator, BOTTOM_RIGHT);
- }
-
- /**
- * Returns an {@link Image} composed of a base image decorated by another image.
- *
- * @param baseImage
- * the base {@link Image} that should be decorated.
- * @param decorator
- * the {@link Image} to decorate the base image.
- * @param corner
- * the corner to place decorator image.
- * @return the resulting decorated {@link Image}.
- */
- public static Image decorateImage(final Image baseImage, final Image decorator, final int corner) {
- if (corner <= 0 || corner >= LAST_CORNER_KEY) {
- throw new IllegalArgumentException("Wrong decorate corner");
- }
- Map> cornerDecoratedImageMap = m_decoratedImageMap[corner];
- if (cornerDecoratedImageMap == null) {
- cornerDecoratedImageMap = new HashMap>();
- m_decoratedImageMap[corner] = cornerDecoratedImageMap;
- }
- Map decoratedMap = cornerDecoratedImageMap.get(baseImage);
- if (decoratedMap == null) {
- decoratedMap = new HashMap();
- cornerDecoratedImageMap.put(baseImage, decoratedMap);
- }
- //
- Image result = decoratedMap.get(decorator);
- if (result == null) {
- final Rectangle bib = baseImage.getBounds();
- final Rectangle dib = decorator.getBounds();
- final Point baseImageSize = new Point(bib.width, bib.height);
- CompositeImageDescriptor compositImageDesc = new CompositeImageDescriptor() {
- @Override
- protected void drawCompositeImage(int width, int height) {
- drawImage(createCachedImageDataProvider(baseImage), 0, 0);
- if (corner == TOP_LEFT) {
- drawImage(getUnzoomedImageDataProvider(decorator.getImageData()) , 0, 0);
- } else if (corner == TOP_RIGHT) {
- drawImage(getUnzoomedImageDataProvider(decorator.getImageData()), bib.width - dib.width, 0);
- } else if (corner == BOTTOM_LEFT) {
- drawImage(getUnzoomedImageDataProvider(decorator.getImageData()), 0, bib.height - dib.height);
- } else if (corner == BOTTOM_RIGHT) {
- drawImage(getUnzoomedImageDataProvider(decorator.getImageData()), bib.width - dib.width, bib.height - dib.height);
- }
- }
- @Override
- protected Point getSize() {
- return baseImageSize;
- }
- };
- //
- result = compositImageDesc.createImage();
- decoratedMap.put(decorator, result);
- }
- return result;
- }
-
- private static ImageDataProvider getUnzoomedImageDataProvider(ImageData imageData) {
- return zoom -> zoom == 100 ? imageData : null;
- }
-
-
- /**
- * Dispose all of the cached images.
- */
- public static void disposeImages() {
- SWTResourceManager.disposeImages();
- // dispose ImageDescriptor images
- {
- for (Iterator I = m_descriptorImageMap.values().iterator(); I.hasNext();) {
- I.next().dispose();
- }
- m_descriptorImageMap.clear();
- }
- // dispose decorated images
- for (int i = 0; i < m_decoratedImageMap.length; i++) {
- Map> cornerDecoratedImageMap = m_decoratedImageMap[i];
- if (cornerDecoratedImageMap != null) {
- for (Map decoratedMap : cornerDecoratedImageMap.values()) {
- for (Image image : decoratedMap.values()) {
- image.dispose();
- }
- decoratedMap.clear();
- }
- cornerDecoratedImageMap.clear();
- }
- }
- // dispose plugin images
- {
- for (Iterator I = m_URLImageMap.values().iterator(); I.hasNext();) {
- I.next().dispose();
- }
- m_URLImageMap.clear();
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // Plugin images support
- //
- ////////////////////////////////////////////////////////////////////////////
- /**
- * Maps URL to images.
- */
- private static Map m_URLImageMap = new HashMap();
-
- /**
- * Provider for plugin resources, used by WindowBuilder at design time.
- */
- public interface PluginResourceProvider {
- URL getEntry(String symbolicName, String path);
- }
-
- /**
- * Instance of {@link PluginResourceProvider}, used by WindowBuilder at design
- * time.
- */
- private static PluginResourceProvider m_designTimePluginResourceProvider = null;
-
- /**
- * Returns an {@link Image} based on a plugin and file path.
- *
- * @param plugin the plugin {@link Object} containing the image
- * @param name the path to the image within the plugin
- * @return the {@link Image} stored in the file at the specified path
- *
- * @deprecated Use {@link #getPluginImage(String, String)} instead.
- */
- @Deprecated
- public static Image getPluginImage(Object plugin, String name) {
- try {
- URL url = getPluginImageURL(plugin, name);
- if (url != null) {
- return getPluginImageFromUrl(url);
- }
- } catch (Throwable e) {
- // Ignore any exceptions
- }
- return null;
- }
-
- /**
- * Returns an {@link Image} based on a {@link Bundle} and resource entry path.
- *
- * @param symbolicName the symbolic name of the {@link Bundle}.
- * @param path the path of the resource entry.
- * @return the {@link Image} stored in the file at the specified path.
- */
- public static Image getPluginImage(String symbolicName, String path) {
- try {
- URL url = getPluginImageURL(symbolicName, path);
- if (url != null) {
- return getPluginImageFromUrl(url);
- }
- } catch (Throwable e) {
- // Ignore any exceptions
- }
- return null;
- }
-
- /**
- * Returns an {@link Image} based on given {@link URL}.
- */
- private static Image getPluginImageFromUrl(URL url) {
- try {
- try {
- String key = url.toExternalForm();
- Image image = m_URLImageMap.get(key);
- if (image == null) {
- InputStream stream = url.openStream();
- try {
- image = getImage(stream);
- m_URLImageMap.put(key, image);
- } finally {
- stream.close();
- }
- }
- return image;
- } catch (Throwable e) {
- // Ignore any exceptions
- }
- } catch (Throwable e) {
- // Ignore any exceptions
- }
- return null;
- }
-
- /**
- * Returns an {@link ImageDescriptor} based on a plugin and file path.
- *
- * @param plugin the plugin {@link Object} containing the image.
- * @param name the path to th eimage within the plugin.
- * @return the {@link ImageDescriptor} stored in the file at the specified path.
- *
- * @deprecated Use {@link #getPluginImageDescriptor(String, String)} instead.
- */
- @Deprecated
- public static ImageDescriptor getPluginImageDescriptor(Object plugin, String name) {
- try {
- try {
- URL url = getPluginImageURL(plugin, name);
- return ImageDescriptor.createFromURL(url);
- } catch (Throwable e) {
- // Ignore any exceptions
- }
- } catch (Throwable e) {
- // Ignore any exceptions
- }
- return null;
- }
-
- /**
- * Returns an {@link ImageDescriptor} based on a {@link Bundle} and resource
- * entry path.
- *
- * @param symbolicName the symbolic name of the {@link Bundle}.
- * @param path the path of the resource entry.
- * @return the {@link ImageDescriptor} based on a {@link Bundle} and resource
- * entry path.
- */
- public static ImageDescriptor getPluginImageDescriptor(String symbolicName, String path) {
- try {
- URL url = getPluginImageURL(symbolicName, path);
- if (url != null) {
- return ImageDescriptor.createFromURL(url);
- }
- } catch (Throwable e) {
- // Ignore any exceptions
- }
- return null;
- }
-
- /**
- * Returns an {@link URL} based on a {@link Bundle} and resource entry path.
- */
- private static URL getPluginImageURL(String symbolicName, String path) {
- // try runtime plugins
- {
- Bundle bundle = Platform.getBundle(symbolicName);
- if (bundle != null) {
- return bundle.getEntry(path);
- }
- }
- // try design time provider
- if (m_designTimePluginResourceProvider != null) {
- return m_designTimePluginResourceProvider.getEntry(symbolicName, path);
- }
- // no such resource
- return null;
- }
-
- /**
- * Returns an {@link URL} based on a plugin and file path.
- *
- * @param plugin the plugin {@link Object} containing the file path.
- * @param name the file path.
- * @return the {@link URL} representing the file at the specified path.
- * @throws Exception
- */
- private static URL getPluginImageURL(Object plugin, String name) throws Exception {
- // try to work with 'plugin' as with OSGI BundleContext
- try {
- Class> BundleClass = Class.forName("org.osgi.framework.Bundle"); //$NON-NLS-1$
- Class> BundleContextClass = Class.forName("org.osgi.framework.BundleContext"); //$NON-NLS-1$
- if (BundleContextClass.isAssignableFrom(plugin.getClass())) {
- Method getBundleMethod = BundleContextClass.getMethod("getBundle", new Class[0]); //$NON-NLS-1$
- Object bundle = getBundleMethod.invoke(plugin, new Object[0]);
- //
- Class> PathClass = Class.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$
- Constructor> pathConstructor = PathClass.getConstructor(new Class[] { String.class });
- Object path = pathConstructor.newInstance(new Object[] { name });
- //
- Class> IPathClass = Class.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$
- Class> PlatformClass = Class.forName("org.eclipse.core.runtime.Platform"); //$NON-NLS-1$
- Method findMethod = PlatformClass.getMethod("find", new Class[] { BundleClass, IPathClass }); //$NON-NLS-1$
- return (URL) findMethod.invoke(null, new Object[] { bundle, path });
- }
- } catch (Throwable e) {
- // Ignore any exceptions
- }
- // else work with 'plugin' as with usual Eclipse plugin
- {
- Class> PluginClass = Class.forName("org.eclipse.core.runtime.Plugin"); //$NON-NLS-1$
- if (PluginClass.isAssignableFrom(plugin.getClass())) {
- //
- Class> PathClass = Class.forName("org.eclipse.core.runtime.Path"); //$NON-NLS-1$
- Constructor> pathConstructor = PathClass.getConstructor(new Class[] { String.class });
- Object path = pathConstructor.newInstance(new Object[] { name });
- //
- Class> IPathClass = Class.forName("org.eclipse.core.runtime.IPath"); //$NON-NLS-1$
- Method findMethod = PluginClass.getMethod("find", new Class[] { IPathClass }); //$NON-NLS-1$
- return (URL) findMethod.invoke(plugin, new Object[] { path });
- }
- }
- return null;
- }
-
- ////////////////////////////////////////////////////////////////////////////
- //
- // General
- //
- ////////////////////////////////////////////////////////////////////////////
- /**
- * Dispose of cached objects and their underlying OS resources. This should only
- * be called when the cached objects are no longer needed (e.g. on application
- * shutdown).
- */
- public static void dispose() {
- disposeColors();
- disposeFonts();
- disposeImages();
- }
-}
\ No newline at end of file