diff options
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/core/controls')
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}] |