/** * Copyright 2014 SAP AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.spotter.eclipse.ui.editors; import java.text.MessageFormat; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; import org.eclipse.jface.viewers.ComboBoxCellEditor; 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.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; /** * A cell editor that presents it's items in a native combo box instead of the * customized <code>CCombo</code>. * <p> * Thus there is no unnecessary white space shown as in the CCombo variant. Also * clicking on an item in the list with the mouse automatically deactivates the * editor. * </p> * * @author Denis Knoepfle * */ public class CustomComboBoxCellEditor extends ComboBoxCellEditor { private String[] items; private int selection; private boolean precedingTraversalEvent = false; /** * The editor's control (using the native combo box) */ private Combo comboBox; private int activationStyle = SWT.NONE; private static final int defaultStyle = SWT.NONE; private static final int CONTROL_MINIMUM_WIDTH = 50; /** * Creates an empty combo without parent. */ public CustomComboBoxCellEditor() { setStyle(defaultStyle); } /** * Creates a new cell editor with a combo box parented under the given * control containing the given list of items. * * @param parent * the parent control * @param items * the list of strings for the combo box */ public CustomComboBoxCellEditor(Composite parent, String[] items) { super(parent, items, defaultStyle); } /** * Creates a new cell editor with a combo box parented under the given * control containing the given list of items. * * @param parent * the parent control * @param items * the strings for the combo box * @param style * the style bits */ public CustomComboBoxCellEditor(Composite parent, String[] items, int style) { setStyle(style); create(parent); setItems(items); } /** * @return the items of the combo box */ @Override public String[] getItems() { return this.items; } /** * Sets the items in the combo box. * * @param items * the items to set for the combo box */ @Override public void setItems(String[] items) { Assert.isNotNull(items); this.items = items; populateComboBox(); } @Override public void setActivationStyle(int activationStyle) { this.activationStyle = activationStyle; } @Override public void activate(ColumnViewerEditorActivationEvent activationEvent) { super.activate(activationEvent); if (activationStyle != SWT.NONE) { boolean mouseSelection = activationEvent.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION || activationEvent.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION; boolean keyboardSelection = activationEvent.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED; boolean programmatic = activationEvent.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC; boolean traversal = activationEvent.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL; if (mouseSelection && (activationStyle & DROP_DOWN_ON_MOUSE_ACTIVATION) != 0 || keyboardSelection && (activationStyle & DROP_DOWN_ON_KEY_ACTIVATION) != 0 || programmatic && (activationStyle & DROP_DOWN_ON_PROGRAMMATIC_ACTIVATION) != 0 || traversal && (activationStyle & DROP_DOWN_ON_TRAVERSE_ACTIVATION) != 0) { comboBox.getDisplay().asyncExec(new Runnable() { @Override public void run() { comboBox.setListVisible(true); } }); } } } @Override protected Control createControl(Composite parent) { comboBox = new Combo(parent, getStyle()); comboBox.setFont(parent.getFont()); populateComboBox(); comboBox.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { keyReleaseOccured(e); } }); comboBox.addTraverseListener(new TraverseListener() { public void keyTraversed(TraverseEvent e) { if (e.detail == SWT.TRAVERSE_RETURN || e.detail == SWT.TRAVERSE_ESCAPE) { e.doit = false; } else { precedingTraversalEvent = true; } } }); comboBox.addSelectionListener(new SelectionAdapter() { public void widgetDefaultSelected(SelectionEvent event) { applyAndDeactivate(); } public void widgetSelected(SelectionEvent event) { // when there was a preceding traversal event by keyboard only // save the selection, don't apply yet if (precedingTraversalEvent) { selection = comboBox.getSelectionIndex(); precedingTraversalEvent = false; } else { // assume the selection was done by mouse click applyAndDeactivate(); } } }); comboBox.addFocusListener(new FocusAdapter() { public void focusLost(FocusEvent e) { CustomComboBoxCellEditor.this.focusLost(); } }); return comboBox; } /** * Returns the zero-based index of the current selection. * * @return the zero-based index of the current selection wrapped as an * <code>Integer</code> */ protected Object doGetValue() { return new Integer(selection); } @Override protected void doSetFocus() { comboBox.setFocus(); } @Override public LayoutData getLayoutData() { LayoutData layoutData = super.getLayoutData(); if ((comboBox == null) || comboBox.isDisposed()) { layoutData.minimumWidth = CONTROL_MINIMUM_WIDTH; } return layoutData; } /** * Sets the value by accepting a zero-based index of a selection. * * @param value * the zero-based index of the selection wrapped as an * <code>Integer</code> */ @Override protected void doSetValue(Object value) { Assert.isTrue(comboBox != null && (value instanceof Integer)); selection = ((Integer) value).intValue(); comboBox.select(selection); } /** * Populates the combo box with the items. */ private void populateComboBox() { if (comboBox != null && items != null) { comboBox.removeAll(); for (int i = 0; i < items.length; i++) { comboBox.add(items[i], i); } setValueValid(true); selection = 0; } } /** * Applies the currently selected value and deactivates the cell editor. */ private void applyAndDeactivate() { selection = comboBox.getSelectionIndex(); Object newValue = doGetValue(); markDirty(); boolean isValid = isCorrect(newValue); setValueValid(isValid); if (!isValid) { // only format if the selection index is valid if (items.length > 0 && selection >= 0 && selection < items.length) { // try to insert the current value into the error message. setErrorMessage(MessageFormat.format(getErrorMessage(), new Object[] { items[selection] })); } else { // Since we don't have a valid index, assume we're using an // editable combo so format using its text value setErrorMessage(MessageFormat.format(getErrorMessage(), new Object[] { comboBox.getText() })); } } fireApplyEditorValue(); deactivate(); } @Override protected void focusLost() { if (isActivated()) { applyAndDeactivate(); } } @Override protected void keyReleaseOccured(KeyEvent keyEvent) { if (keyEvent.keyCode == SWT.ESC) { fireCancelEditor(); } else if (keyEvent.character == SWT.CR || keyEvent.keyCode == SWT.TAB) { applyAndDeactivate(); } } }