summaryrefslogtreecommitdiff
path: root/propertysheet/src/org/eclipse/wb/core/controls
diff options
context:
space:
mode:
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/core/controls')
-rw-r--r--propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java510
-rw-r--r--propertysheet/src/org/eclipse/wb/core/controls/CComboBox.java664
-rw-r--r--propertysheet/src/org/eclipse/wb/core/controls/CFlatButton.java160
-rw-r--r--propertysheet/src/org/eclipse/wb/core/controls/CImageLabel.java140
-rw-r--r--propertysheet/src/org/eclipse/wb/core/controls/CSpinner.java569
-rw-r--r--propertysheet/src/org/eclipse/wb/core/controls/Messages.java16
-rw-r--r--propertysheet/src/org/eclipse/wb/core/controls/messages.properties2
7 files changed, 2061 insertions, 0 deletions
diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java b/propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java
new file mode 100644
index 0000000..8782e96
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/core/controls/CCombo3.java
@@ -0,0 +1,510 @@
+/*******************************************************************************
+ * 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.wb.draw2d.IColorConstants;
+import org.eclipse.wb.internal.core.model.property.table.PropertyTable;
+import org.eclipse.wb.internal.core.utils.binding.editors.controls.DefaultControlActionsManager;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.TypedListener;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * Combo control for {@link PropertyTable} and combo property editors.
+ *
+ * @author scheglov_ke
+ * @coverage core.control
+ */
+public class CCombo3 extends Composite {
+ private final long m_createTime = System.currentTimeMillis();
+ private final CImageLabel m_text;
+ private final Button m_arrow;
+ private final Shell m_popup;
+ private final Table m_table;
+ private boolean m_fullDropdownTableSize = false;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public CCombo3(Composite parent, int style) {
+ super(parent, style);
+ addEvents(this, m_comboListener, new int[]{SWT.Dispose, SWT.Move, SWT.Resize});
+ // create label
+ {
+ m_text = new CImageLabel(this, SWT.NONE);
+ new DefaultControlActionsManager(m_text);
+ addEvents(m_text, m_textListener, new int[]{
+ SWT.KeyDown,
+ SWT.KeyUp,
+ SWT.MouseDown,
+ SWT.MouseUp,
+ SWT.MouseMove,
+ SWT.MouseDoubleClick,
+ SWT.Traverse,
+ SWT.FocusIn,
+ SWT.FocusOut});
+ }
+ // create arrow
+ {
+ m_arrow = new Button(this, SWT.ARROW | SWT.DOWN);
+ addEvents(m_arrow, m_arrowListener, new int[]{SWT.Selection, SWT.FocusIn, SWT.FocusOut});
+ }
+ // create popup Shell
+ {
+ Shell shell = getShell();
+ m_popup = new Shell(shell, SWT.NONE);
+ m_popup.setLayout(new FillLayout());
+ }
+ // create table for items
+ {
+ m_table = new Table(m_popup, SWT.FULL_SELECTION);
+ addEvents(m_table, m_tableListener, new int[]{SWT.Selection, SWT.FocusIn, SWT.FocusOut});
+ //
+ new TableColumn(m_table, SWT.NONE);
+ }
+ // Focus tracking filter
+ {
+ final Listener filter = new Listener() {
+ private boolean hasFocus;
+
+ public void handleEvent(Event event) {
+ boolean old_hasFocus = hasFocus;
+ hasFocus =
+ m_text.isFocusControl()
+ || m_arrow.isFocusControl()
+ || m_popup.isFocusControl()
+ || m_table.isFocusControl();
+ // configure colors
+ if (hasFocus) {
+ m_text.setBackground(IColorConstants.listSelection);
+ m_text.setForeground(IColorConstants.listSelectionText);
+ } else {
+ m_text.setBackground(IColorConstants.listBackground);
+ m_text.setForeground(IColorConstants.listForeground);
+ }
+ // send FocusOut event
+ if (old_hasFocus && !hasFocus) {
+ Event e = new Event();
+ e.widget = CCombo3.this;
+ e.time = event.time;
+ notifyListeners(SWT.FocusOut, e);
+ }
+ }
+ };
+ getDisplay().addFilter(SWT.FocusIn, filter);
+ addListener(SWT.Dispose, new Listener() {
+ public void handleEvent(Event event) {
+ getDisplay().removeFilter(SWT.FocusIn, filter);
+ }
+ });
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Events handling
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private final Listener m_comboListener = new Listener() {
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ case SWT.Dispose :
+ if (!m_popup.isDisposed()) {
+ m_popup.dispose();
+ }
+ break;
+ case SWT.Move :
+ doDropDown(false);
+ break;
+ case SWT.Resize :
+ doResize();
+ break;
+ }
+ }
+ };
+ private final Listener m_textListener = new Listener() {
+ public void handleEvent(final Event event) {
+ switch (event.type) {
+ case SWT.MouseDown :
+ if (System.currentTimeMillis() - m_createTime < 400) {
+ // send "logical" double click for case when we just activated combo
+ // and almost right away click second time (but first time on editor)
+ event.detail = -1;
+ notifyListeners(SWT.MouseDoubleClick, event);
+ // when we use "auto drop on editor activation" option, this click is
+ // is "logically" second one, so it should close combo
+ if (!isDisposed()) {
+ doDropDown(false);
+ }
+ } else {
+ m_text.setCapture(true);
+ doDropDown(!isDropped());
+ }
+ break;
+ case SWT.MouseUp : {
+ m_text.setCapture(false);
+ TableItem item = getItemUnderCursor(event);
+ if (item != null) {
+ doDropDown(false);
+ sendSelectionEvent(event);
+ }
+ break;
+ }
+ case SWT.MouseDoubleClick :
+ // prevent resending MouseDoubleClick that we sent on fast MouseDown
+ if (event.detail != -1) {
+ notifyListeners(SWT.MouseDoubleClick, event);
+ }
+ break;
+ case SWT.MouseMove : {
+ TableItem item = getItemUnderCursor(event);
+ if (item != null) {
+ m_table.setSelection(new TableItem[]{item});
+ }
+ break;
+ }
+ case SWT.KeyDown : {
+ // check for keyboard navigation and selection
+ {
+ int selectionIndex = m_table.getSelectionIndex();
+ if (event.keyCode == SWT.ARROW_UP) {
+ selectionIndex--;
+ if (selectionIndex < 0) {
+ selectionIndex = m_table.getItemCount() - 1;
+ }
+ m_table.setSelection(selectionIndex);
+ return;
+ } else if (event.keyCode == SWT.ARROW_DOWN) {
+ m_table.setSelection((selectionIndex + 1) % m_table.getItemCount());
+ return;
+ } else if (event.character == SWT.CR || event.character == ' ') {
+ sendSelectionEvent(event);
+ return;
+ }
+ }
+ // be default just resend event
+ resendKeyEvent(event);
+ break;
+ }
+ case SWT.KeyUp :
+ resendKeyEvent(event);
+ break;
+ }
+ }
+
+ private TableItem getItemUnderCursor(Event event) {
+ Point displayLocation = m_text.toDisplay(new Point(event.x, event.y));
+ Point tableLocation = m_table.toControl(displayLocation);
+ return m_table.getItem(tableLocation);
+ }
+ };
+ private final Listener m_arrowListener = new Listener() {
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ /*case SWT.FocusIn : {
+ resendFocusEvent(event);
+ break;
+ }*/
+ case SWT.Selection : {
+ doDropDown(!isDropped());
+ break;
+ }
+ }
+ }
+ };
+ private final Listener m_tableListener = new Listener() {
+ public void handleEvent(Event event) {
+ switch (event.type) {
+ case SWT.Selection : {
+ doDropDown(false);
+ // show selected item in text
+ {
+ int index = m_table.getSelectionIndex();
+ select(index);
+ }
+ // send selection event
+ sendSelectionEvent(event);
+ break;
+ }
+ }
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Events utils
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sends selection event.
+ */
+ private void sendSelectionEvent(Event event) {
+ Event e = new Event();
+ e.time = event.time;
+ e.stateMask = event.stateMask;
+ notifyListeners(SWT.Selection, e);
+ }
+
+ /**
+ * Resends KeyDown/KeyUp events.
+ */
+ private void resendKeyEvent(Event event) {
+ Event e = new Event();
+ e.time = event.time;
+ e.character = event.character;
+ e.keyCode = event.keyCode;
+ e.stateMask = event.stateMask;
+ notifyListeners(event.type, e);
+ }
+
+ /**
+ * Adds given listener as handler for events in given widget.
+ */
+ private void addEvents(Widget widget, Listener listener, int[] events) {
+ for (int i = 0; i < events.length; i++) {
+ widget.addListener(events[i], listener);
+ }
+ }
+
+ /**
+ * Adds the listener to receive events.
+ */
+ public void addSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ TypedListener typedListener = new TypedListener(listener);
+ addListener(SWT.Selection, typedListener);
+ addListener(SWT.DefaultSelection, typedListener);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Activity
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Sets drop state of combo.
+ */
+ public void doDropDown(boolean drop) {
+ // check, may be we already in this drop state
+ if (drop == isDropped()) {
+ return;
+ }
+ // close combo
+ if (!drop) {
+ m_popup.setVisible(false);
+ m_text.setFocus();
+ return;
+ }
+ // open combo
+ {
+ // prepare popup location
+ Point comboSize = getSize();
+ Point popupLocation;
+ {
+ //popupLocation = getParent().toDisplay(getLocation());
+ popupLocation = toDisplay(new Point(0, 0));
+ popupLocation.y += comboSize.y;
+ }
+ // calculate and set popup location
+ {
+ TableColumn tableColumn = m_table.getColumn(0);
+ // pack everything
+ tableColumn.pack();
+ m_table.pack();
+ m_popup.pack();
+ // calculate bounds
+ Rectangle tableBounds = m_table.getBounds();
+ tableBounds.height = Math.min(tableBounds.height, m_table.getItemHeight() * 20); // max 20 items without scrolling
+ m_table.setBounds(tableBounds);
+ // calculate size
+ int remainingDisplayHeight = getDisplay().getClientArea().height - popupLocation.y - 10;
+ int preferredHeight = Math.min(tableBounds.height, remainingDisplayHeight);
+ int remainingDisplayWidth = getDisplay().getClientArea().width - popupLocation.x - 5;
+ int preferredWidth =
+ isFullDropdownTableWidth()
+ ? Math.min(tableBounds.width, remainingDisplayWidth)
+ : comboSize.x;
+ // set popup bounds calculated as computeTrim basing on combo width and table height paying attention on remaining display space
+ Rectangle popupBounds =
+ m_popup.computeTrim(popupLocation.x, popupLocation.y, preferredWidth, preferredHeight);
+ m_popup.setBounds(popupBounds);
+ // adjust column size
+ tableColumn.setWidth(m_table.getClientArea().width);
+ }
+ m_popup.setVisible(true);
+ // scroll to selection if needed
+ m_table.showSelection();
+ }
+ }
+
+ /**
+ * Initiates "press-hold-drag" sequence.
+ */
+ public void startDrag() {
+ m_text.setCapture(true);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Access
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public void setFullDropdownTableWidth(boolean freeTableSize) {
+ m_fullDropdownTableSize = freeTableSize;
+ }
+
+ public boolean isFullDropdownTableWidth() {
+ return m_fullDropdownTableSize;
+ }
+
+ public boolean isDropped() {
+ return m_popup.isVisible();
+ }
+
+ public void setQuickSearch(boolean value) {
+ // TODO
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Access: items
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Removes all items.
+ */
+ public void removeAll() {
+ TableItem[] items = m_table.getItems();
+ for (int index = 0; index < items.length; index++) {
+ TableItem item = items[index];
+ item.dispose();
+ }
+ }
+
+ /**
+ * Adds new item with given text.
+ */
+ public void add(String text) {
+ add(text, null);
+ }
+
+ /**
+ * Adds new item with given text and image.
+ */
+ public void add(String text, Image image) {
+ checkWidget();
+ TableItem item = new TableItem(m_table, SWT.NONE);
+ item.setText(text);
+ item.setImage(image);
+ }
+
+ /**
+ * @return an item at given index
+ */
+ public String getItem(int index) {
+ checkWidget();
+ return m_table.getItem(index).getText();
+ }
+
+ /**
+ * @return the number of items
+ */
+ public int getItemCount() {
+ checkWidget();
+ return m_table.getItemCount();
+ }
+
+ /**
+ * @return the index of the selected item
+ */
+ public int getSelectionIndex() {
+ checkWidget();
+ return m_table.getSelectionIndex();
+ }
+
+ /**
+ * Selects an item with given index.
+ */
+ public void select(int index) {
+ checkWidget();
+ if (index == -1) {
+ m_table.deselectAll();
+ m_text.setText(null);
+ m_text.setImage(null);
+ return;
+ } else {
+ TableItem item = m_table.getItem(index);
+ m_text.setText(item.getText());
+ m_text.setImage(item.getImage());
+ m_table.select(index);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Access: text and image
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ /**
+ * Selects item with given text.
+ */
+ public void setText(String text) {
+ // try to find item with given text
+ TableItem[] items = m_table.getItems();
+ for (int index = 0; index < items.length; index++) {
+ TableItem item = items[index];
+ if (item.getText().equals(text)) {
+ select(index);
+ return;
+ }
+ }
+ // not found, remove selection
+ select(-1);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Resize support
+ // TODO: computeSize
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ protected void doResize() {
+ Rectangle clientArea = getClientArea();
+ int areaWidth = clientArea.width;
+ int areaHeight = clientArea.height;
+ // compute sizes of controls
+ Point buttonSize = m_arrow.computeSize(areaHeight, areaHeight);
+ Point textSize = m_text.computeSize(areaWidth - buttonSize.x, areaHeight);
+ // set controls location/size
+ m_arrow.setLocation(areaWidth - buttonSize.x, 0);
+ m_arrow.setSize(buttonSize);
+ m_text.setSize(areaWidth - buttonSize.x, Math.max(textSize.y, areaHeight));
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CComboBox.java b/propertysheet/src/org/eclipse/wb/core/controls/CComboBox.java
new file mode 100644
index 0000000..9f0c8f9
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/core/controls/CComboBox.java
@@ -0,0 +1,664 @@
+/*******************************************************************************
+ * 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 com.google.common.collect.Lists;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.events.TypedEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableColumn;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.TypedListener;
+import org.eclipse.wb.internal.core.model.property.editor.TextControlActionsManager;
+import org.eclipse.wb.internal.core.model.property.table.PropertyTable;
+import org.eclipse.wb.internal.core.utils.check.Assert;
+
+import java.util.ArrayList;
+
+/**
+ * Extended ComboBox control for {@link PropertyTable} and combo property editors.
+ *
+ * @author sablin_aa
+ * @coverage core.control
+ */
+public class CComboBox extends Composite {
+ private Text m_text;
+ private Button m_button;
+ private Canvas m_canvas;
+ private Shell m_popup;
+ private TableViewer m_table;
+ private boolean m_fullDropdownTableWidth = false;
+ private boolean m_wasFocused;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public CComboBox(Composite parent, int style) {
+ super(parent, style);
+ createContents(this);
+ m_wasFocused = isComboFocused();
+ // add display hook
+ final Listener displayFocusInHook = new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ boolean focused = isComboFocused();
+ if (m_wasFocused && !focused) {
+ // close DropDown on focus out ComboBox
+ comboDropDown(false);
+ }
+ if (event.widget != CComboBox.this) {
+ // forward to ComboBox listeners
+ if (!m_wasFocused && focused) {
+ event.widget = CComboBox.this;
+ notifyListeners(SWT.FocusIn, event);
+ }
+ if (m_wasFocused && !focused) {
+ event.widget = CComboBox.this;
+ notifyListeners(SWT.FocusOut, event);
+ }
+ }
+ m_wasFocused = focused;
+ }
+ };
+ final Listener displayFocusOutHook = new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ m_wasFocused = isComboFocused();
+ }
+ };
+ {
+ Display display = getDisplay();
+ display.addFilter(SWT.FocusIn, displayFocusInHook);
+ display.addFilter(SWT.FocusOut, displayFocusOutHook);
+ }
+ // combo listeners
+ addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ resizeInner();
+ }
+ });
+ addDisposeListener(new DisposeListener() {
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ {
+ // remove Display hooks
+ Display display = getDisplay();
+ display.removeFilter(SWT.FocusIn, displayFocusInHook);
+ display.removeFilter(SWT.FocusOut, displayFocusOutHook);
+ }
+ disposeInner();
+ }
+ });
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Contents
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ protected void createContents(Composite parent) {
+ createText(parent);
+ createButton(parent);
+ createImage(parent);
+ createPopup(parent);
+ }
+
+ /**
+ * Create Text widget.
+ */
+ protected void createText(Composite parent) {
+ m_text = new Text(parent, SWT.NONE);
+ new TextControlActionsManager(m_text);
+ // key press processing
+ m_text.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ switch (e.keyCode) {
+ case SWT.ESC :
+ if (isDroppedDown()) {
+ // close dropdown
+ comboDropDown(false);
+ e.doit = false;
+ } else {
+ // forward to ComboBox listeners
+ notifyListeners(SWT.KeyDown, convert2event(e));
+ }
+ break;
+ case SWT.ARROW_UP :
+ if (isDroppedDown()) {
+ // prev item in dropdown list
+ Table table = m_table.getTable();
+ int index = table.getSelectionIndex() - 1;
+ table.setSelection(index < 0 ? table.getItemCount() - 1 : index);
+ e.doit = false;
+ } else {
+ // forward to ComboBox listeners
+ notifyListeners(SWT.KeyDown, convert2event(e));
+ }
+ break;
+ case SWT.ARROW_DOWN :
+ if (isDroppedDown()) {
+ // next item in dropdown list
+ Table table = m_table.getTable();
+ int index = table.getSelectionIndex() + 1;
+ table.setSelection(index == table.getItemCount() ? 0 : index);
+ e.doit = false;
+ } else if ((e.stateMask & SWT.ALT) != 0) {
+ // force drop down combo
+ comboDropDown(true);
+ e.doit = false;
+ // return focus to text
+ setFocus2Text(false);
+ } else {
+ // forward to ComboBox listeners
+ notifyListeners(SWT.KeyDown, convert2event(e));
+ }
+ break;
+ case '\r' :
+ Table table = m_table.getTable();
+ if (isDroppedDown() && table.getSelectionIndex() != -1) {
+ // forward to Table listeners
+ table.notifyListeners(SWT.Selection, convert2event(e));
+ } else {
+ m_text.selectAll();
+ setSelectionText(getEditText());
+ // forward to ComboBox listeners
+ notifyListeners(SWT.Selection, convert2event(e));
+ }
+ break;
+ }
+ }
+ });
+ // modifications processing
+ m_text.addModifyListener(new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ if (isDroppedDown()) {
+ m_table.refresh();
+ } else {
+ // force drop down combo
+ if (m_text.isFocusControl()) {
+ comboDropDown(true);
+ // return focus to text
+ setFocus2Text(false);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Create arrow button.
+ */
+ protected void createButton(Composite parent) {
+ m_button = new Button(parent, SWT.ARROW | SWT.DOWN);
+ m_button.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ comboDropDown(!isDroppedDown());
+ // return focus to text
+ setFocus2Text(true);
+ }
+ });
+ }
+
+ /**
+ * Create image canvas.
+ */
+ protected void createImage(Composite parent) {
+ m_canvas = new Canvas(parent, SWT.BORDER);
+ m_canvas.addPaintListener(new PaintListener() {
+ @Override
+ public void paintControl(PaintEvent e) {
+ Image selectionImage = getSelectionImage();
+ if (selectionImage != null) {
+ e.gc.drawImage(selectionImage, 0, 0);
+ } else {
+ e.gc.fillRectangle(m_canvas.getClientArea());
+ }
+ }
+ });
+ }
+
+ /**
+ * Create popup shell with table.
+ */
+ protected void createPopup(Composite parent) {
+ m_popup = new Shell(getShell(), SWT.BORDER);
+ m_popup.setLayout(new FillLayout());
+ createTable(m_popup);
+ }
+
+ /**
+ * Create table.
+ */
+ protected void createTable(Composite parent) {
+ m_table = new TableViewer(parent, SWT.FULL_SELECTION);
+ new TableViewerColumn(m_table, SWT.LEFT);
+ m_table.getTable().addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ int selectionIndex = m_table.getTable().getSelectionIndex();
+ setSelectionIndex(selectionIndex);
+ comboDropDown(false);
+ // forward to ComboBox listeners
+ notifyListeners(SWT.Selection, convert2event(e));
+ }
+ });
+ m_table.setContentProvider(getContentProvider());
+ m_table.setLabelProvider(getLabelProvider());
+ m_table.addFilter(getFilterProvider());
+ }
+
+ /**
+ * Placement inner widgets.
+ */
+ protected void resizeInner() {
+ Rectangle clientArea = getClientArea();
+ int rightOccupied = 0;
+ int leftOccupied = 0;
+ {
+ // button
+ m_button.setBounds(
+ clientArea.width - clientArea.height,
+ 0,
+ clientArea.height,
+ clientArea.height);
+ rightOccupied = clientArea.height;
+ }
+ {
+ Image selectionImage = getSelectionImage();
+ if (selectionImage != null) {
+ // image
+ m_canvas.setSize(clientArea.height, clientArea.height);
+ leftOccupied = clientArea.height;
+ } else {
+ m_canvas.setSize(1, clientArea.height);
+ leftOccupied = 1;
+ }
+ }
+ {
+ // text
+ m_text.setBounds(
+ leftOccupied,
+ 0,
+ clientArea.width - rightOccupied - leftOccupied,
+ clientArea.height);
+ }
+ }
+
+ /**
+ * Dispose inner widgets.
+ */
+ protected void disposeInner() {
+ if (!m_popup.isDisposed()) {
+ m_popup.dispose();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Providers
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ protected IContentProvider getContentProvider() {
+ return new IStructuredContentProvider() {
+ @Override
+ public Object[] getElements(Object inputElement) {
+ return m_items.toArray(new ComboBoxItem[m_items.size()]);
+ }
+
+ @Override
+ public void dispose() {
+ }
+
+ @Override
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+ };
+ }
+
+ protected IBaseLabelProvider getLabelProvider() {
+ return new LabelProvider() {
+ @Override
+ public Image getImage(Object element) {
+ ComboBoxItem item = (ComboBoxItem) element;
+ return item.m_image;
+ }
+
+ @Override
+ public String getText(Object element) {
+ ComboBoxItem item = (ComboBoxItem) element;
+ return item.m_label;
+ }
+ };
+ }
+
+ protected ViewerFilter getFilterProvider() {
+ return new ViewerFilter() {
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ String lookingString = m_text.getText().toLowerCase();
+ if (isDroppedDown() && lookingString.length() > 0) {
+ ComboBoxItem item = (ComboBoxItem) element;
+ return item.m_label.toLowerCase().indexOf(lookingString) != -1;
+ }
+ return true;
+ }
+ };
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Items
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ protected static class ComboBoxItem {
+ public final String m_label;
+ public final Image m_image;
+
+ public ComboBoxItem(String label, Image image) {
+ m_label = label;
+ m_image = image;
+ }
+ }
+
+ ArrayList<ComboBoxItem> m_items = Lists.newArrayList();
+
+ /**
+ * Add new item.
+ */
+ public void addItem(String label, Image image) {
+ Assert.isTrue(!isDroppedDown());
+ m_items.add(new ComboBoxItem(label, image));
+ }
+
+ public void addItem(String label) {
+ addItem(label, null);
+ }
+
+ public void removeAll() {
+ m_items.clear();
+ }
+
+ public int getItemCount() {
+ return m_items.size();
+ }
+
+ public String getItemLabel(int index) {
+ return m_items.get(index).m_label;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Access
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public boolean isComboFocused() {
+ return isFocusControl()
+ || m_text.isFocusControl()
+ || m_button.isFocusControl()
+ || m_canvas.isFocusControl()
+ || m_popup.isFocusControl()
+ || m_table.getTable().isFocusControl();
+ }
+
+ /**
+ * Edit text.
+ */
+ public String getEditText() {
+ return m_text.getText();
+ }
+
+ public void setEditText(String text) {
+ m_text.setText(text == null ? "" : text);
+ m_text.selectAll();
+ }
+
+ public void setEditSelection(int start, int end) {
+ m_text.setSelection(start, end);
+ }
+
+ /**
+ * Read only.
+ */
+ public void setReadOnly(boolean value) {
+ m_text.setEditable(!value);
+ m_button.setEnabled(!value);
+ }
+
+ /**
+ * Drop down width.
+ */
+ public boolean isFullDropdownTableWidth() {
+ return m_fullDropdownTableWidth;
+ }
+
+ public void setFullDropdownTableWidth(boolean value) {
+ Assert.isTrue(!isDroppedDown());
+ m_fullDropdownTableWidth = value;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Selection
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private int m_selectionIndex = -1;
+
+ /**
+ * Selection index.
+ */
+ public int getSelectionIndex() {
+ return m_selectionIndex;
+ }
+
+ public void setSelectionIndex(int index) {
+ m_selectionIndex = index;
+ if (isDroppedDown()) {
+ m_table.getTable().setSelection(m_selectionIndex);
+ }
+ setEditText(getSelectionText());
+ }
+
+ /**
+ * Selection text.
+ */
+ private String getSelectionText() {
+ if (m_selectionIndex != -1 && isDroppedDown()) {
+ Object itemData = m_table.getTable().getItem(m_selectionIndex).getData();
+ return ((ComboBoxItem) itemData).m_label;
+ }
+ return null;
+ }
+
+ /**
+ * Selection image.
+ */
+ private Image getSelectionImage() {
+ return m_selectionIndex != -1 ? m_items.get(m_selectionIndex).m_image : null;
+ }
+
+ public void setSelectionText(String label) {
+ TableItem[] items = m_table.getTable().getItems();
+ for (int i = 0; i < items.length; i++) {
+ TableItem item = items[i];
+ if (item.getText().equals(label)) {
+ setSelectionIndex(i);
+ return;
+ }
+ }
+ // no such item
+ setSelectionIndex(-1);
+ setEditText(label);
+ }
+
+ /**
+ * Adds the listener to receive events.
+ */
+ public void addSelectionListener(SelectionListener listener) {
+ checkWidget();
+ if (listener == null) {
+ SWT.error(SWT.ERROR_NULL_ARGUMENT);
+ }
+ TypedListener typedListener = new TypedListener(listener);
+ addListener(SWT.Selection, typedListener);
+ addListener(SWT.DefaultSelection, typedListener);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Popup
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public boolean isDroppedDown() {
+ return m_popup.isVisible();
+ }
+
+ public void comboDropDown(boolean dropdown) {
+ // check, may be we already in this drop state
+ if (dropdown == isDroppedDown()) {
+ return;
+ }
+ // close combo
+ if (dropdown) {
+ // initialize
+ m_table.setInput(m_items);
+ Table table = m_table.getTable();
+ TableColumn column = table.getColumn(0);
+ column.pack();
+ table.pack();
+ m_popup.pack();
+ // compute table size
+ Rectangle tableBounds = table.getBounds();
+ tableBounds.height = Math.min(tableBounds.height, table.getItemHeight() * 15);// max 15 items without scrolling
+ table.setBounds(tableBounds);
+ // prepare popup point
+ Point comboLocation = toDisplay(new Point(0, 0));
+ Point comboSize = getSize();
+ // compute popup size
+ Display display = getDisplay();
+ Rectangle clientArea = display.getClientArea();
+ int remainingDisplayHeight = clientArea.height - comboLocation.y - comboSize.y - 10;
+ int preferredHeight = Math.min(tableBounds.height, remainingDisplayHeight);
+ int remainingDisplayWidth = clientArea.width - comboLocation.x - 10;
+ int preferredWidth =
+ isFullDropdownTableWidth()
+ ? Math.min(tableBounds.width, remainingDisplayWidth)
+ : comboSize.x;
+ Rectangle popupBounds =
+ new Rectangle(comboLocation.x,
+ comboLocation.y + comboSize.y,
+ preferredWidth,
+ preferredHeight);
+ Rectangle trimBounds =
+ m_popup.computeTrim(popupBounds.x, popupBounds.y, popupBounds.width, popupBounds.height);
+ m_popup.setBounds(popupBounds.x, popupBounds.y, 2 * popupBounds.width - trimBounds.width, 2
+ * popupBounds.height
+ - trimBounds.height);
+ // adjust column size
+ column.setWidth(table.getClientArea().width);
+ // show popup
+ m_popup.setVisible(true);
+ table.setSelection(getSelectionIndex());
+ } else {
+ // hide popup
+ m_popup.setVisible(false);
+ }
+ }
+
+ protected final void setFocus2Text(final boolean selectAll) {
+ getDisplay().asyncExec(new Runnable() {
+ final boolean m_selectAll = selectAll;
+
+ @Override
+ public void run() {
+ if (!m_text.isDisposed()) {
+ m_text.setFocus();
+ if (m_selectAll) {
+ m_text.selectAll();
+ }
+ }
+ }
+ });
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Utilities
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ protected static Event convert2event(TypedEvent tEvent) {
+ Event event = new Event();
+ event.widget = tEvent.widget;
+ event.display = tEvent.display;
+ event.widget = tEvent.widget;
+ event.time = tEvent.time;
+ event.data = tEvent.data;
+ if (tEvent instanceof KeyEvent) {
+ KeyEvent kEvent = (KeyEvent) tEvent;
+ event.character = kEvent.character;
+ event.keyCode = kEvent.keyCode;
+ event.stateMask = kEvent.stateMask;
+ event.doit = kEvent.doit;
+ }
+ if (tEvent instanceof SelectionEvent) {
+ SelectionEvent sEvent = (SelectionEvent) tEvent;
+ event.item = sEvent.item;
+ event.x = sEvent.x;
+ event.y = sEvent.y;
+ event.width = sEvent.width;
+ event.height = sEvent.height;
+ event.detail = sEvent.detail;
+ event.stateMask = sEvent.stateMask;
+ event.text = sEvent.text;
+ event.doit = sEvent.doit;
+ }
+ return event;
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CFlatButton.java b/propertysheet/src/org/eclipse/wb/core/controls/CFlatButton.java
new file mode 100644
index 0000000..156cf5e
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/core/controls/CFlatButton.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * 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.wb.draw2d.IColorConstants;
+import org.eclipse.wb.internal.core.utils.ui.DrawUtils;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+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.Listener;
+
+/**
+ * Class representing flat push button as it looks in Mac OSX.
+ *
+ * It doesn't draw text, not need for now. ;-)
+ *
+ * @author mitin_aa
+ */
+public final class CFlatButton extends Canvas {
+ // colors
+ private static final Color COLOR_FACE = DrawUtils.getShiftedColor(IColorConstants.button, 12);
+ private static final Color COLOR_FACE_SELECTED = IColorConstants.buttonDarker;
+ private static final Color COLOR_BORDER_GRADIENT1 = DrawUtils.getShiftedColor(
+ IColorConstants.button,
+ -12);
+ private static final Color COLOR_BORDER_GRADIENT1_SELECTED = DrawUtils.getShiftedColor(
+ IColorConstants.buttonDarker,
+ 64);
+ private static final Color COLOR_BORDER_GRADIENT2 = DrawUtils.getShiftedColor(COLOR_FACE, -8);
+ private static final Color COLOR_BORDER_GRADIENT2_SELECTED = DrawUtils.getShiftedColor(
+ COLOR_FACE_SELECTED,
+ -8);
+ // fields
+ private Image m_image;
+ private boolean m_down;
+ private boolean m_selected;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public CFlatButton(Composite parent, int style) {
+ super(parent, style);
+ addPaintListener(new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ boolean isSelected = m_down | m_selected;
+ Color faceColor = isSelected ? COLOR_FACE_SELECTED : COLOR_FACE;
+ Color borderGradientColor1 =
+ isSelected ? COLOR_BORDER_GRADIENT1_SELECTED : COLOR_BORDER_GRADIENT1;
+ Color borderGradientColor2 =
+ isSelected ? COLOR_BORDER_GRADIENT2_SELECTED : COLOR_BORDER_GRADIENT2;
+ GC gc = e.gc;
+ Rectangle ca = getClientArea();
+ // draw client area
+ // dark border
+ gc.setForeground(IColorConstants.buttonDarker);
+ gc.drawRectangle(ca.x, ca.y, ca.width - 1, ca.height - 1);
+ cropClientArea(ca);
+ // gradient border
+ gc.setForeground(borderGradientColor1);
+ gc.setBackground(borderGradientColor2);
+ gc.fillGradientRectangle(ca.x, ca.y, ca.width, ca.height, true);
+ cropClientArea(ca);
+ // fill background
+ gc.setBackground(faceColor);
+ gc.fillRectangle(ca);
+ // draw face upper-half gradient
+ Rectangle ca1 = getClientArea();
+ cropClientArea(ca1);
+ gc.setForeground(faceColor);
+ gc.setBackground(borderGradientColor1);
+ gc.fillGradientRectangle(ca1.x, ca1.y, ca1.width, ca1.height / 4, true);
+ // draw face down-half gradient
+ ca1.x += 1;
+ ca1.width -= 2;
+ gc.setForeground(borderGradientColor1);
+ gc.setBackground(faceColor);
+ gc.fillGradientRectangle(ca1.x, ca1.y + ca1.height / 4 - 1, ca1.width, ca1.height / 2, true);
+ // draw image
+ Image image = getImage();
+ if (image != null) {
+ Rectangle imageBounds = image.getBounds();
+ // center it in client area
+ int x = ca.x + (ca.width - imageBounds.width) / 2;
+ int y = ca.y + (ca.height - imageBounds.height) / 2;
+ gc.drawImage(image, x, y);
+ }
+ }
+ });
+ addListener(SWT.MouseDown, new Listener() {
+ public void handleEvent(Event e) {
+ m_down = true;
+ redraw();
+ }
+ });
+ addListener(SWT.MouseUp, new Listener() {
+ public void handleEvent(Event e) {
+ m_down = false;
+ redraw();
+ update();
+ if (getClientArea().contains(e.x, e.y)) {
+ fireSelectionEvent(e.time, e.stateMask);
+ }
+ }
+ });
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Utils
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private void fireSelectionEvent(int time, int stateMask) {
+ Event event = new Event();
+ event.time = time;
+ event.stateMask = stateMask;
+ notifyListeners(SWT.Selection, event);
+ }
+
+ private void cropClientArea(Rectangle ca) {
+ ca.x += 1;
+ ca.y += 1;
+ ca.width -= 2;
+ ca.height -= 2;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Access
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public final Image getImage() {
+ return m_image;
+ }
+
+ public void setImage(Image image) {
+ m_image = image;
+ }
+
+ public void setSelected(boolean selected) {
+ m_selected = selected;
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/core/controls/CImageLabel.java b/propertysheet/src/org/eclipse/wb/core/controls/CImageLabel.java
new file mode 100644
index 0000000..eb5bce4
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/core/controls/CImageLabel.java
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * 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.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.Listener;
+
+/**
+ * Simple control for displaying image and text.
+ *
+ * For unknown reason CLabel shows such things not very good - vertical text alignment is strange
+ * (bottom?).
+ *
+ * @author scheglov_ke
+ * @coverage core.control
+ */
+public class CImageLabel extends Canvas {
+ private static final int SPACE = 5;
+ private Image m_image;
+ private String m_text;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public CImageLabel(Composite parent, int style) {
+ super(parent, style | SWT.NO_BACKGROUND);
+ addListener(SWT.Dispose, new Listener() {
+ public void handleEvent(Event event) {
+ if (m_backImage != null) {
+ m_backImage.dispose();
+ m_backImage = null;
+ }
+ }
+ });
+ addListener(SWT.Paint, new Listener() {
+ public void handleEvent(Event event) {
+ doPaint(event.gc);
+ }
+ });
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Access
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public Image getImage() {
+ return m_image;
+ }
+
+ public void setImage(Image image) {
+ m_image = image;
+ redraw();
+ }
+
+ public String getText() {
+ return m_text;
+ }
+
+ public void setText(String text) {
+ m_text = text;
+ redraw();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Paint
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ private Image m_backImage;
+
+ private void doPaint(GC paintGC) {
+ Rectangle clientArea = getClientArea();
+ // prepare back image
+ GC gc;
+ {
+ if (m_backImage == null || !m_backImage.getBounds().equals(clientArea)) {
+ if (m_backImage != null) {
+ m_backImage.dispose();
+ }
+ m_backImage = new Image(getDisplay(), clientArea.width, clientArea.height);
+ }
+ //
+ gc = new GC(m_backImage);
+ gc.setBackground(paintGC.getBackground());
+ gc.setForeground(paintGC.getForeground());
+ gc.fillRectangle(clientArea);
+ }
+ //
+ Point textExtent = m_text == null ? new Point(0, 0) : gc.textExtent(m_text);
+ Rectangle imageBounds = m_image == null ? new Rectangle(0, 0, 0, 0) : m_image.getBounds();
+ //
+ if (m_image != null) {
+ int x = clientArea.x;
+ int y = clientArea.y + (clientArea.height - imageBounds.height) / 2;
+ gc.drawImage(m_image, x, y);
+ }
+ if (m_text != null) {
+ int x = clientArea.x + imageBounds.width + SPACE;
+ int y = clientArea.y + (clientArea.height - textExtent.y) / 2;
+ gc.drawText(m_text, x, y);
+ }
+ // flush back image
+ {
+ paintGC.drawImage(m_backImage, 0, 0);
+ gc.dispose();
+ }
+ }
+
+ @Override
+ public Point computeSize(int wHint, int hHint, boolean changed) {
+ // prepare text size
+ GC gc = new GC(this);
+ Point textExtent = m_text == null ? new Point(0, 0) : gc.textExtent(m_text);
+ gc.dispose();
+ // prepare image size
+ Rectangle imageBounds = m_image == null ? new Rectangle(0, 0, 0, 0) : m_image.getBounds();
+ // calculate control size
+ int width = imageBounds.width + SPACE + textExtent.x;
+ int height = Math.max(imageBounds.height, textExtent.y);
+ return new Point(width, height);
+ }
+}
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);
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/core/controls/Messages.java b/propertysheet/src/org/eclipse/wb/core/controls/Messages.java
new file mode 100644
index 0000000..3d83ffc
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/core/controls/Messages.java
@@ -0,0 +1,16 @@
+package org.eclipse.wb.core.controls;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.wb.core.controls.messages"; //$NON-NLS-1$
+ public static String CSpinner_canNotParse;
+ public static String CSpinner_outOfRange;
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/propertysheet/src/org/eclipse/wb/core/controls/messages.properties b/propertysheet/src/org/eclipse/wb/core/controls/messages.properties
new file mode 100644
index 0000000..75a1ca0
--- /dev/null
+++ b/propertysheet/src/org/eclipse/wb/core/controls/messages.properties
@@ -0,0 +1,2 @@
+CSpinner_canNotParse=Text "{0}"does not satisfy pattern "{1}"
+CSpinner_outOfRange=Value {0} is out of range [{1}, {2}]