package org.csstudio.sds.ui.internal.editor.newproperties;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.csstudio.sds.model.AbstractWidgetModel;
import org.csstudio.sds.model.DynamicsDescriptor;
import org.csstudio.sds.model.IPropertyChangeListener;
import org.csstudio.sds.model.WidgetProperty;
import org.csstudio.sds.model.commands.SetPropertyCommand;
import org.csstudio.sds.ui.SdsUiPlugin;
import org.csstudio.sds.ui.internal.commands.ChangeDynamicsCommand;
import org.csstudio.sds.ui.internal.editor.DisplayEditor;
import org.csstudio.sds.ui.internal.properties.PropertyDescriptorFactoryService;
import org.csstudio.sds.ui.internal.properties.view.DynamicAspectsWizard;
import org.csstudio.sds.ui.internal.properties.view.ModalWizardDialog;
import org.csstudio.sds.ui.properties.IPropertyDescriptor;
import org.csstudio.sds.ui.properties.IPropertyDescriptorFactory;
import org.csstudio.ui.util.CustomMediaFactory;
import org.eclipse.core.runtime.Assert;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.ImageHyperlink;
import org.eclipse.ui.views.properties.tabbed.AbstractPropertySection;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
/**
* Abstract base class for all section types. All property sections should
* inherit from here, to ensure a common look and feel when editing properties
* using the tabbed properties view.
*
* @author Sven Wende
*
* @param <E>
* the type of {@link WidgetProperty} that will be edited using this
* section
*
*
*/
@SuppressWarnings("unchecked")
public abstract class AbstractBaseSection<E extends WidgetProperty> extends AbstractPropertySection
implements IPropertyChangeListener {
protected static final Color COLOR_CONTROL_ACTIVE = CustomMediaFactory.getInstance()
.getColor(255, 255, 64);
protected static final Color COLOR_CONTROL_INACTIVE = CustomMediaFactory.getInstance()
.getColor(255, 0, 0);
protected static final Color COLOR_DIFFERENT_LABEL = CustomMediaFactory.getInstance()
.getColor(109, 34, 124);
protected static final Color COLOR_SHARED_VALUE = CustomMediaFactory.getInstance()
.getColor(CustomMediaFactory.COLOR_BLACK);
static final int STANDARD_WIDGET_WIDTH = 150;
// static final int STANDARD_CCOMBO_WIDTH = STANDARD_WIDGET_WIDTH + 10;
static final int STANDARD_CCOMBO_WIDTH = STANDARD_WIDGET_WIDTH;
static final int STANDARD_WIDGET_HEIGHT = 12;
static final int STANDARD_CCOMBO_HEIGHT = STANDARD_WIDGET_HEIGHT + 2;
static final int STANDARD_IMAGEBUTTON_WIDTH = 24;
private String label;
private String propertyId;
protected AbstractWidgetModel selectedWidget;
protected List<AbstractWidgetModel> selectedWidgets;
protected E mainWidgetProperty;
private CommandStack commandStack;
// private Button openDynamicsButton;
// private Button removeDynamicsButton;
private Composite compositeForControls;
private CLabel nameLabel;
private ImageHyperlink nameHyperlink;
private ImageHyperlink editSettingsButton;
private ImageHyperlink removeSettingsButton;
/**
* Constructs the section.
*
* @param propertyId
* the id of the property beeing edited
*/
public AbstractBaseSection(String propertyId) {
assert propertyId != null;
this.propertyId = propertyId;
}
protected Font getDefaultFont() {
return CustomMediaFactory.getInstance().getFont("Arial", 9, SWT.NONE);
}
/**
* Returns the property beeing edited. If multiple widgets are selected -
* this will be the property of the primary selected widget.
*
* @return the property beeing edited (of the primary selected widget) or
* null
*/
public E getMainWidgetProperty() {
return mainWidgetProperty;
}
/**
*{@inheritDoc}
*/
@Override
public final void createControls(final Composite parent,
TabbedPropertySheetPage aTabbedPropertySheetPage) {
super.createControls(parent, aTabbedPropertySheetPage);
// .. main composite
Composite composite = getWidgetFactory().createComposite(parent);
GridLayoutFactory.swtDefaults().numColumns(6).margins(0, 2).spacing(0, 2)
.applyTo(composite);
// .. info icon with additional help (will be invisible if no additional
// help is available)
nameHyperlink = getWidgetFactory().createImageHyperlink(composite, SWT.NONE);
GridDataFactory.fillDefaults().hint(30, SWT.DEFAULT).align(SWT.BEGINNING, SWT.BEGINNING)
.applyTo(nameHyperlink);
Image image = CustomMediaFactory.getInstance().getImageFromPlugin(SdsUiPlugin.PLUGIN_ID,
"icons/info.png");
nameHyperlink.setActiveImage(image);
nameHyperlink.setImage(image);
nameHyperlink.setVisible(false);
// .. name of the property
nameLabel = getWidgetFactory().createCLabel(composite, label);
GridDataFactory.fillDefaults().hint(STANDARD_WIDGET_WIDTH, SWT.DEFAULT)
.align(SWT.BEGINNING, SWT.BEGINNING).applyTo(nameLabel);
// .. composite for controls that are added by subclasses
compositeForControls = getWidgetFactory().createComposite(composite);
GridDataFactory.fillDefaults().hint(STANDARD_WIDGET_WIDTH * 3 + 10, getMinimumHeight())
.applyTo(compositeForControls);
editSettingsButton = getWidgetFactory().createImageHyperlink(composite, SWT.NONE);
editSettingsButton.setImage(CustomMediaFactory.getInstance()
.getImageFromPlugin(SdsUiPlugin.PLUGIN_ID, "icons/dynamics_add.gif"));
editSettingsButton.setToolTipText("Edit channel configuration");
GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING)
.applyTo(editSettingsButton);
editSettingsButton.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
WidgetProperty p = getMainWidgetProperty();
IPropertyDescriptorFactory desriptorFactory = PropertyDescriptorFactoryService
.getInstance().getPropertyDescriptorFactory(p.getPropertyType());
IPropertyDescriptor descriptor = desriptorFactory.createPropertyDescriptor(p
.getId(), p);
Map<String, String> allInheritedAliases = mainWidgetProperty.getWidgetModel()
.getAllInheritedAliases();
// .. open the dynamics wizard
DynamicAspectsWizard wizard = new DynamicAspectsWizard(mainWidgetProperty
.getDynamicsDescriptor(),
allInheritedAliases,
descriptor,
mainWidgetProperty
.getPropertyValue());
if (wizard != null) {
if (Window.OK == ModalWizardDialog.open(Display.getCurrent().getActiveShell(),
wizard)) {
DynamicsDescriptor dynamicsDescriptor = wizard.getDynamicsDescriptor();
applyDynamics(dynamicsDescriptor);
}
}
}
});
removeSettingsButton = getWidgetFactory().createImageHyperlink(composite, SWT.NONE);
removeSettingsButton.setImage(CustomMediaFactory.getInstance()
.getImageFromPlugin(SdsUiPlugin.PLUGIN_ID, "icons/dynamics_remove.gif"));
GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING)
.applyTo(removeSettingsButton);
removeSettingsButton.setToolTipText("Removes existing channel configuration.");
removeSettingsButton.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
applyDynamics(null);
}
});
// .. create a underline
Composite line = new Composite(composite, SWT.NONE);
GridDataFactory.fillDefaults().hint(SWT.DEFAULT, 1).span(5, 1).applyTo(line);
line.setBackground(CustomMediaFactory.getInstance().getColor(247, 239, 231));
// .. delegate to subclasses
doCreateControls(compositeForControls, aTabbedPropertySheetPage);
}
/**
* Template method. Subclasses need to implement the controls that should be
* used to edit the property.
*
* @param parent
* the parent composite
*
* @param aTabbedPropertySheetPage
* the current sheet page
*/
protected abstract void doCreateControls(final Composite parent,
TabbedPropertySheetPage aTabbedPropertySheetPage);
/**
* Template method. Subclasses need to update their controls according to
* the specified widget property.
*
* @param widgetProperty
* the property of the primary selected widget which can be
* changed by this section editor
*/
protected abstract void doRefreshControls(E widgetProperty);
/**
*{@inheritDoc}
*/
@Override
public void setInput(IWorkbenchPart part, ISelection selection) {
super.setInput(part, selection);
// .. get the command stack of the active editor
if (part instanceof DisplayEditor) {
commandStack = ((DisplayEditor) part).getCommandStack();
}
// .. remove property change listener from previous property
if (mainWidgetProperty != null) {
mainWidgetProperty.removePropertyChangeListener(this);
}
// .. get the selected widget (main selection)
selectedWidget = getFromSelection(selection);
// .. get all selected widgets
selectedWidgets = new ArrayList<AbstractWidgetModel>();
if (selection instanceof IStructuredSelection) {
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
Iterator<Object> it = structuredSelection.iterator();
Assert.isNotNull(it);
while (it.hasNext()) {
AbstractWidgetModel widget = getFromEditPart(it.next());
if (widget != null) {
selectedWidgets.add(widget);
}
}
}
// .. get the main widget property to be edited
mainWidgetProperty = selectedWidget != null ? (E) selectedWidget
.getPropertyInternal(propertyId) : null;
// .. update descriptions
nameHyperlink.setToolTipText("");
nameHyperlink.setVisible(false);
if (mainWidgetProperty != null) {
String description = mainWidgetProperty.getLongDescription();
if (description != null && description.length() > 0) {
nameHyperlink.setToolTipText(description);
nameHyperlink.setVisible(true);
}
}
}
/**
*{@inheritDoc}
*/
@Override
public void aboutToBeHidden() {
// .. stop listening to the property
if (mainWidgetProperty != null) {
mainWidgetProperty.removePropertyChangeListener(this);
}
}
/**
*{@inheritDoc}
*/
@Override
public void aboutToBeShown() {
// .. start listening to the property
if (mainWidgetProperty != null) {
mainWidgetProperty.addPropertyChangeListener(this);
}
}
/**
*{@inheritDoc}
*/
@Override
public final void refresh() {
if (mainWidgetProperty != null) {
// .. update label and buttons icons depending on the dynamics
// configuration
boolean dynamic = mainWidgetProperty.getDynamicsDescriptor() != null;
nameLabel.setText(mainWidgetProperty.getDescription());
int fontStyle = dynamic ? SWT.BOLD : SWT.NORMAL;
Color color = COLOR_SHARED_VALUE;
if (!haveAllSelectedWidgetsTheSameValue()) {
fontStyle = fontStyle | SWT.ITALIC;
color = COLOR_DIFFERENT_LABEL;
}
nameLabel.setFont(CustomMediaFactory.getInstance().getDefaultFont(fontStyle));
nameLabel.setForeground(color);
editSettingsButton.setImage(CustomMediaFactory.getInstance()
.getImageFromPlugin(SdsUiPlugin.PLUGIN_ID,
dynamic ? "icons/dynamics_edit.gif"
: "icons/dynamics_add.gif"));
removeSettingsButton.setVisible(dynamic);
}
// .. delegate to subclasses
doRefreshControls(mainWidgetProperty);
}
private boolean haveAllSelectedWidgetsTheSameValue() {
if (mainWidgetProperty == null || selectedWidgets.size() < 2) {
return true;
}
for (AbstractWidgetModel widget : selectedWidgets) {
if (!widget.getPropertyInternal(propertyId).getPropertyValue()
.equals(mainWidgetProperty.getPropertyValue())) {
return false;
}
}
return true;
}
/**
*{@inheritDoc}
*/
/**
*{@inheritDoc}
*/
@Override
public void dynamicsDescriptorChanged(DynamicsDescriptor dynamicsDescriptor) {
refresh();
}
/**
*{@inheritDoc}
*/
@Override
public void propertyManualValueChanged(String propertyId, Object manualValue) {
}
/**
*{@inheritDoc}
*/
@Override
public void propertyValueChanged(Object oldValue, Object newValue) {
refresh();
}
/**
* Cancels editing. May be called by subclasses to implement a cancel
* behaviour depending on their controls.
*/
protected final void cancelEditing() {
refresh();
nameLabel.setFocus();
}
/**
* Saves the specified value to the properties edited. Will only fire in
* case the new value does not not equal the old value.
*
* @param newValue
*/
protected void applyPropertyChange(Object newValue) {
if (newValue != null
&& mainWidgetProperty != null
&& !mainWidgetProperty.getPropertyValue()
.equals(mainWidgetProperty.checkValue(newValue))) {
CompoundCommand chain = new CompoundCommand();
if (selectedWidgets != null) {
for (AbstractWidgetModel widget : selectedWidgets) {
assert widget.hasProperty(propertyId);
chain.add(new SetPropertyCommand(widget, propertyId, newValue));
}
}
commandStack.execute(chain);
}
doRefreshControls(mainWidgetProperty);
}
/**
* Saves the specified dynamics descriptor to the properties being edited.
*
* @param newDynamicsDescriptor
* the new dynamics descriptor
*/
protected void applyDynamics(DynamicsDescriptor newDynamicsDescriptor) {
CompoundCommand chain = new CompoundCommand();
if (selectedWidgets != null) {
for (AbstractWidgetModel widget : selectedWidgets) {
assert widget.hasProperty(propertyId);
chain.add(new ChangeDynamicsCommand(widget.getPropertyInternal(propertyId),
newDynamicsDescriptor != null ? newDynamicsDescriptor
.clone() : null));
}
}
commandStack.execute(chain);
}
/**
* Utility method to retrieve the primary selected widget from the specified
* selection.
*
* @param selection
* the current selection
* @return the primary selected widget or null
*/
private AbstractWidgetModel getFromSelection(ISelection selection) {
AbstractWidgetModel result = null;
if (selection instanceof IStructuredSelection) {
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
return getFromEditPart(structuredSelection.getFirstElement());
}
return result;
}
/**
* Utility method to retrieve a widget model from a GEF Editpart.
*
* @param o
* the Editpart
* @return the widget or null
*/
private AbstractWidgetModel getFromEditPart(Object o) {
AbstractWidgetModel result = null;
if (o instanceof EditPart) {
EditPart ep = (EditPart) o;
if (ep.getModel() instanceof AbstractWidgetModel) {
result = (AbstractWidgetModel) ep.getModel();
}
}
return result;
}
}