/**
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* 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.
*
*/
package gov.redhawk.common.ui.widgets;
import gov.redhawk.common.ui.internal.widgets.DvalSliderDialog;
import gov.redhawk.common.ui.internal.widgets.UpDown;
import java.text.DecimalFormat;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.events.VerifyListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
/**
* A control that simulates a control similar to the Midas Dval Control.
*/
public class Dval extends Composite {
private static final int SLIDER_DIALOG_SIZE = 50;
private static final double ONE_HALF = 0.5;
private static final double DEFAULT_VALUE = 0.0;
private static final double DEFAULT_MINIMUM = 0.0;
private static final double DEFAULT_MAXIMUM = 10.0;
private static final double DEFAULT_INCREMENT = 0.1;
private static final double DEFAULT_PAGE_INCREMENT = 1.0;
private static final int DEFAULT_NUM_DIGITS = 3;
/** The arrow. */
private final Button arrow;
/** The layout. */
private final GridLayout layout;
/** The textbox. */
private final Text textbox;
/** The updown. */
private final UpDown updown;
/** The digits. */
private int digits;
/** The maximum. */
private double maximum;
/** The minimum. */
private double minimum;
/** The increment. */
private double increment;
/** The pageincrement. */
private double pageincrement;
/** The form. */
private final DecimalFormat form;
/** The current value. */
private Double currentValue;
/** The sliderdlg. */
private final DvalSliderDialog sliderdlg;
/** The modify listeners. */
private ListenerList modifyListeners;
/** The default listeners. */
private final ListenerList defaultListeners = new ListenerList(ListenerList.IDENTITY);
/** The mouse down listener. */
private final Listener mouseDownListener = new Listener() {
@Override
public void handleEvent(final Event e) {
Dval.this.arrowMouseDown(e);
}
};
/** The verify listener. */
private final VerifyListener verifyListener = new VerifyListener() {
@Override
public void verifyText(final VerifyEvent e) {
Dval.this.verifyText(e);
}
};
/** The key listener. */
private final KeyListener keyListener = new KeyListener() {
@Override
public void keyPressed(final KeyEvent e) {
Dval.this.keyPressed(e);
}
@Override
public void keyReleased(final KeyEvent e) {
// Do nothing
}
};
/** The selection listener. */
private final SelectionListener selectionListener = new SelectionListener() {
@Override
public void widgetDefaultSelected(final SelectionEvent e) {
// Do nothing
}
@Override
public void widgetSelected(final SelectionEvent e) {
if (e.detail == SWT.ARROW_UP) {
Dval.this.increment();
} else {
Dval.this.decrement();
}
//make textbox gain focus, so user can indicate value commit by removing focus
getTextBox().setFocus();
}
};
/** The slider selection. */
private final Listener sliderSelection = new Listener() {
@Override
public void handleEvent(final Event e) {
final double value = Dval.this.sliderdlg.getSelection();
Dval.this.setValue(value);
fireModifiedEvent(new Event());
}
};
/** The shell listener. */
private final Listener shellListener = new Listener() {
@Override
public void handleEvent(final Event e) {
Dval.this.shellMoved(e);
}
};
/** The control listener. */
private final ControlListener controlListener = new ControlListener() {
@Override
public void controlMoved(final ControlEvent e) {
}
@Override
public void controlResized(final ControlEvent e) {
Dval.this.controlResized(e);
}
};
/** The modify listener. */
private final Listener modifyListener = new Listener() {
@Override
public void handleEvent(final Event e) {
Dval.this.controlModified(e);
}
};
/** The focus listener. */
private final FocusListener focusListener = new FocusListener() {
@Override
public void focusGained(final FocusEvent e) {
// Nothing to do
}
@Override
public void focusLost(final FocusEvent e) {
// When focus is lost, if the value in the text-box is invalid
// (i.e. it's greater than max or less then min) then make sure it
// is valid
final String widText = Dval.this.textbox.getText();
try {
Dval.this.setValue(Double.parseDouble(widText));
} catch (final NumberFormatException excep) {
// PASS
}
}
};
private final int precision;
private Listener deferredListener;
private Integer deferredListenerEventType;
/**
* The Constructor.
*
* @param composite Parent
*/
public Dval(final Composite composite) {
this(composite, SWT.None);
}
/**
* The Constructor.
*
* @param composite Parent
*/
public Dval(final Composite composite, final int style) {
super(composite, style);
this.getParent().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(final DisposeEvent e) {
dispose();
}
});
/* Create the base widgets */
final int NUM_COLUMNS = 3;
this.layout = new GridLayout(NUM_COLUMNS, false);
this.setLayout(this.layout);
final GridDataFactory factory = GridDataFactory.fillDefaults().grab(true, true);
this.textbox = new Text(this, SWT.RIGHT | SWT.BORDER | SWT.SINGLE);
if (this.deferredListener != null && this.deferredListenerEventType != null) {
this.textbox.addListener(this.deferredListenerEventType, this.deferredListener);
this.deferredListener = null;
this.deferredListenerEventType = null;
}
factory.applyTo(this.textbox);
this.updown = new UpDown(this, 0);
this.arrow = new Button(this, SWT.ARROW | SWT.DOWN | SWT.BORDER);
this.form = new DecimalFormat();
this.form.setGroupingUsed(false);
this.sliderdlg = new DvalSliderDialog(this.getShell());
this.precision = Integer.MIN_VALUE;
/* Set default range */
setValue(DEFAULT_VALUE);
setMinimum(DEFAULT_MINIMUM);
setMaximum(DEFAULT_MAXIMUM);
setIncrement(DEFAULT_INCREMENT);
setPageIncrement(DEFAULT_PAGE_INCREMENT);
setDigits(DEFAULT_NUM_DIGITS);
/* Handle Events */
this.arrow.addListener(SWT.MouseDown, this.mouseDownListener);
this.textbox.addVerifyListener(this.verifyListener);
this.textbox.addKeyListener(this.keyListener);
this.textbox.addListener(SWT.Modify, this.modifyListener);
this.textbox.addFocusListener(this.focusListener);
this.updown.addSelectionListener(this.selectionListener);
getShell().addListener(SWT.Move, this.shellListener);
addControlListener(this.controlListener);
}
/**
* {@inheritDoc}
*/
@Override
public void dispose() {
this.arrow.removeListener(SWT.MouseDown, this.mouseDownListener);
this.textbox.removeVerifyListener(this.verifyListener);
this.textbox.removeKeyListener(this.keyListener);
this.textbox.removeListener(SWT.Modify, this.modifyListener);
this.textbox.removeFocusListener(this.focusListener);
this.updown.removeSelectionListener(this.selectionListener);
if (this.sliderdlg != null) {
this.sliderdlg.close();
}
getShell().removeListener(SWT.Move, this.shellListener);
removeControlListener(this.controlListener);
super.dispose();
}
/**
* Arrow mouse down.
*
* @param e the e
*/
protected void arrowMouseDown(final Event e) {
if (this.sliderdlg != null) {
if (!this.sliderdlg.isOpen()) {
showSlider();
} else {
hideSlider();
//make textbox gain focus, so user can indicate value commit by removing focus
getTextBox().setFocus();
}
}
}
/**
* Increment.
*/
private void increment() {
setValue(this.currentValue + this.increment);
fireDefaultSelection();
}
/**
* Pageincrement.
*/
private void pageincrement() {
setValue(this.currentValue + this.pageincrement);
fireDefaultSelection();
}
/**
* Decrement.
*/
private void decrement() {
setValue(this.currentValue - this.increment);
fireDefaultSelection();
}
/**
* Pagedecrement.
*/
private void pagedecrement() {
setValue(this.currentValue - this.pageincrement);
fireDefaultSelection();
}
/**
* Checks if is text valid.
*
* @return true, if is text valid
*/
protected boolean isTextValid() {
try {
final double value = Double.parseDouble(this.textbox.getText());
return !((value > this.maximum) || (value < this.minimum));
} catch (final NumberFormatException e) {
// The verify input should prevent us from getting actual invalid
// entries, an exception here implies that either the box is blank
// or has started with a negative sign.
return false;
}
}
/**
* Apply a formating to the text within the dval.
*
* @param pattern the pattern
*/
public void applyFormat(final String pattern) {
this.form.applyPattern(pattern);
}
/**
* Sets the digits.
*
* @param value Number of digits to display
*/
public void setDigits(final int value) {
this.digits = value;
this.form.setMaximumFractionDigits(this.digits);
this.form.setMinimumFractionDigits(this.digits);
if (this.sliderdlg != null && this.sliderdlg.isOpen()) {
this.sliderdlg.setDigits(value);
}
}
/**
* Sets the increment.
*
* @param value Value to increment by
*/
public void setIncrement(final double value) {
this.increment = Math.floor(value * this.precision + ONE_HALF) / this.precision;
if (this.sliderdlg != null && this.sliderdlg.isOpen()) {
this.sliderdlg.setIncrement(value);
}
}
/**
* Sets the page increment.
*
* @param value Value to large increment by
*/
public void setPageIncrement(final double value) {
this.pageincrement = Math.floor(value * this.precision + ONE_HALF) / this.precision;
if (this.sliderdlg != null && this.sliderdlg.isOpen()) {
this.sliderdlg.setPageIncrement(value);
}
}
/**
* Sets the maximum.
*
* @param value Maximum value
*/
public void setMaximum(final double value) {
this.maximum = Math.floor(value * this.precision + ONE_HALF) / this.precision;
// Reset the value just in case it is now out of range
if (this.sliderdlg != null && this.sliderdlg.isOpen()) {
this.sliderdlg.setMaximum(value);
}
setValue(this.currentValue);
}
/**
* Sets the minimum.
*
* @param value Minimum value
*/
public void setMinimum(final double value) {
this.minimum = Math.floor(value * this.precision + ONE_HALF) / this.precision;
// Reset the value just in case it is now out of range
this.setValue(this.getValue());
if (this.sliderdlg != null && this.sliderdlg.isOpen()) {
this.sliderdlg.setMinimum(value);
}
setValue(this.currentValue);
}
/**
* Sets the value.
*
* @param value Set the current value
*/
public void setValue(final Double value) {
if (value == null) {
throw new NullPointerException("Dval Value can not be null");
}
this.currentValue = Math.floor(value * this.precision + ONE_HALF) / this.precision;
if (this.currentValue >= this.maximum) {
this.updown.getUpButton().setEnabled(false);
this.updown.getDownButton().setEnabled(true);
this.currentValue = this.maximum;
} else if (this.currentValue <= this.minimum) {
this.updown.getUpButton().setEnabled(true);
this.updown.getDownButton().setEnabled(false);
this.currentValue = this.minimum;
} else {
this.updown.getUpButton().setEnabled(true);
this.updown.getDownButton().setEnabled(true);
}
if (!this.textbox.isDisposed()) {
this.textbox.setText(this.form.format(this.currentValue));
}
if (this.sliderdlg != null && this.sliderdlg.isOpen()) {
this.sliderdlg.setSelection(this.currentValue);
}
}
/**
* Gets the digits.
*/
public int getDigits() {
return this.digits;
}
/**
* Gets the increment.
*/
public double getIncrement() {
return this.increment;
}
/**
* Sets the page increment.
*/
public double getPageIncrement() {
return this.pageincrement;
}
/**
* Gets the maximum.
*
* @return Maximum value
*/
public double getMaximum() {
return this.maximum;
}
/**
* Gets the minimum.
*
* @return Minimum value
*/
public double getMinimum() {
return this.minimum;
}
/**
* Gets the value.
*
* @return Current Value
*/
public Double getValue() {
return this.currentValue;
}
/**
* Fire default selection.
*/
private void fireDefaultSelection() {
final Event e = new Event();
e.type = SWT.DefaultSelection;
e.doit = true;
e.widget = this.textbox;
e.display = e.widget.getDisplay();
final Object[] listeners = this.defaultListeners.getListeners();
for (final Object listener : listeners) {
((Listener) listener).handleEvent(e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void addListener(final int eventType, final Listener listener) {
if (eventType == SWT.Modify) {
if (this.modifyListeners == null) {
this.modifyListeners = new ListenerList(ListenerList.IDENTITY);
}
this.modifyListeners.add(listener);
} else {
if (eventType == SWT.DefaultSelection) {
this.defaultListeners.add(listener);
}
if (this.textbox != null) {
this.textbox.addListener(eventType, listener);
} else {
this.deferredListener = listener;
this.deferredListenerEventType = eventType;
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void removeListener(final int eventType, final Listener handler) {
if (eventType == SWT.Modify) {
if (this.modifyListeners == null) {
return;
}
this.modifyListeners.remove(handler);
} else {
this.textbox.removeListener(eventType, handler);
}
}
/* Listener interface */
/**
* Verify text.
*
* @param e the e
*/
protected void verifyText(final VerifyEvent e) {
final String string = e.text;
final char[] chars = new char[string.length()];
string.getChars(0, chars.length, chars, 0);
for (int i = 0; i < chars.length; i++) {
if (!(('.' == chars[i]) || ('-' == chars[i]) || ('0' <= chars[i] && chars[i] <= '9') || ('e' == chars[i]) || ('E' == chars[i]))) {
e.doit = false;
this.setValue(this.currentValue);
return;
}
}
/*
* if (string.contains(".") && text.contains(".")) { e.doit = false;
* return; }
*/
}
/**
* Key pressed.
*
* @param e the e
*/
protected void keyPressed(final KeyEvent e) {
if (e.keyCode == SWT.ARROW_UP) {
increment();
} else if (e.keyCode == SWT.ARROW_DOWN) {
decrement();
} else if (e.keyCode == SWT.PAGE_UP) {
pageincrement();
} else if (e.keyCode == SWT.PAGE_DOWN) {
pagedecrement();
}
}
/**
* Sets the text background.
*
* @param color the new text background
*/
public void setTextBackground(final Color color) {
this.textbox.setBackground(color);
}
/**
* Control resized.
*
* @param e the e
*/
protected void controlResized(final ControlEvent e) {
if (this.sliderdlg != null && this.sliderdlg.isOpen()) {
this.sliderdlg.setSize(this.getSize().x, Dval.SLIDER_DIALOG_SIZE);
}
}
/**
* Shell moved.
*
* @param e the e
*/
protected void shellMoved(final Event e) {
if (this.isDisposed()) {
return;
}
if ((this.sliderdlg != null) && (this.sliderdlg.isOpen())) {
final Point pos = this.getDisplay().map(this, null, this.textbox.getLocation());
pos.y += this.textbox.getSize().y;
this.sliderdlg.getShell().setLocation(pos.x, pos.y);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setEnabled(final boolean enabled) {
super.setEnabled(enabled);
this.textbox.setEnabled(enabled);
this.updown.setEnabled(enabled);
this.arrow.setEnabled(enabled);
if (!enabled) {
hideSlider();
}
}
/**
* Control modified.
*
* @param e the e
*/
protected void controlModified(final Event e) {
final String widText = this.textbox.getText();
try {
this.currentValue = Double.parseDouble(widText);
fireModifiedEvent(e);
} catch (final NumberFormatException excep) {
// PASS
}
}
/**
* Fire modified event.
*
* @param e the e
*/
protected void fireModifiedEvent(final Event e) {
e.item = this;
e.type = SWT.Modify;
if (this.modifyListeners != null) {
for (final Object listener : this.modifyListeners.getListeners()) {
((Listener) listener).handleEvent(e);
}
}
}
/**
* Show slider.
* @since 1.1
*/
public void showSlider() {
checkWidget();
if (this.sliderdlg != null && !this.sliderdlg.isOpen()) {
this.arrow.setAlignment(SWT.UP);
final Point pos = this.getDisplay().map(this, null, this.textbox.getLocation());
pos.y += this.textbox.getSize().y;
final Point size = this.getSize();
size.y = Dval.SLIDER_DIALOG_SIZE;
this.sliderdlg.open(pos,
size,
this.getValue(),
this.getDigits(),
this.getMinimum(),
this.getMaximum(),
this.getIncrement(),
this.getPageIncrement());
this.sliderdlg.getSlider().addListener(SWT.Selection, this.sliderSelection);
this.sliderdlg.getSlider().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(final DisposeEvent e) {
if (Dval.this.sliderdlg != null && Dval.this.sliderdlg.getSlider() != null && !Dval.this.sliderdlg.getSlider().isDisposed()) {
Dval.this.sliderdlg.getSlider().removeListener(SWT.Selection, Dval.this.sliderSelection);
}
}
});
}
}
/**
* Hide slider.
* @since 1.1
*/
public void hideSlider() {
checkWidget();
if (this.sliderdlg != null && this.sliderdlg.isOpen()) {
this.arrow.setAlignment(SWT.DOWN);
this.sliderdlg.close();
}
}
/**
* @since 2.0
*/
@Override
public void setVisible(final boolean visible) {
if (!visible) {
hideSlider();
}
super.setVisible(visible);
}
public Control getTextBox() {
return this.textbox;
}
public Composite getUpdown() {
return this.updown;
}
public Button getArrow() {
return this.arrow;
}
public void setParseIntegerOnly(final boolean parseIntegerOnly) {
this.form.setParseIntegerOnly(parseIntegerOnly);
}
}