/*
* Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
*
* THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
* WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
* IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
* CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
* NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
* DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
* THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
* USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
* PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
* AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
*/
package org.csstudio.sds.ui.internal.properties.view;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.csstudio.dal.DynamicValueState;
import org.csstudio.domain.common.LayoutUtil;
import org.csstudio.platform.simpledal.ConnectionState;
import org.csstudio.sds.model.DynamicsDescriptor;
import org.csstudio.sds.model.initializers.WidgetInitializationService;
import org.csstudio.sds.ui.internal.dynamicswizard.SimpleChannelPage;
import org.csstudio.sds.ui.properties.IPropertyDescriptor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ICellEditorListener;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
/**
* A wizard, which enables users to configure dynamic settings for a property.
* This includes the binding of channels and/or macro scripts (e.g. color rules)
* to properties.
*
* @author Sven Wende, Stefan Hofer, Kai Meyer
* @version $Revision: 1.55 $
*
*/
public final class DynamicAspectsWizard extends Wizard {
/**
* The dynamics descriptor that is edited.
*/
private DynamicsDescriptor _dynamicsDescriptor;
/**
* The property descriptor.
*/
private IPropertyDescriptor _propertyDescriptor;
/**
* Names of existing aliases that can be used in channel names.
*/
private Map<String, String> _aliases;
/**
* The SimpleChannelPage.
*/
private SimpleChannelPage _simpleChannelPage;
/**
* The StatePage.
*/
private StatePage _statePage;
private Object _initValue;
/**
* Constructs a dynamic aspects wizard for the specified property sheet
* entry.
*
* @param entry
* the property sheet entry
* @param dynamicsDescriptor
* the dynamic descriptor or null, if none exists
* @param aliasNames
* names of existing aliases that can be used in channel names.
* @param propertyDescriptor
* The Descriptor for the property
*/
public DynamicAspectsWizard(final DynamicsDescriptor dynamicsDescriptor, final Map<String, String> aliasNames,
final IPropertyDescriptor propertyDescriptor, Object currentValue) {
assert aliasNames != null;
_aliases = aliasNames;
_initValue = currentValue;
_propertyDescriptor = propertyDescriptor;
_dynamicsDescriptor = dynamicsDescriptor != null ? dynamicsDescriptor.clone() : new DynamicsDescriptor();
}
/**
* {@inheritDoc}
*/
@Override
public boolean performFinish() {
_dynamicsDescriptor = new DynamicsDescriptor();
_simpleChannelPage.performFinish(_dynamicsDescriptor);
_statePage.performFinish(_dynamicsDescriptor);
return true;
}
/**
* Returns the prepared {@link DynamicsDescriptor}.
*
* @return the {@link DynamicsDescriptor}
*/
public DynamicsDescriptor getDynamicsDescriptor() {
return _dynamicsDescriptor;
}
/**
* {@inheritDoc}
*/
@Override
public void addPages() {
_simpleChannelPage = new SimpleChannelPage("Simple Channel", _dynamicsDescriptor, _propertyDescriptor.getPropertyType(), _aliases);
addPage(_simpleChannelPage);
_statePage = new StatePage("States", _dynamicsDescriptor.getConnectionStateDependentPropertyValues(), _dynamicsDescriptor
.getConditionStateDependentPropertyValues());
addPage(_statePage);
}
/**
* The page for configuring the states.
*
* @author Kai Meyer
*/
private final class StatePage extends WizardPage {
private Map<ConnectionState, Object> _connectionStateValues = new HashMap<ConnectionState, Object>();
private Map<DynamicValueState, Object> _conditionStateValues = new HashMap<DynamicValueState, Object>();
/**
* A CellEditorListener for the CellEditor.
*/
private ICellEditorListener _connectionEditorListener;
/**
* A CellEditorListener for the CellEditor.
*/
private ICellEditorListener _conditionEditorListener;
/**
* The composite for the ConnectionStates.
*/
private StateComposite<ConnectionState> _connectionStateComposite;
/**
* The composite for the ConditionStates.
*/
private StateComposite<DynamicValueState> _conditionStateComposite;
/**
* Constructor.
*
* @param pageName
* the name of the page
*/
protected StatePage(final String pageName, Map<ConnectionState, Object> connectionStateValues,
Map<DynamicValueState, Object> conditionStateValues) {
super(pageName);
assert pageName != null;
setTitle("State Configurater");
setDescription("Use this page to configure the states");
_connectionStateValues = connectionStateValues != null ? connectionStateValues : new HashMap<ConnectionState, Object>();
_conditionStateValues = conditionStateValues != null ? conditionStateValues : new HashMap<DynamicValueState, Object>();
}
/**
* Creates a new cell editor listener.
*
* @param composite
* The StateComposite where the CellEditorListener is
* registered
* @return ICellEditorListener A new ICellEditorListener, which sets the
* error message on the page
*/
private ICellEditorListener createEditorListener(final StateComposite<?> composite) {
ICellEditorListener editorListener = new ICellEditorListener() {
@Override
public void cancelEditor() {
setErrorMessage(null);
}
@Override
public void editorValueChanged(final boolean oldValidState, final boolean newValidState) {
setErrorMessage(composite.getErrorMessage());
}
@Override
public void applyEditorValue() {
setErrorMessage(null);
}
};
return editorListener;
}
/**
* {@inheritDoc}
*/
@Override
public void createControl(final Composite parent) {
Composite c = new Composite(parent, SWT.None);
c.setLayout(new GridLayout(1, false));
// we display only the supported connection states
Set<ConnectionState> supportedConnectionStates = WidgetInitializationService.getInstance().getSupportedConnectionStates();
_connectionStateComposite = new StateComposite<ConnectionState>(c, SWT.NONE, "Connection", supportedConnectionStates
.toArray(new Enum[supportedConnectionStates.size()])) {
@Override
protected Map<ConnectionState, Object> getStateMap() {
return _connectionStateValues;
}
};
_connectionEditorListener = this.createEditorListener(_connectionStateComposite);
_connectionStateComposite.addEditorListener(_connectionEditorListener);
_conditionStateComposite = new StateComposite<DynamicValueState>(c, SWT.NONE, "Condition", DynamicValueState.values()) {
@Override
protected Map<DynamicValueState, Object> getStateMap() {
return _conditionStateValues;
}
};
_conditionEditorListener = this.createEditorListener(_conditionStateComposite);
_conditionStateComposite.addEditorListener(_conditionEditorListener);
// important for wizards -> set the control
setControl(c);
}
/**
* Prepare this page for closing.
*/
public void performFinish(DynamicsDescriptor dynamicsDescriptor) {
dynamicsDescriptor.setConnectionStateDependentPropertyValues(_connectionStateValues);
dynamicsDescriptor.setConditionStateDependentPropertyValues(_conditionStateValues);
_connectionStateComposite.finished();
_conditionStateComposite.finished();
}
/**
* A Composite, which contains a Table to configure states.
*
* @author Kai Meyer
*/
@SuppressWarnings("unchecked")
private abstract class StateComposite<ETYPE extends Enum> extends Composite {
/**
* The Action to add a state.
*/
private Action _addStateAction;
/**
* The Action to remove a state.
*/
private Action _removeStateAction;
/**
* A table viewer, which is used to configure states.
*/
private TableViewer _stateTableViewer;
/**
* The CellEditor from the PropertyDescriptor.
*/
private CellEditor _editor;
/**
* The title of the States.
*/
private final String _stateName;
/**
* All possible states.
*/
private final Enum[] _allStates;
/**
* Constructor.
*
* @param parent
* The parent of this Composite
* @param style
* The style for this Composite
* @param stateName
* The title for the States
* @param allStates
* All possible states
*/
public StateComposite(final Composite parent, final int style, final String stateName, final Enum[] allStates) {
super(parent, style);
this.setLayoutData(LayoutUtil.createGridDataForHorizontalFillingCell());
this.setLayout(new GridLayout(1, false));
_stateName = stateName;
_allStates = allStates;
this.makeActions();
_stateTableViewer = this.createConnectionStateTable(this);
this.refresh();
}
/**
* Makes the Actions for the Table.
*/
private void makeActions() {
_addStateAction = new AddStateAction(_stateName);
_removeStateAction = new RemoveStateAction(_stateName);
}
/**
* Creates the TableViewer for connection states.
*
* @param parent
* The parent Composite for the table
* @return The TableViewer for the connection states
*/
private TableViewer createConnectionStateTable(final Composite parent) {
Group group = new Group(parent, SWT.NONE);
group.setLayout(new GridLayout(1, false));
group.setText(_stateName + " States");
group.setLayoutData(LayoutUtil.createGridDataForFillingCell());
// define column names
String[] columnNames = new String[] { "FIRST", "PROP_NAME", "PROP_TYPE" }; //$NON-NLS-1$ //$NON-NLS-2$
// create table
final Table table = new Table(group, SWT.FULL_SELECTION | SWT.SCROLL_PAGE);
table.setLinesVisible(true);
table.setLayoutData(LayoutUtil.createGridDataForHorizontalFillingCell(150));
table.setHeaderVisible(true);
TableColumn column = new TableColumn(table, SWT.CENTER, 0);
column.setText("First");
column.setWidth(0);
column = new TableColumn(table, SWT.LEFT, 1);
column.setText(_stateName + " State");
column.setWidth(300);
column = new TableColumn(table, SWT.LEFT, 2);
column.setText("Value");
column.setWidth(140);
// create viewer
TableViewer viewer = new TableViewer(table);
viewer.setUseHashlookup(true);
// define column properties
viewer.setColumnProperties(columnNames);
// configure cell editors
_editor = _propertyDescriptor.createPropertyEditor(table);
CellEditor[] editors = new CellEditor[columnNames.length];
editors[0] = null;
editors[1] = null;
editors[2] = _editor;
viewer.setCellEditors(editors);
viewer.setCellModifier(new StateCellModifier());
viewer.setContentProvider(new ArrayContentProvider());
viewer.setLabelProvider(new StateTableLabelProvider());
viewer.setInput(this.getStateMap().keySet());
// create popup menu
MenuManager popupMenu = new MenuManager();
popupMenu.add(_addStateAction);
popupMenu.add(_removeStateAction);
Menu menu = popupMenu.createContextMenu(viewer.getTable());
viewer.getTable().setMenu(menu);
// double click listener
viewer.getControl().addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick(final MouseEvent e) {
addState();
}
});
return viewer;
}
/**
* Removes the current selected State.
*/
private void removeState() {
TableItem[] selection = _stateTableViewer.getTable().getSelection();
for (TableItem item : selection) {
this.getStateMap().remove(item.getData());
}
this.refresh();
}
/**
* Opens a dialog and adds the selected State to the Table.
*/
private void addState() {
StateDialog dialog = new StateDialog(this.getShell(), "Choose the '" + _stateName + " State' you want to add", this
.makeStateArray(this.getAllStates()));
if (dialog.open() == Window.OK) {
for (Enum state : dialog.getSelectedStates()) {
this.getStateMap().put((ETYPE) state, _initValue);
}
this.refresh();
}
}
/**
* Refreshes the Table and the Actions.
*/
private void refresh() {
_stateTableViewer.refresh();
_removeStateAction.setEnabled(_stateTableViewer.getTable().getItemCount() > 0);
_addStateAction.setEnabled(_stateTableViewer.getTable().getItemCount() < _allStates.length);
}
/**
* returns the Map od states.
*
* @return HashMap The Map od states
*/
protected abstract Map<ETYPE, Object> getStateMap();
/**
* Returns all possible states.
*
* @return All possible States
*/
public Enum[] getAllStates() {
return _allStates;
}
/**
* Generates a Enum[], which entries are not in the Table yet.
*
* @param states
* Possible Enums
* @return A Enum[], which entries are not in the Table yet
*/
private Enum[] makeStateArray(final Enum[] states) {
List<Enum> stateList = new LinkedList<Enum>();
Set<ETYPE> keys = this.getStateMap().keySet();
for (Enum state : states) {
if (!keys.contains(state)) {
stateList.add(state);
}
}
return stateList.toArray(new Enum[stateList.size()]);
}
/**
* Returns an error message or null.
*
* @return The error Message or null
*/
public String getErrorMessage() {
return _editor.getErrorMessage();
}
/**
* Adds the given ICellEditorListener to the CellEditor of the
* table.
*
* @param listener
* The ICellEditorListener for the table
*/
public void addEditorListener(final ICellEditorListener listener) {
_editor.addListener(listener);
}
/**
* Deactivates all CellEditors to perform last changes.
*/
public void finished() {
for (CellEditor editor : _stateTableViewer.getCellEditors()) {
if (editor != null) {
editor.deactivate();
}
}
}
/**
* An action, which adds a state to the configuration.
*
* @author Kai Meyer
*/
protected final class AddStateAction extends Action {
/**
* Constructor.
*
* @param stateName
* The Name of the states
*/
protected AddStateAction(final String stateName) {
super("Add " + stateName + " State");
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
addState();
}
}
/**
* An action, which removes a state to the configuration.
*
* @author Kai Meyer
*/
protected final class RemoveStateAction extends Action {
/**
* Constructor.
*
* @param stateName
* The Name of the states
*/
protected RemoveStateAction(final String stateName) {
super("Remove " + stateName + " State");
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
removeState();
}
}
/**
* A Dialog, which allows to choose the state, which should be
* created.
*
* @author Kai Meyer
*/
private final class StateDialog extends TitleAreaDialog {
/**
* A List of the selected states.
*/
private List<Enum> _selectedStates = new LinkedList<Enum>();
/**
* The Enums for this dialog.
*/
private Enum[] _states;
/**
* The message for this dialog.
*/
private String _message;
/**
* The List of Buttons.
*/
private final List<Button> _buttonList = new LinkedList<Button>();
/**
* Constructor.
*
* @param parentShell
* The parent Shell for this Dialog
* @param message
* The message for this dialog
* @param states
* The available States to display
*/
public StateDialog(final Shell parentShell, final String message, final Enum[] states) {
super(parentShell);
_states = states;
_message = message;
}
/**
* {@inheritDoc}
*/
@Override
protected void configureShell(final Shell shell) {
super.configureShell(shell);
shell.setText("Selected State");
}
/**
* {@inheritDoc}
*/
@Override
protected Control createDialogArea(final Composite parent) {
final Composite composite = (Composite) super.createDialogArea(parent);
this.setTitle("Select the State");
this.setMessage(_message);
Composite comp = new Composite(composite, SWT.NONE);
comp.setLayout(new GridLayout(1, false));
Label label = new Label(comp, SWT.NONE);
label.setText("Available States:");
if (_states.length > 0) {
this.createRadioButtons(comp);
} else {
label = new Label(comp, SWT.WRAP);
label.setText("No States available");
}
return composite;
}
/**
* Creates a Radiobutton for every State.
*
* @param parent
* The parent composite for the Buttons
*/
private void createRadioButtons(final Composite parent) {
for (Enum state : _states) {
Button button = new Button(parent, SWT.CHECK);
button.setText(state.name());
button.setData(state);
_buttonList.add(button);
}
}
/**
* Gets the selected state.
*
* @return String The selected state
*/
public Enum[] getSelectedStates() {
return _selectedStates.toArray(new Enum[_selectedStates.size()]);
}
/**
* {@inheritDoc}
*/
@Override
protected void okPressed() {
for (Button button : _buttonList) {
if (button.getSelection()) {
_selectedStates.add((Enum) button.getData());
}
}
super.okPressed();
}
}
/**
* The CellModifier for States.
*
* @author Kai Meyer
*/
private final class StateCellModifier implements ICellModifier {
/**
* {@inheritDoc}
*/
@Override
public boolean canModify(final Object element, final String property) {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public Object getValue(final Object element, final String property) {
if (element instanceof ConnectionState || element instanceof DynamicValueState) {
return getStateMap().get(element);
}
return "";
}
/**
* {@inheritDoc}
*/
@Override
public void modify(final Object element, final String property, final Object value) {
if (element instanceof TableItem) {
ETYPE state = (ETYPE) ((TableItem) element).getData();
if (value != null) {
getStateMap().put((ETYPE) state, value);
}
_stateTableViewer.refresh();
}
}
}
/**
* A TableLabelProvider for states.
*
* @author Kai Meyer
*
*/
private final class StateTableLabelProvider extends LabelProvider implements ITableLabelProvider {
/**
* {@inheritDoc}
*/
@Override
public Image getColumnImage(final Object element, final int columnIndex) {
if (columnIndex == 2) {
Object value = getStateMap().get(element);
if (value != null) {
if (_propertyDescriptor.getLabelProvider() != null) {
try {
return _propertyDescriptor.getLabelProvider().getImage(value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public String getColumnText(final Object element, final int columnIndex) {
if (element == null) {
return "Fehler";
} else {
if (columnIndex == 2) {
Object value = getStateMap().get(element);
if (value != null) {
if (_propertyDescriptor.getLabelProvider() != null) {
try {
return _propertyDescriptor.getLabelProvider().getText(value);
} catch (Exception e) {
e.printStackTrace();
}
}
return value.toString();
}
}
return element.toString();
}
}
}
}
}
}