diff options
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/core/controls/CSpinner.java')
-rw-r--r-- | propertysheet/src/org/eclipse/wb/core/controls/CSpinner.java | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CSpinner.java b/propertysheet/src/org/eclipse/wb/core/controls/CSpinner.java new file mode 100644 index 0000000..d414df9 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/core/controls/CSpinner.java @@ -0,0 +1,569 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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 + *******************************************************************************/ +package org.eclipse.wb.core.controls; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Layout; +import org.eclipse.swt.widgets.Spinner; +import org.eclipse.swt.widgets.Text; + +import java.text.DecimalFormat; +import java.text.MessageFormat; +import java.text.ParseException; + +/** + * Custom implementation of {@link Spinner}. + * + * @author scheglov_ke + * @coverage core.control + */ +public class CSpinner extends Composite { + private static final Color COLOR_VALID = Display.getCurrent().getSystemColor( + SWT.COLOR_LIST_BACKGROUND); + private static final Color COLOR_INVALID = new Color(null, 255, 230, 230); + private int m_minimum = 0; + private int m_maximum = 100; + private int m_increment = 1; + private int m_value = 0; + private int m_multiplier = 1; + private String m_formatPattern = "0"; + private DecimalFormat m_format = new DecimalFormat(m_formatPattern); + //////////////////////////////////////////////////////////////////////////// + // + // GUI fields + // + //////////////////////////////////////////////////////////////////////////// + private final Button m_button; + private final Text m_text; + private final Spinner m_spinner; + private Composite win32Hack; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public CSpinner(Composite parent, int style) { + super(parent, style); + m_button = new Button(this, SWT.ARROW | SWT.DOWN); + { + int textStyle = SWT.SINGLE | SWT.RIGHT; + if (IS_OS_MAC_OSX_COCOA) { + textStyle |= SWT.BORDER; + } + m_text = new Text(this, textStyle); + m_text.setText("" + m_value); + m_text.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.ARROW_DOWN) { + e.doit = false; + updateValue(e.keyCode); + } + } + + @Override + public void keyReleased(KeyEvent e) { + try { + m_value = (int) (m_format.parse(m_text.getText()).doubleValue() * m_multiplier); + if (m_value < m_minimum || m_value > m_maximum) { + m_text.setBackground(COLOR_INVALID); + setState(MessageFormat.format( + Messages.CSpinner_outOfRange, + m_value, + m_minimum, + m_maximum)); + notifySelectionListeners(false); + } else { + setState(null); + notifySelectionListeners(true); + } + } catch (ParseException ex) { + setState(MessageFormat.format( + Messages.CSpinner_canNotParse, + m_text.getText(), + m_formatPattern)); + notifySelectionListeners(false); + } + } + }); + } + if (!IS_OS_MAC_OSX) { + win32Hack = new Composite(this, SWT.NONE); + win32Hack.setBackground(getDisplay().getSystemColor(SWT.COLOR_WHITE)); + win32Hack.moveAbove(null); + win32Hack.moveBelow(m_text); + } + { + m_spinner = new Spinner(this, SWT.VERTICAL); + m_spinner.setMinimum(0); + m_spinner.setMaximum(50); + m_spinner.setIncrement(1); + m_spinner.setPageIncrement(1); + m_spinner.setSelection(25); + m_spinner.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + setFocus(); + } + }); + m_spinner.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + m_text.forceFocus(); + if (m_spinner.getSelection() > 25) { + updateValue(SWT.ARROW_UP); + } else { + updateValue(SWT.ARROW_DOWN); + } + m_spinner.setSelection(25); + } + }); + setBackground(getDisplay().getSystemColor(SWT.COLOR_WHITE)); + if (IS_OS_WINDOWS_XP || IS_OS_WINDOWS_2003) { + setLayout(new WindowsXpLayout()); + } else if (IS_OS_WINDOWS_VISTA || IS_OS_WINDOWS_7) { + setLayout(new WindowsVistaLayout()); + } else if (IS_OS_LINUX) { + setLayout(new LinuxLayout()); + } else if (IS_OS_MAC_OSX) { + if (IS_OS_MAC_OSX_COCOA) { + setLayout(new MacCocoaLayout()); + } else { + setLayout(new MacLayout()); + } + } else { + setLayout(new WindowsXpLayout()); + } + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + m_text.setEnabled(enabled); + m_spinner.setEnabled(enabled); + } + + /** + * Sets the number of decimal places used by the receiver. + * <p> + * See {@link Spinner#setDigits(int)}. + */ + public void setDigits(int digits) { + m_formatPattern = "0."; + m_multiplier = 1; + for (int i = 0; i < digits; i++) { + m_formatPattern += "0"; + m_multiplier *= 10; + } + m_format = new DecimalFormat(m_formatPattern); + updateText(); + } + + /** + * Sets minimum and maximum using single invocation. + */ + public void setRange(int minimum, int maximum) { + setMinimum(minimum); + setMaximum(maximum); + } + + /** + * @return the minimum value that the receiver will allow. + */ + public int getMinimum() { + return m_minimum; + } + + /** + * Sets the minimum value that the receiver will allow. + */ + public void setMinimum(int minimum) { + m_minimum = minimum; + setSelection(Math.max(m_value, m_minimum)); + } + + /** + * Sets the maximum value that the receiver will allow. + */ + public void setMaximum(int maximum) { + m_maximum = maximum; + setSelection(Math.min(m_value, m_maximum)); + } + + /** + * 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. + */ + public void setIncrement(int increment) { + m_increment = increment; + } + + /** + * Sets the <em>value</em>, 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. + */ + public void setSelection(int newValue) { + newValue = Math.min(Math.max(m_minimum, newValue), m_maximum); + if (newValue != m_value) { + m_value = newValue; + updateText(); + // set valid state + setState(null); + } + } + + private void updateText() { + String text = m_format.format((double) m_value / m_multiplier); + m_text.setText(text); + m_text.selectAll(); + } + + /** + * @return the <em>selection</em>, which is the receiver's position. + */ + public int getSelection() { + return m_value; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Update + // + //////////////////////////////////////////////////////////////////////////// + /** + * Updates {@link #m_value} into given direction. + */ + private void updateValue(int direction) { + // prepare new value + int newValue; + { + newValue = m_value; + if (direction == SWT.ARROW_UP) { + newValue += m_increment; + } + if (direction == SWT.ARROW_DOWN) { + newValue -= m_increment; + } + } + // update value + setSelection(newValue); + notifySelectionListeners(true); + } + + /** + * Sets the valid/invalid state. + * + * @param message + * the message to show, or <code>null</code> if valid. + */ + private void setState(String message) { + m_text.setToolTipText(message); + if (message == null) { + m_text.setBackground(COLOR_VALID); + } else { + m_text.setBackground(COLOR_INVALID); + } + } + + /** + * Notifies {@link SWT#Selection} listeners with value and state. + */ + private void notifySelectionListeners(boolean valid) { + Event event = new Event(); + event.detail = m_value; + event.doit = valid; + notifyListeners(SWT.Selection, event); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Windows XP + // + //////////////////////////////////////////////////////////////////////////// + /** + * Implementation of {@link Layout} for Windows XP. + */ + private class WindowsXpLayout extends Layout { + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + Point size = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + size.x += m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT).x - m_spinner.getClientArea().width; + // add Text widget margin + size.y += 2; + // apply hints + if (wHint != SWT.DEFAULT) { + size.x = Math.min(size.x, wHint); + } + if (hHint != SWT.DEFAULT) { + size.y = Math.min(size.y, hHint); + } + // OK, final size + return size; + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle cRect = composite.getClientArea(); + if (cRect.isEmpty()) { + return; + } + // prepare size of Text + Point tSize = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + // prepare size of Spinner + Point sSize; + sSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); + sSize.y = Math.min(sSize.y, Math.min(tSize.y, cRect.height)); + sSize.x = Math.min(sSize.x, cRect.width); + // prepare width of arrows part of Spinner + int arrowWidth = m_button.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; + // set bounds for Spinner and Text + m_spinner.setBounds( + cRect.x + cRect.width - sSize.x + 1, + cRect.y - 1, + sSize.x, + cRect.height + 2); + m_text.setBounds(cRect.x, cRect.y + 1, cRect.width - arrowWidth, tSize.y); + win32Hack.setBounds(cRect.x, cRect.y, cRect.width - arrowWidth, sSize.y); + } + } + //////////////////////////////////////////////////////////////////////////// + // + // Windows Vista + // + //////////////////////////////////////////////////////////////////////////// + /** + * Implementation of {@link Layout} for Windows Vista. + */ + private class WindowsVistaLayout extends Layout { + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + Point size = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + size.x += m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT).x - m_spinner.getClientArea().width; + // add Text widget margin + size.y += 3; + // apply hints + if (wHint != SWT.DEFAULT) { + size.x = Math.min(size.x, wHint); + } + if (hHint != SWT.DEFAULT) { + size.y = Math.min(size.y, hHint); + } + // OK, final size + return size; + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle cRect = composite.getClientArea(); + if (cRect.isEmpty()) { + return; + } + // prepare size of Text + Point tSize = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + // prepare size of Spinner + Point sSize; + sSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); + sSize.y = Math.min(sSize.y, Math.min(tSize.y, cRect.height)); + sSize.x = Math.min(sSize.x, cRect.width); + // prepare width of arrows part of Spinner + int arrowWidth = m_button.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; + // set bounds for Spinner and Text + m_spinner.setBounds( + cRect.x + cRect.width - sSize.x + 1, + cRect.y - 1, + sSize.x, + cRect.height + 2); + m_text.setBounds(cRect.x, cRect.y + 1, cRect.width - arrowWidth, tSize.y); + win32Hack.setBounds(cRect.x, cRect.y, cRect.width - arrowWidth, sSize.y); + } + } + //////////////////////////////////////////////////////////////////////////// + // + // Linux + // + //////////////////////////////////////////////////////////////////////////// + /** + * Implementation of {@link Layout} for Linux. + */ + private class LinuxLayout extends Layout { + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + Point size = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + size.x += m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT).x - m_spinner.getClientArea().width; + // apply hints + if (wHint != SWT.DEFAULT) { + size.x = Math.min(size.x, wHint); + } + if (hHint != SWT.DEFAULT) { + size.y = Math.min(size.y, hHint); + } + // OK, final size + return size; + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle cRect = composite.getClientArea(); + if (cRect.isEmpty()) { + return; + } + // prepare size of Text + Point tSize = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + // prepare size of Spinner + Point sSize; + sSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); + sSize.y = Math.min(sSize.y, Math.min(tSize.y, cRect.height)); + sSize.x = Math.min(sSize.x, cRect.width); + // prepare width of arrows part of Spinner + int arrowWidth; + { + m_spinner.setSize(sSize); + arrowWidth = sSize.x - m_spinner.getClientArea().width; + } + // set bounds for Spinner and Text + m_spinner.setBounds(cRect.x + cRect.width - sSize.x, cRect.y - 2, sSize.x, cRect.height + 4); + m_text.setBounds(cRect.x, cRect.y, cRect.width - arrowWidth, tSize.y); + } + } + //////////////////////////////////////////////////////////////////////////// + // + // MacOSX + // + //////////////////////////////////////////////////////////////////////////// + /** + * Implementation of {@link Layout} for MacOSX. + */ + private class MacLayout extends Layout { + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + Point size = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + size.x += m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT).x - m_spinner.getClientArea().width; + // add Text widget margin + size.y += 4; + // apply hints + if (wHint != SWT.DEFAULT) { + size.x = Math.min(size.x, wHint); + } + if (hHint != SWT.DEFAULT) { + size.y = Math.min(size.y, hHint); + } + // OK, final size + return size; + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle cRect = composite.getClientArea(); + if (cRect.isEmpty()) { + return; + } + // prepare size of Text + Point tSize = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + tSize.y += 4; + // prepare size of Spinner + Point sSize; + sSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); + sSize.y = Math.min(sSize.y, Math.min(tSize.y, cRect.height)); + sSize.x = Math.min(sSize.x, cRect.width); + // prepare width of arrows part of Spinner + int arrowWidth = m_button.computeSize(-1, -1).x; + // set bounds for Spinner and Text + m_spinner.setBounds(cRect.x + cRect.width - sSize.x, cRect.y, sSize.x, cRect.height); + m_text.setBounds(cRect.x, cRect.y + 2, cRect.width - arrowWidth - 2, tSize.y); + } + } + /** + * Implementation of {@link Layout} for MacOSX Cocoa. + */ + private class MacCocoaLayout extends Layout { + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + Point textSize = m_text.computeSize(SWT.DEFAULT, SWT.DEFAULT); + Point spinnerSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT); + int arrowWidth = m_button.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; + int width = textSize.x + arrowWidth; + int height = Math.max(spinnerSize.y, textSize.y); + // apply hints + if (wHint != SWT.DEFAULT) { + width = Math.min(width, wHint); + } + if (hHint != SWT.DEFAULT) { + height = Math.min(height, hHint); + } + return new Point(width, height); + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle clientArea = composite.getClientArea(); + if (clientArea.isEmpty()) { + return; + } + // prepare size of Spinner + Point spinnerSize = m_spinner.computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache); + // prepare width of arrows part of Spinner + int arrowWidth = m_button.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; + m_spinner.setBounds(clientArea.x + clientArea.width - arrowWidth - 1, clientArea.y + + clientArea.height + - spinnerSize.y, arrowWidth + 2, spinnerSize.y); + m_text.setBounds( + clientArea.x + 2, + clientArea.y + 2, + clientArea.width - arrowWidth - 5, + clientArea.y + clientArea.height - 4); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // System utils + // + //////////////////////////////////////////////////////////////////////////// + private static final String OS_NAME = System.getProperty("os.name"); + private static final String OS_VERSION = System.getProperty("os.version"); + private static final String WS_TYPE = SWT.getPlatform(); + private static final boolean IS_OS_MAC_OSX = isOS("Mac OS X"); + private static final boolean IS_OS_MAC_OSX_COCOA = IS_OS_MAC_OSX && "cocoa".equals(WS_TYPE); + private static final boolean IS_OS_LINUX = isOS("Linux") || isOS("LINUX"); + private static final boolean IS_OS_WINDOWS_XP = isWindowsVersion("5.1"); + private static final boolean IS_OS_WINDOWS_2003 = isWindowsVersion("5.2"); + private static final boolean IS_OS_WINDOWS_VISTA = isWindowsVersion("6.0"); + private static final boolean IS_OS_WINDOWS_7 = isWindowsVersion("6.1"); + + private static boolean isOS(String osName) { + return OS_NAME != null && OS_NAME.startsWith(osName); + } + + private static boolean isWindowsVersion(String windowsVersion) { + return isOS("Windows") && OS_VERSION != null && OS_VERSION.startsWith(windowsVersion); + } +} |