/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* 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
*******************************************************************************/
/**
*
*/
package org.ebayopensource.turmeric.eclipse.ui.components;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
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.graphics.GC;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
/**
* <p>
* This cell editor combines both comboBox and Dialog. It would open a Dialog if
* user select the last item: "...".
* </p>
*
* <p>
* This class would automatically append an item "..." in the end, so no need to
* add it yourself.
* </p>
*
* @author yayu
* @see org.eclipse.jface.viewers.ComboBoxCellEditor
* @since 1.0.0
*/
public abstract class SOAComboDialogCellEditor extends CellEditor {
/** The Constant DIALOG_BUTTON_LABEL. */
public static final String DIALOG_BUTTON_LABEL = "Browse Types...";
/**
* The list of items to present in the combo box.
*/
private String[] items;
/** The value;. */
Object selectedValue;
/**
* The custom combo box control.
*/
CCombo comboBox;
/**
* Default ComboBoxCellEditor style
*/
private static final int defaultStyle = SWT.READ_ONLY;
/**
* Instantiates a new sOA combo dialog cell editor.
*/
public SOAComboDialogCellEditor() {
super();
setStyle(defaultStyle);
}
/**
* Instantiates a new sOA combo dialog cell editor.
*
* @param parent the parent
* @param items the items
*/
public SOAComboDialogCellEditor(Composite parent, String[] items) {
this(parent, items, defaultStyle);
}
/**
* Instantiates a new sOA combo dialog cell editor.
*
* @param parent the parent
* @param items the items
* @param style the style
*/
public SOAComboDialogCellEditor(Composite parent, String[] items, int style) {
super(parent, style);
setItems(items);
}
/**
* Gets the item list.
*
* @return the item list
*/
protected List<String> getItemList() {
return Arrays.asList(getItems());
}
/**
* Returns the list of choices for the combo box.
*
* @return the list of choices for the combo box
*/
public String[] getItems() {
return items;
}
/**
* Sets the list of choices for the combo box.
*
* @param items the list of choices for the combo box
*/
public void setItems(String[] items) {
Assert.isNotNull(items);
this.items = (String[]) ArrayUtils.add(items, DIALOG_BUTTON_LABEL);
fillcomboBox();
}
/**
* {@inheritDoc}
*/
@Override
protected Control createControl(Composite parent) {
comboBox = new CCombo(parent, getStyle());
comboBox.setFont(parent.getFont());
fillcomboBox();
comboBox.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
keyReleaseOccured(e);
}
});
comboBox.addSelectionListener(new SelectionAdapter() {
/**
* in order to avoid twice pop up, using "lose focus" to trigger
* "postEditing". if users selected "browse type..." item, then
* force focus on parent (must be table because this is a table cell
* editor). Then combo will lose focus and the focus lost event will
* be triggered.
*/
@Override
public void widgetDefaultSelected(SelectionEvent event) {
Object newValue = comboBox.getText();
Composite parent = comboBox.getParent();
if (DIALOG_BUTTON_LABEL.equals(newValue)
&& ((parent == null) == false)
&& (parent.isDisposed() == false)) {
parent.forceFocus();
}
}
@Override
public void widgetSelected(SelectionEvent event) {
Object newValue = comboBox.getText();
Composite parent = comboBox.getParent();
if (DIALOG_BUTTON_LABEL.equals(newValue)
&& ((parent == null) == false)
&& (parent.isDisposed() == false)) {
parent.forceFocus();
}
}
});
comboBox.addTraverseListener(new TraverseListener() {
@Override
public void keyTraversed(TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_ESCAPE
|| e.detail == SWT.TRAVERSE_RETURN) {
e.doit = false;
}
}
});
comboBox.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
SOAComboDialogCellEditor.this.focusLost();
}
});
return comboBox;
}
/**
* return selected value.
*
* @return the object
*/
@Override
protected Object doGetValue() {
return selectedValue;
}
/**
* {@inheritDoc}
*/
@Override
protected void doSetFocus() {
comboBox.setFocus();
}
/**
* if combobox contains no item, set its default width to 60, otherwise, set
* its mini width to ten characters.
*
* @return the layout data
*/
@Override
public LayoutData getLayoutData() {
LayoutData data = super.getLayoutData();
data.minimumWidth = 60;
if ((comboBox != null) && (comboBox.isDisposed() == false)) {
GC gc = new GC(comboBox);
data.minimumWidth = (gc.getFontMetrics().getAverageCharWidth() * 10) + 10;
gc.dispose();
}
return data;
}
/**
* {@inheritDoc}
*/
@Override
protected void doSetValue(Object value) {
Assert.isTrue((comboBox != null) && (value != null));
int selection = ArrayUtils.indexOf(getItems(), value);
if (selection < 0) {
// data from the dialog and not present in the combo
comboBox.setText(value.toString());
} else {
// data from drop down list
comboBox.select(selection);
}
}
/**
* Updates the list of choices for the combo box for the current control.
*/
private void fillcomboBox() {
if (comboBox != null && items != null) {
comboBox.removeAll();
for (int i = 0; i < items.length; i++) {
comboBox.add(items[i], i);
}
setValueValid(true);
if (items.length > 0) {
selectedValue = items[0];
comboBox.setVisibleItemCount(Math.min(items.length, 10));
}
}
}
/**
* Applies the currently selected value and deactivates the cell editor.
*/
protected void postEditing() {
Object newValue = comboBox.getText();
if (DIALOG_BUTTON_LABEL.equals(newValue)) {
// the last item
newValue = openDialogBox(comboBox);
}
if (newValue == null) {
comboBox.setText(getValueText(selectedValue));
return;
} else {
comboBox.setText(newValue.toString());
}
markDirty();
boolean isValid = isCorrect(newValue);
setValueValid(isValid);
if (isValid) {
selectedValue = newValue;
} else {
setErrorMessage(MessageFormat.format(getErrorMessage(),
new Object[] { selectedValue }));
}
fireApplyEditorValue();
deactivate();
}
/**
* The child class can override this method in the case that the toString()
* method of the value's class is not implemented properly.
*
* @param value the value
* @return the value text
*/
protected String getValueText(Object value) {
return String.valueOf(value);
}
/**
* {@inheritDoc}
*/
@Override
protected void focusLost() {
if (isActivated()) {
postEditing();
}
}
/**
* {@inheritDoc}
*/
@Override
protected void keyReleaseOccured(KeyEvent keyEvent) {
if (keyEvent.character == '\u001b') { // Escape character
fireCancelEditor();
} else if (keyEvent.character == '\t') { // tab key
postEditing();
}
}
/**
* pop up a dialog when users selected Browse Types...
*
* @param cellEditorWindow the cell editor window
* @return the object
*/
protected abstract Object openDialogBox(Control cellEditorWindow);
}