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 + * + * @exception SWTException + * + * @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 + * + * @exception SWTException + * + * + * @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 + * + */ + public int getIncrement() { + checkWidget(); + return increment; + } + + /** + * Returns the 'lower selection', which is the lower receiver's position. + * + * @return the selection + * + * @exception SWTException + * + */ + public int getLowerValue() { + checkWidget(); + return lowerValue; + } + + /** + * Returns the maximum value which the receiver will allow. + * + * @return the maximum + * + * @exception SWTException + * + */ + public int getMaximum() { + checkWidget(); + return maximum; + } + + /** + * Returns the minimum value which the receiver will allow. + * + * @return the minimum + * + * @exception SWTException + * + */ + 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 + * + */ + 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 + * + */ + 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 + * + */ + 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 + * + * @exception SWTException + * + * + * @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 + * + */ + 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 + * + * @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 + * + * @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 + * + * @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 + * + */ + 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 + * + */ + 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 + * + */ + 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 + * + * @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 + * + * @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