package com.minres.scviewer.database.ui.swt.internal; import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Text; /** * Instances of this class are selectable user interface objects that allow the * user to enter and modify numeric values. *

*

*
Styles:
*
READ_ONLY, FLAT
*
Events:
*
Selection, Modify
*
*

*/ public class HorizontalSpinner extends Composite { private enum ALIGNMENT { LEFT, RIGHT, BOTH }; private final List modifyListeners = new ArrayList(); private Button leftButton; private Button rightButton; private Text text; private int digits = 0; private int increment = 1; private int maximum = 0; private int minimum = 255; private int pageIncrement = 10; private int storedValue = 0; private ALIGNMENT alignment = ALIGNMENT.BOTH; private final char decimalFormatSeparator; /** * 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 * * @exception IllegalArgumentException * * @exception SWTException * * * @see SWT#READ_ONLY * @see SWT#FLAT */ public HorizontalSpinner(final Composite parent, final int style) { super(parent, style); if ((style & SWT.LEFT) == SWT.LEFT) { alignment = ALIGNMENT.LEFT; } if ((style & SWT.RIGHT) == SWT.RIGHT) { alignment = ALIGNMENT.RIGHT; } final GridLayout gd = new GridLayout(3, false); gd.horizontalSpacing = gd.verticalSpacing = 0; gd.marginWidth = gd.marginHeight = 0; setLayout(gd); createContent(style); addTextListeners(); addButtonsListener(); addModifyListeners(); decimalFormatSeparator = new DecimalFormatSymbols().getDecimalSeparator(); } /** * Create the content of the widget * * @param style style of the widget */ private void createContent(final int style) { final boolean readOnly = (style & SWT.READ_ONLY) == SWT.READ_ONLY; final boolean flat = (style & SWT.FLAT) == SWT.FLAT; final int buttonStyle = SWT.ARROW | (flat ? SWT.FLAT : SWT.NONE); if (alignment == ALIGNMENT.BOTH) { createMinusButton(buttonStyle); createText(readOnly); createPlusButton(buttonStyle); } else if (alignment == ALIGNMENT.LEFT) { createMinusButton(buttonStyle); createPlusButton(buttonStyle); createText(readOnly); } else { createText(readOnly); createMinusButton(buttonStyle); createPlusButton(buttonStyle); } } /** * Create minus button * * @param buttonStyle button style */ private void createMinusButton(final int buttonStyle) { leftButton = new Button(this, buttonStyle | SWT.LEFT); leftButton.setFont(getFont()); leftButton.setBackground(getBackground()); leftButton.setCursor(getCursor()); leftButton.setEnabled(getEnabled()); leftButton.setFont(getFont()); leftButton.setForeground(getForeground()); leftButton.setLayoutData(new GridData(GridData.FILL, GridData.FILL, false, false)); } /** * Create the text zone * * @param readOnly if true, the text is read only */ private void createText(final boolean readOnly) { text = new Text(this, readOnly ? SWT.READ_ONLY : SWT.NONE); final GridData gd = new GridData(GridData.FILL, GridData.CENTER, true, false); gd.minimumWidth = 40; text.setLayoutData(gd); } /** * Create plus button * * @param buttonStyle button style */ private void createPlusButton(final int buttonStyle) { rightButton = new Button(this, buttonStyle | SWT.RIGHT); rightButton.setFont(getFont()); rightButton.setBackground(getBackground()); rightButton.setCursor(getCursor()); rightButton.setEnabled(getEnabled()); rightButton.setFont(getFont()); rightButton.setForeground(getForeground()); rightButton.setLayoutData(new GridData(GridData.FILL, GridData.FILL, false, false)); } /** * Add the text listeners */ private void addTextListeners() { text.addListener(SWT.Verify, e -> { if (e.character != 0 && !(Character.isDigit(e.character) || e.character == '-') && e.keyCode != SWT.BS && e.keyCode != SWT.DEL) { e.doit = false; return; } e.doit = verifyEntryAndStoreValue(e.text, e.keyCode); }); text.addListener(SWT.KeyUp, e -> { if (e.keyCode == SWT.ARROW_UP) { increaseValue(increment); } if (e.keyCode == SWT.ARROW_DOWN) { decreaseValue(increment); } if (e.keyCode == SWT.PAGE_UP) { increaseValue(pageIncrement); } if (e.keyCode == SWT.PAGE_DOWN) { decreaseValue(pageIncrement); } }); text.addListener(SWT.FocusOut, e -> { if (text.getText().trim().equals("")) { setSelection(storedValue); } }); } /** * Verify the entry and store the value in the field storedValue * * @param entry entry to check * @param keyCode code of the typed key * @return true if the entry if correct, false * otherwise */ private boolean verifyEntryAndStoreValue(final String entry, final int keyCode) { return true; } /** * Add the listener to the buttons */ private void addButtonsListener() { leftButton.addListener(SWT.Selection, e -> { decreaseValue(increment); }); rightButton.addListener(SWT.Selection, e -> { increaseValue(increment); }); } /** * Increase the value stored in this snippet * * @param value value to increase */ private void increaseValue(final int value) { setSelection(getSelection() + value); } /** * Decrease the value stored in this snippet * * @param value value to decrease */ private void decreaseValue(final int value) { setSelection(getSelection() - value); } /** * Add the modify listeners */ private void addModifyListeners() { text.addModifyListener(e -> { for (final ModifyListener m : modifyListeners) { m.modifyText(e); } }); } /** * Adds the listener to the collection of listeners who will be notified * when the receiver's text is modified, by sending it one of the messages * defined in the ModifyListener interface. * * @param listener the listener which should be notified * * @exception IllegalArgumentException * * @exception SWTException * * * @see ModifyListener * @see #removeModifyListener * @see org.eclipse.swt.widgets.Spinner#addModifyListener(org.eclipse.swt.events.ModifyListener) */ public void addModifyListener(final ModifyListener listener) { checkWidget(); modifyListeners.add(listener); } /** * Adds the listener to the collection of listeners who will be notified * when the control is selected by the user, by sending it one of the * messages defined in the SelectionListener interface. *

* widgetSelected is not called for texts. * widgetDefaultSelected is typically called when ENTER is * pressed in a single-line text. *

* * @param listener the listener which should be notified when the control is * selected by the user * * @exception IllegalArgumentException * * @exception SWTException * * * @see SelectionListener * @see #removeSelectionListener * @see SelectionEvent */ public void addSelectionListener(final SelectionListener listener) { checkWidget(); } /** * Copies the selected text. *

* The current selection is copied to the clipboard. *

* * @exception SWTException * */ public void copy() { checkWidget(); text.copy(); } /** * Cuts the selected text. *

* The current selection is first copied to the clipboard and then deleted * from the widget. *

* * @exception SWTException * */ public void cut() { checkWidget(); text.cut(); } /** * Returns the number of decimal places used by the receiver. * * @return the digits * * @exception SWTException * */ public int getDigits() { checkWidget(); return digits; } /** * Returns the amount that the receiver's value will be modified by when the * up/down arrows are pressed. * * @return the increment * * @exception SWTException * */ public int getIncrement() { checkWidget(); return increment; } /** * 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 receiver's position will be modified by when * the page up/down keys are pressed. * * @return the page increment * * @exception SWTException * */ public int getPageIncrement() { checkWidget(); return pageIncrement; } /** * Returns the selection, which is the receiver's position. * * @return the selection * * @exception SWTException * */ public int getSelection() { checkWidget(); return storedValue; } /** * Returns a string containing a copy of the contents of the receiver's text * field, or an empty string if there are no contents. * * @return the receiver's text * * @exception SWTException * * */ public String getText() { checkWidget(); return text.getText(); } /** * Returns the maximum number of characters that the receiver's text field * is capable of holding. If this has not been changed by * setTextLimit(), it will be the constant * Spinner.LIMIT. * * @return the text limit * * @exception SWTException * * * @see #LIMIT */ public int getTextLimit() { checkWidget(); return text.getTextLimit(); } /** * Pastes text from clipboard. *

* The selected text is deleted from the widget and new text inserted from * the clipboard. *

* * @exception SWTException * */ public void paste() { checkWidget(); text.paste(); } /** * Removes the listener from the collection of listeners who will be * notified when the receiver's text is modified. * * @param listener the listener which should no longer be notified * * @exception IllegalArgumentException * * @exception SWTException * * * @see ModifyListener * @see #addModifyListener */ public void removeModifyListener(final ModifyListener listener) { checkWidget(); modifyListeners.remove(listener); } /** * Removes the listener from the collection of listeners who will be * notified when the control is selected by the user. * * @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(); } /** * Sets the number of decimal places used by the receiver. *

* The digit setting is used to allow for floating point values in the * receiver. For example, to set the selection to a floating point value of * 1.37 call setDigits() with a value of 2 and setSelection() with a value * of 137. Similarly, if getDigits() has a value of 2 and getSelection() * returns 137 this should be interpreted as 1.37. This applies to all * numeric APIs. *

* * @param value the new digits (must be greater than or equal to zero) * * @exception IllegalArgumentException * * @exception SWTException * */ public void setDigits(final int value) { checkWidget(); digits = value; convertSelectionToStringValue(); } /** * Sets the amount that the receiver's value will be modified by when the * up/down arrows are pressed to the argument, which must be at least one. * * @param value the new increment (must be greater than zero) * * @exception SWTException * */ public void setIncrement(final int value) { checkWidget(); increment = value; } /** * Sets the maximum value that the receiver will allow. This new value will * be ignored if it is less 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 or equal to the * current minimum * * @exception SWTException * */ public void setMaximum(final int value) { checkWidget(); maximum = value; } /** * Sets the minimum value that the receiver will allow. This new value will * be ignored if it is greater 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 less than or equal to the * current maximum * * @exception SWTException * */ public void setMinimum(final int value) { checkWidget(); minimum = value; } /** * Sets the amount that the receiver's position will be modified by when the * page up/down keys are pressed to the argument, which must be at least * one. * * @param value the page increment (must be greater than zero) * * @exception SWTException * */ public void setPageIncrement(final int value) { checkWidget(); pageIncrement = value; } /** * Sets the selection, which is the receiver's position, to the * argument. If the argument is not within the range specified by minimum * and maximum, it will be adjusted to fall within this range. * * @param value the new selection (must be zero or greater) * * @exception SWTException * */ public void setSelection(int selection) { checkWidget(); if (selection < minimum) { selection = minimum; } else if (selection > maximum) { selection = maximum; } storedValue = selection; text.setText(convertSelectionToStringValue()); } /** * Convert the selection into a string * * @return the string representation of the selection */ private String convertSelectionToStringValue() { if (getDigits() == 0) { return String.valueOf(storedValue); } final StringBuilder unformatted = new StringBuilder(String.valueOf(storedValue * Math.pow(10, -1 * getDigits()))); for (int i = 0; i < digits; i++) { unformatted.append("0"); } final int position = unformatted.indexOf("."); final String temp = unformatted.substring(0, position + 1 + digits); return temp.replace('.', decimalFormatSeparator); } /** * Sets the maximum number of characters that the receiver's text field is * capable of holding to be the argument. *

* To reset this value to the default, use * setTextLimit(Spinner.LIMIT). Specifying a limit value larger * than Spinner.LIMIT sets the receiver's limit to * Spinner.LIMIT. *

* * @param limit new text limit * * @exception IllegalArgumentException * * @exception SWTException * * * @see #LIMIT */ public void setTextLimit(final int limit) { checkWidget(); text.setTextLimit(limit); } /** * Sets the receiver's selection, minimum value, maximum value, digits, * increment and page increment all at once. *

* Note: This is similar to setting the values individually using the * appropriate methods, but may be implemented in a more efficient fashion * on some platforms. *

* * @param selection the new selection value * @param minimum the new minimum value * @param maximum the new maximum value * @param digits the new digits value * @param increment the new increment value * @param pageIncrement the new pageIncrement value * * @exception SWTException * */ public void setValues(final int selection, final int minimum, final int maximum, final int digits, final int increment, final int pageIncrement) { setMinimum(minimum); setMaximum(maximum); setDigits(digits); setIncrement(increment); setPageIncrement(pageIncrement); setSelection(selection); } /** * Sets the receiver's drag detect state. If the argument is * true, the receiver will detect drag gestures, otherwise * these gestures will be ignored. * * @param dragDetect the new drag detect state * * @exception SWTException * */ @Override public boolean setFocus() { checkWidget(); return text.setFocus(); } /** * Forces the receiver to have the keyboard focus, causing all * keyboard events to be delivered to it. * * @return true if the control got focus, and * false if it was unable to. * * @exception SWTException * * * @see #setFocus */ @Override public boolean forceFocus() { checkWidget(); return text.forceFocus(); } /** * Sets the receiver's background color to the color specified by the * argument, or to the default system color for the control if the argument * is null. *

* Note: This operation is a hint and may be overridden by the platform. For * example, on Windows the background of a Button cannot be changed. *

* * @param color the new color (or null) * * @exception IllegalArgumentException * * @exception SWTException * */ @Override public void setBackground(final Color color) { super.setBackground(color); leftButton.setBackground(color); rightButton.setBackground(color); text.setBackground(color); } /** * Sets the receiver's background image to the image specified by the * argument, or to the default system color for the control if the argument * is null. The background image is tiled to fill the available space. *

* Note: This operation is a hint and may be overridden by the platform. For * example, on Windows the background of a Button cannot be changed. *

* * @param image the new image (or null) * * @exception IllegalArgumentException * * @exception SWTException * */ @Override public void setBackgroundImage(final Image image) { super.setBackgroundImage(image); leftButton.setBackgroundImage(image); rightButton.setBackgroundImage(image); text.setBackgroundImage(image); } /** * Sets the receiver's cursor to the cursor specified by the argument, or to * the default cursor for that kind of control if the argument is null. *

* When the mouse pointer passes over a control its appearance is changed to * match the control's cursor. *

* * @param cursor the new cursor (or null) * * @exception IllegalArgumentException * * @exception SWTException * */ @Override public void setCursor(final Cursor cursor) { super.setCursor(cursor); leftButton.setCursor(cursor); rightButton.setCursor(cursor); text.setCursor(cursor); } /** * Enables the receiver if the argument is true, and disables * it otherwise. A disabled control is typically not selectable from the * user interface and draws with an inactive or "grayed" look. * * @param enabled the new enabled state * * @exception SWTException * */ @Override public void setEnabled(final boolean enabled) { super.setEnabled(enabled); leftButton.setEnabled(enabled); rightButton.setEnabled(enabled); text.setEnabled(enabled); } /** * Sets the font that the receiver will use to paint textual information to * the font specified by the argument, or to the default font for that kind * of control if the argument is null. * * @param font the new font (or null) * * @exception IllegalArgumentException * * @exception SWTException * */ @Override public void setFont(final Font font) { super.setFont(font); text.setFont(font); } /** * Sets the receiver's foreground color to the color specified by the * argument, or to the default system color for the control if the argument * is null. *

* Note: This operation is a hint and may be overridden by the platform. *

* * @param color the new color (or null) * * @exception IllegalArgumentException * * @exception SWTException * */ @Override public void setForeground(final Color color) { super.setForeground(color); leftButton.setForeground(color); rightButton.setForeground(color); text.setForeground(color); } /** * Sets the receiver's pop up menu to the argument. All controls may * optionally have a pop up menu that is displayed when the user requests * one for the control. The sequence of key strokes, button presses and/or * button releases that are used to request a pop up menu is platform * specific. *

* Note: Disposing of a control that has a pop up menu will dispose of the * menu. To avoid this behavior, set the menu to null before the control is * disposed. *

* * @param menu the new pop up menu * * @exception IllegalArgumentException * * @exception SWTException * */ @Override public void setMenu(final Menu menu) { super.setMenu(menu); leftButton.setMenu(menu); rightButton.setMenu(menu); text.setMenu(menu); } /** * Sets the receiver's tool tip text to the argument, which may be null * indicating that the default tool tip for the control will be shown. For a * control that has a default tool tip, such as the Tree control on Windows, * setting the tool tip text to an empty string replaces the default, * causing no tool tip text to be shown. *

* The mnemonic indicator (character '&') is not displayed in a tool * tip. To display a single '&' in the tool tip, the character '&' * can be escaped by doubling it in the string. *

* * @param string the new tool tip text (or null) * * @exception SWTException * */ @Override public void setToolTipText(final String tooltipText) { super.setToolTipText(tooltipText); leftButton.setToolTipText(tooltipText); rightButton.setToolTipText(tooltipText); text.setToolTipText(tooltipText); } }