blob: 5ad23152f0339ef802b3b2a37bc154bd40b6ec7b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2008 Oracle. 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: Oracle. - initial API and implementation
*******************************************************************************/
package org.eclipse.jpt.ui.internal.widgets;
import java.text.Collator;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jpt.ui.internal.JptUiMessages;
import org.eclipse.jpt.utility.internal.ClassTools;
import org.eclipse.jpt.utility.internal.model.Model;
import org.eclipse.jpt.utility.internal.model.value.PropertyValueModel;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
/**
* This pane simply shows a combo where its data is populating through
* {@link #choices()} and a default value can also be added.
* <p>
* Here the layout of this pane:
* <pre>
* -----------------------------------------------------------------------------
* | ------------------------------------------------------------------------- |
* | | I |v| |
* | ------------------------------------------------------------------------- |
* -----------------------------------------------------------------------------</pre>
*
* @version 2.0
* @since 1.0
*/
@SuppressWarnings("nls")
public abstract class EnumComboViewer<T extends Model, V> extends AbstractPane<T>
{
/**
* The main widget of this pane.
*/
private ComboViewer comboViewer;
/**
* A constant used to represent the <code>null</code> value.
*/
private static final String NULL_VALUE = "null";
/**
* Creates a new <code>EnumComboViewer</code>.
*
* @param parentPane The parent container of this one
* @param parent The parent container
* @param widgetFactory The factory used to create various widgets
*/
protected EnumComboViewer(AbstractPane<? extends T> parentPane,
Composite parent) {
super(parentPane, parent);
}
/**
* Creates a new <code>EnumComboViewer</code>.
*
* @param parentPane The parent container of this one
* @param parent The parent container
* @param widgetFactory The factory used to create various widgets
*/
protected EnumComboViewer(AbstractPane<?> parentPane,
PropertyValueModel<? extends T> subjectHolder,
Composite parent) {
super(parentPane, subjectHolder, parent);
}
/**
* Creates a new <code>EnumComboViewer</code>.
*
* @param subjectHolder The holder of this pane's subject
* @param parent The parent container
* @param widgetFactory The factory used to create various widgets
*/
protected EnumComboViewer(PropertyValueModel<? extends T> subjectHolder,
Composite parent,
IWidgetFactory widgetFactory) {
super(subjectHolder, parent, widgetFactory);
}
/*
* (non-Javadoc)
*/
@Override
protected void addPropertyNames(Collection<String> propertyNames) {
super.addPropertyNames(propertyNames);
propertyNames.add(propertyName());
}
/**
* Creates the list of choices and add an extra element that represents the
* default value.
*
* @return The combo's choices including the default value
*/
private Object[] buildChoices() {
V[] choices = choices();
Object[] extendedChoices;
if (subject() != null) {
extendedChoices = new Object[choices.length + 1];
System.arraycopy(choices, 0, extendedChoices, 1, choices.length);
extendedChoices[0] = NULL_VALUE;
}
else {
extendedChoices = choices();
}
Arrays.sort(extendedChoices, buildComparator());
return extendedChoices;
}
private Comparator<Object> buildComparator() {
return new Comparator<Object>() {
final LabelProvider labelProvider = buildLabelProvider();
public int compare(Object value1, Object value2) {
String displayString1 = labelProvider.getText(value1);
String displayString2 = labelProvider.getText(value2);
return Collator.getInstance().compare(displayString1, displayString2);
}
};
}
/**
* Retrieves the localized string from the given NLS class by creating the
* key. That key is the concatenation of the composite's short class name
* with the toString() of the given value separated by an underscore.
*
* @param nlsClass The NLS class used to retrieve the localized text
* @param compositeClass The class used for creating the key, its short class
* name is the beginning of the key
* @param value The value used to append its toString() to the generated key
* @return The localized text associated with the value
*/
protected final String buildDisplayString(Class<?> nlsClass,
Class<?> compositeClass,
Object value) {
StringBuilder sb = new StringBuilder();
sb.append(ClassTools.shortNameFor(compositeClass));
sb.append("_");
sb.append(value.toString().toLowerCase());
return (String) ClassTools.staticFieldValue(nlsClass, sb.toString());
}
/**
* Retrieves the localized string from the given NLS class by creating the
* key. That key is the concatenation of the composite's short class name
* with the toString() of the given value separated by an underscore.
*
* @param nlsClass The NLS class used to retrieve the localized text
* @param composite The object used to retrieve the short class name that is
* the beginning of the key
* @param value The value used to append its toString() to the generated key
* @return The localized text associated with the value
*/
protected final String buildDisplayString(Class<?> nlsClass,
Object composite,
Object value) {
return this.buildDisplayString(nlsClass, composite.getClass(), value);
}
/**
* Creates the display string for the given element. If the element is the
* virtual <code>null</code> value then its display string will be "Default"
* appended by the actual default value, if it exists.
*
* @param value The value to convert into a human readable string
* @return The string representation of the given element
*/
@SuppressWarnings("unchecked")
private String buildDisplayString(Object value) {
if (value == NULL_VALUE) {
V defaultValue = (subject() != null) ? defaultValue() : null;
if (defaultValue != null) {
String displayString = displayString(defaultValue);
return NLS.bind(JptUiMessages.EnumComboViewer_defaultWithDefault, displayString);
}
else {
return JptUiMessages.EnumComboViewer_default;
}
}
return displayString((V) value);
}
private LabelProvider buildLabelProvider() {
return new LabelProvider() {
@Override
public String getText(Object element) {
return buildDisplayString(element);
}
};
}
private ISelection buildSelection() {
Object value = (subject() != null) ? getValue() : null;
if (value == null) {
value = NULL_VALUE;
}
return new StructuredSelection(value);
}
@SuppressWarnings("unchecked")
private ISelectionChangedListener buildSelectionChangedListener() {
return new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent e) {
if (isPopulating()) {
return;
}
StructuredSelection selection = (StructuredSelection) e.getSelection();
Object value = selection.getFirstElement();
// Convert the default "null" value to a real null
if (value == NULL_VALUE) {
value = null;
}
EnumComboViewer.this.setValue((V) value);
}
};
}
/**
* Returns the possible choices to show in the viewer.
*
* @return The items to show in the combos
*/
protected abstract V[] choices();
/**
* Returns the default value, this method is not called if the subject is
* <code>null</code>.
*
* @return The value that is declared as being the default when it is not
* defined or <code>null</code> if there is no default value
*/
protected abstract V defaultValue();
/**
* Returns the displayable string for the given value.
*
* @param value The value to translate into a human readable string
* @return The localized text representing the given value
*/
protected abstract String displayString(V value);
/*
* (non-Javadoc)
*/
@Override
protected void doPopulate() {
super.doPopulate();
this.populateCombo();
}
/*
* (non-Javadoc)
*/
@Override
public void enableWidgets(boolean enabled) {
super.enableWidgets(enabled);
if (!this.comboViewer.getCCombo().isDisposed()) {
this.comboViewer.getCCombo().setEnabled(enabled);
}
}
protected final CCombo getCombo() {
return this.comboViewer.getCCombo();
}
/**
* Retrieves the subject's value. The subject is never <code>null</code>.
*
* @return The subject' value, which can be <code>null</code>
*/
protected abstract V getValue();
/*
* (non-Javadoc)
*/
@Override
protected void initializeLayout(Composite container) {
this.comboViewer = buildComboViewer(container, buildLabelProvider());
this.comboViewer.addSelectionChangedListener(buildSelectionChangedListener());
}
/**
* Populates
*/
private void populateCombo() {
this.getCombo().removeAll();
this.comboViewer.add(this.buildChoices());
this.updateSelection();
}
/*
* (non-Javadoc)
*/
@Override
protected void propertyChanged(String propertyName) {
super.propertyChanged(propertyName);
this.updateSelection();
}
/**
* Returns the property name used to listen for changes of the value when it
* is done outside of this viewer.
*
* @return The property name associated with the value being shown by this
* viewer
* @deprecated Use {@link #addPropertyNames(Collection)}
*/
@Deprecated
protected String propertyName() {
return "";
}
/**
* Requests the given new value be set on the subject.
*
* @param value The new value to be set
*/
protected abstract void setValue(V value);
/**
* Updates the combo's selected item.
*/
private void updateSelection() {
this.comboViewer.setSelection(this.buildSelection());
this.comboViewer.getCCombo().setSelection(new Point(0, 0));
}
}