/*
* 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.editparts;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import org.csstudio.dal.simple.ChannelListener;
import org.csstudio.dal.simple.ConnectionParameters;
import org.csstudio.dal.simple.SimpleDALBroker;
import org.csstudio.platform.model.pvs.IProcessVariableAddress;
import org.csstudio.platform.model.pvs.IProcessVariableAdressProvider;
import org.csstudio.sds.cursorservice.CursorService;
import org.csstudio.sds.internal.connection.ConnectionUtilNew;
import org.csstudio.sds.internal.connection.IListenerRegistry;
import org.csstudio.sds.internal.preferences.PreferenceConstants;
import org.csstudio.sds.model.AbstractWidgetModel;
import org.csstudio.sds.model.DisplayModel;
import org.csstudio.sds.model.RuntimeContext;
import org.csstudio.sds.model.WidgetProperty;
import org.csstudio.sds.ui.SdsUiPlugin;
import org.csstudio.sds.ui.cursors.internal.CursorHelper;
import org.csstudio.sds.ui.figures.IBorderEquippedWidget;
import org.csstudio.sds.ui.figures.ICrossedFigure;
import org.csstudio.sds.ui.figures.IRhombusEquippedWidget;
import org.csstudio.sds.ui.figures.ToolTipFigure;
import org.csstudio.sds.ui.internal.editparts.WidgetPropertyChangeListener;
import org.csstudio.sds.ui.internal.properties.view.IPropertySource;
import org.csstudio.sds.util.ChannelReferenceValidationException;
import org.csstudio.sds.util.ChannelReferenceValidationUtil;
import org.csstudio.sds.util.ExecutionService;
import org.csstudio.sds.util.TooltipResolver;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.draw2d.ChopboxAnchor;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseMotionListener;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.NodeEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.ui.progress.UIJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cosylab.util.CommonException;
/**
* This is the base class for all controllers of SDS widgets. In the GEF
* model-view-controller architecture, subclasses of this class are the
* controllers.
*
* @author Sven Wende
* @version $Revision: 1.98 $
*
*/
public abstract class AbstractBaseEditPart extends AbstractGraphicalEditPart
implements NodeEditPart, PropertyChangeListener,
IProcessVariableAdressProvider, IListenerRegistry {
enum ConnectionStatus {
DISCONNECTED, CONNECTED, CONNECTING, DISCONNECTING
}
private static final Logger LOG = LoggerFactory.getLogger(AbstractBaseEditPart.class);
private ConnectionStatus _connectionStatus = ConnectionStatus.DISCONNECTED;
private final Semaphore _semaphore;
private final Map<WidgetProperty, org.csstudio.sds.model.IPropertyChangeListener> _outChannelListeners;
/**
* A map, which takes property ids as key and property change listeners as
* values.
*/
public HashMap<String, WidgetPropertyChangeListener> _propertyChangeListenersById;
/**
* A map, which takes properties as key and property change listeners as
* values.
*/
private final HashMap<WidgetProperty, WidgetPropertyChangeListener> _propertyChangeListenersByProperty;
/**
* The execution mode (Run vs. Edit mode).
*/
private ExecutionMode _executionMode;
private MouseMotionListener _motionListener;
private final IPropertyChangeListener _preferencesListener;
private int selected;
private List<SimpleDalListenerInfo> registeredSimpleDalListeners = new ArrayList<SimpleDalListenerInfo>();
/**
* Standard constructor.
*/
public AbstractBaseEditPart() {
_semaphore = new Semaphore(1);
_executionMode = ExecutionMode.EDIT_MODE;
_propertyChangeListenersById = new HashMap<String, WidgetPropertyChangeListener>();
_propertyChangeListenersByProperty = new HashMap<WidgetProperty, WidgetPropertyChangeListener>();
_preferencesListener = new PreferencesListener();
_outChannelListeners = new HashMap<WidgetProperty, org.csstudio.sds.model.IPropertyChangeListener>();
}
/**
* Sets the execution mode. The execution mode is one of
* {@link ExecutionMode#EDIT_MODE} or {@link ExecutionMode#RUN_MODE}.
*
* @param executionMode
* The new execution mode
*/
public final void setExecutionMode(final ExecutionMode executionMode) {
_executionMode = executionMode;
}
/**
* Returns the current execution mode. The execution mode is one of
* {@link ExecutionMode#EDIT_MODE} or {@link ExecutionMode#RUN_MODE}.
*
* @return The current execution mode
*/
public final ExecutionMode getExecutionMode() {
return _executionMode;
}
/**
* Returns the model as {@link AbstractWidgetModel}.
*
* @return the model of this {@link AbstractBaseEditPart}
*/
protected AbstractWidgetModel getCastedModel() {
return (AbstractWidgetModel) getModel();
}
/**
* {@inheritDoc}
*/
@Override
protected final IFigure createFigure() {
AbstractWidgetModel model = getWidgetModel();
// FIXME: Sven Wende: Das Setzen dieser Property und alles, was im
// Weiteren damit zusammenh�ngt sollte dringend! einem Refactoring
// unterworfen werden. Die Property und das Handling in den konkreten
// Widgets ist nicht abstrakt und k�nnte viel weiter oben in der
// Widget-Hierarchie und der Editpart-Hierarchie erledigt werden. Diesen
// Aufruf hier zu machen, ist ein wilder Hack!!
getCastedModel().setPropertyValue(
AbstractWidgetModel.PROP_WRITE_ACCESS_GRANTED,
!SdsUiPlugin.getCorePreferenceStore().getBoolean(
PreferenceConstants.PROP_WRITE_ACCESS_DENIED));
// create figure
IFigure f = doCreateFigure();
if (f == null) {
throw new IllegalArgumentException(
"Editpart does not provide a figure!"); //$NON-NLS-1$
}
// initialize figure
// ... enabled
updateFigureEnablementState(f);
// ... colors
f.setBackgroundColor(getModelColor(AbstractWidgetModel.PROP_COLOR_BACKGROUND));
f.setForegroundColor(getModelColor(AbstractWidgetModel.PROP_COLOR_FOREGROUND));
// ... size
f.setSize(model.getWidth(), model.getHeight());
if (f instanceof IAdaptable) {
ICrossedFigure crossedFigure = (ICrossedFigure) ((IAdaptable) f)
.getAdapter(ICrossedFigure.class);
crossedFigure.setCrossedOut(model.isCrossedOut());
}
if (f instanceof IAdaptable) {
// ... borders
IBorderEquippedWidget borderEquippedWidgetAdapter = (IBorderEquippedWidget) ((IAdaptable) f)
.getAdapter(IBorderEquippedWidget.class);
if (borderEquippedWidgetAdapter != null) {
borderEquippedWidgetAdapter.setBorderWidth(model
.getBorderWidth());
borderEquippedWidgetAdapter
.setBorderColor(getModelColor(AbstractWidgetModel.PROP_BORDER_COLOR));
borderEquippedWidgetAdapter.setBorderStyle(model
.getBorderStyle());
if (model.getPrimaryPV() == null) {
borderEquippedWidgetAdapter.setBorderText("");
} else {
try {
borderEquippedWidgetAdapter
.setBorderText(ChannelReferenceValidationUtil
.createCanonicalName(
model.getPrimaryPV(),
model.getAliases()));
} catch (ChannelReferenceValidationException e) {
borderEquippedWidgetAdapter.setBorderText(model
.getPrimaryPV());
}
}
}
// ... cross out
ICrossedFigure crossedEquippedWidgetAdapter = (ICrossedFigure) ((IAdaptable) f)
.getAdapter(ICrossedFigure.class);
if (crossedEquippedWidgetAdapter != null) {
crossedEquippedWidgetAdapter
.setCrossedOut(model.isCrossedOut());
}
// ... rhombus
IRhombusEquippedWidget rhombusEquippedWidgetAdapter = (IRhombusEquippedWidget) ((IAdaptable) f)
.getAdapter(IRhombusEquippedWidget.class);
if (rhombusEquippedWidgetAdapter != null) {
rhombusEquippedWidgetAdapter.setVisible(model.isRhombus());
}
}
// ... cursor
CursorHelper.applyCursor(f, model.getCursorId());
// ... visibility
f.setVisible(model.isLive() ? model.isVisible() : true);
// f.repaint();
return f;
}
/**
* Implementors should create and return the figure here.
*
* @return the figure
*/
protected abstract IFigure doCreateFigure();
/**
* {@inheritDoc}
*/
@Override
protected final void refreshVisuals() {
doRefreshVisuals(getFigure());
}
/**
* Resizes the figure. Use {@link AbstractBaseEditPart} to implement more
* complex refreshing behavior.
*
* @param refreshableFigure
* the figure
*/
protected synchronized void doRefreshVisuals(final IFigure refreshableFigure) {
super.refreshVisuals();
AbstractWidgetModel model = getWidgetModel();
Dimension size = new Dimension(model.getWidth(), model.getHeight());
Rectangle bounds = new Rectangle(new Point(model.getX(), model.getY()),
size);
GraphicalEditPart parent = (GraphicalEditPart) getParent();
if (parent != null) {
parent.setLayoutConstraint(this, refreshableFigure, bounds);
}
}
/**
* Returns the widget model, which is managed by this controller. This is
* for convinience only. The method returns the same object as
* {@link #getModel()}.
*
* @return the casted model
*/
public final AbstractWidgetModel getWidgetModel() {
return (AbstractWidgetModel) getModel();
}
/**
* Returns runtime context information.
*
* @return runtime context information
*/
protected RuntimeContext getRuntimeContext() {
return getCastedModel().getRoot().getRuntimeContext();
}
/**
* {@inheritDoc}
*/
@Override
public void activate() {
super.activate();
final AbstractWidgetModel model = getWidgetModel();
// add as property change listener to the widget
model.addPropertyChangeListener(this);
// add one property change listener to each property of the widget
for (WidgetProperty property : model.getProperties()) {
WidgetPropertyChangeListener listener = new WidgetPropertyChangeListener(
this);
property.addPropertyChangeListener(listener);
_propertyChangeListenersById.put(property.getId(), listener);
_propertyChangeListenersByProperty.put(property, listener);
}
// register handlers for standard properties
registerStandardPropertyChangeHandlers();
// let subclasses register their property change handlers
registerPropertyChangeHandlers();
// connect
handleLiveState(model.isLive());
// register a mouse motion listener that updates the tooltip and cursor
// of the figure lazily (this saves runtime resources and improves
// performance)
if (!(model instanceof DisplayModel)) {
_motionListener = new MouseMotionListener.Stub() {
@Override
public void mouseEntered(final MouseEvent me) {
// initialize cursor states
CursorService.getInstance().applyCursor(getCastedModel());
// update the tooltip
String resolvedTooltipText = TooltipResolver
.resolveToValue(getWidgetModel().getToolTipText(),
getWidgetModel());
getFigure().setToolTip(
new ToolTipFigure(resolvedTooltipText));
}
};
getFigure().addMouseMotionListener(_motionListener);
}
// listen to preference changes
SdsUiPlugin.getCorePreferenceStore().addPropertyChangeListener(
_preferencesListener);
}
/**
* Register the standard property change handlers that are in common for all
* element types. These are:
* <ul>
* <li>PROP_VISIBILITY</li>
* <li>PROP_POS_X</li>
* <li>PROP_POS_Y</li>
* <li>PROP_WIDTH</li>
* <li>PROP_HEIGHT</li>
* <li>PROP_COLOR_BACKGROUND</li>
* <li>PROP_COLOR_FOREGROUND</li>
* <li>PROP_BORDER_WIDTH</li>
* <li>PROP_BORDER_COLOR</li>
* <li>PROP_BORDER_STYLE</li>
* <li>PROP_LAYER</li>
* <li>PROP_ENABLED</li>
* </ul>
*
*/
private void registerStandardPropertyChangeHandlers() {
IWidgetPropertyChangeHandler visibilityHandler = new IWidgetPropertyChangeHandler() {
@Override
public boolean handleChange(final Object oldValue,
final Object newValue, final IFigure refreshableFigure) {
boolean visible = (Boolean) newValue;
final IFigure figure = getFigure();
if (getWidgetModel().isLive()) {
figure.setVisible(visible);
} else {
if (!visible) {
figure.setVisible(false);
UIJob job = new UIJob("reset") {
@Override
public IStatus runInUIThread(
final IProgressMonitor monitor) {
figure.setVisible(true);
return Status.OK_STATUS;
}
};
job.schedule(2000);
}
}
return true;
}
};
setPropertyChangeHandler(AbstractWidgetModel.PROP_VISIBILITY,
visibilityHandler);
IWidgetPropertyChangeHandler fullRefreshHandler = new IWidgetPropertyChangeHandler() {
@Override
public boolean handleChange(final Object oldValue,
final Object newValue, final IFigure refreshableFigure) {
AbstractBaseEditPart.this.doRefreshVisuals(refreshableFigure);
return true;
}
};
setPropertyChangeHandler(AbstractWidgetModel.PROP_POS_X,
fullRefreshHandler);
setPropertyChangeHandler(AbstractWidgetModel.PROP_POS_Y,
fullRefreshHandler);
setPropertyChangeHandler(AbstractWidgetModel.PROP_WIDTH,
fullRefreshHandler);
setPropertyChangeHandler(AbstractWidgetModel.PROP_HEIGHT,
fullRefreshHandler);
setPropertyChangeHandler(AbstractWidgetModel.PROP_COLOR_BACKGROUND,
new ColorChangeHandler<IFigure>() {
@Override
protected void doHandle(final IFigure figure,
final Color color) {
figure.setBackgroundColor(color);
}
});
setPropertyChangeHandler(AbstractWidgetModel.PROP_COLOR_FOREGROUND,
new ColorChangeHandler<IFigure>() {
@Override
protected void doHandle(final IFigure figure,
final Color color) {
figure.setForegroundColor(color);
}
});
IWidgetPropertyChangeHandler borderWidthHandler = new IWidgetPropertyChangeHandler() {
@Override
public boolean handleChange(final Object oldValue,
final Object newValue, final IFigure figure) {
if (figure instanceof IAdaptable) {
IBorderEquippedWidget borderEquippedWidgetAdapter = (IBorderEquippedWidget) ((IAdaptable) figure)
.getAdapter(IBorderEquippedWidget.class);
if (borderEquippedWidgetAdapter != null) {
borderEquippedWidgetAdapter
.setBorderWidth((Integer) newValue);
}
return true;
}
return false;
}
};
setPropertyChangeHandler(AbstractWidgetModel.PROP_BORDER_WIDTH,
borderWidthHandler);
setPropertyChangeHandler(AbstractWidgetModel.PROP_BORDER_COLOR,
new ColorChangeHandler<IFigure>() {
@Override
protected void doHandle(final IFigure figure,
final Color color) {
if (figure instanceof IAdaptable) {
IBorderEquippedWidget borderEquippedWidgetAdapter = (IBorderEquippedWidget) ((IAdaptable) figure)
.getAdapter(IBorderEquippedWidget.class);
if (borderEquippedWidgetAdapter != null) {
borderEquippedWidgetAdapter
.setBorderColor(color);
}
}
}
});
IWidgetPropertyChangeHandler borderStyleHandler = new IWidgetPropertyChangeHandler() {
@Override
public boolean handleChange(final Object oldValue,
final Object newValue, final IFigure figure) {
if (figure instanceof IAdaptable) {
IBorderEquippedWidget borderEquippedWidgetAdapter = (IBorderEquippedWidget) ((IAdaptable) figure)
.getAdapter(IBorderEquippedWidget.class);
if (borderEquippedWidgetAdapter != null) {
borderEquippedWidgetAdapter
.setBorderStyle((Integer) newValue);
}
return true;
}
return false;
}
};
setPropertyChangeHandler(AbstractWidgetModel.PROP_BORDER_STYLE,
borderStyleHandler);
// enabled
IWidgetPropertyChangeHandler enableHandler = new IWidgetPropertyChangeHandler() {
@Override
public boolean handleChange(final Object oldValue,
final Object newValue, final IFigure figure) {
if (getExecutionMode().equals(ExecutionMode.RUN_MODE)) {
updateFigureEnablementState(figure);
} else {
figure.setEnabled(false);
}
return true;
}
};
setPropertyChangeHandler(AbstractWidgetModel.PROP_ENABLED,
enableHandler);
setPropertyChangeHandler(AbstractWidgetModel.PROP_ACCESS_GRANTED,
enableHandler);
// layer
IWidgetPropertyChangeHandler layerHandler = new IWidgetPropertyChangeHandler() {
@Override
public boolean handleChange(final Object oldValue,
final Object newValue, final IFigure refreshableFigure) {
if (getParent() instanceof AbstractContainerEditPart) {
String oldLayerName = getLayerName(oldValue);
String newLayerName = getLayerName(newValue);
((AbstractContainerEditPart) getParent())
.handleLayerChanged(AbstractBaseEditPart.this,
oldLayerName, newLayerName);
}
return true;
}
};
setPropertyChangeHandler(AbstractWidgetModel.PROP_LAYER, layerHandler);
IWidgetPropertyChangeHandler primaryPVHandler = new IWidgetPropertyChangeHandler() {
@Override
public boolean handleChange(final Object oldValue,
final Object newValue, final IFigure refreshableFigure) {
String pv = getCastedModel().getPrimaryPV();
Map<String, String> aliases = getCastedModel().getAliases();
if (figure instanceof IAdaptable) {
IBorderEquippedWidget borderEquippedWidgetAdapter = (IBorderEquippedWidget) ((IAdaptable) refreshableFigure)
.getAdapter(IBorderEquippedWidget.class);
if (borderEquippedWidgetAdapter != null) {
if (pv == null) {
borderEquippedWidgetAdapter.setBorderText("");
} else {
try {
borderEquippedWidgetAdapter
.setBorderText(ChannelReferenceValidationUtil
.createCanonicalName(pv,
aliases));
} catch (ChannelReferenceValidationException e) {
borderEquippedWidgetAdapter.setBorderText(pv);
}
}
}
}
return true;
}
};
setPropertyChangeHandler(AbstractWidgetModel.PROP_PRIMARY_PV,
primaryPVHandler);
setPropertyChangeHandler(AbstractWidgetModel.PROP_ALIASES,
primaryPVHandler);
// cursor
IWidgetPropertyChangeHandler actionDataHandler = new IWidgetPropertyChangeHandler() {
@Override
public boolean handleChange(final Object oldValue,
final Object newValue, final IFigure refreshableFigure) {
if (newValue != null) {
CursorHelper.applyCursor(figure, newValue.toString());
}
return true;
}
};
setPropertyChangeHandler(AbstractWidgetModel.PROP_CURSOR,
actionDataHandler);
// crossed
IWidgetPropertyChangeHandler crossedHandler = new IWidgetPropertyChangeHandler() {
@Override
public boolean handleChange(final Object oldValue,
final Object newValue, final IFigure refreshableFigure) {
if (refreshableFigure instanceof IAdaptable) {
ICrossedFigure crossedFigure = (ICrossedFigure) ((IAdaptable) refreshableFigure)
.getAdapter(ICrossedFigure.class);
crossedFigure.setCrossedOut((Boolean) newValue);
return true;
}
return false;
}
};
setPropertyChangeHandler(AbstractWidgetModel.PROP_CROSSED_OUT,
crossedHandler);
// rhombus
IWidgetPropertyChangeHandler rhombusHandler = new IWidgetPropertyChangeHandler() {
@Override
public boolean handleChange(final Object oldValue,
final Object newValue, final IFigure refreshableFigure) {
if (refreshableFigure instanceof IAdaptable) {
IRhombusEquippedWidget rhombusEquippedWidgetAdapter = (IRhombusEquippedWidget) ((IAdaptable) refreshableFigure)
.getAdapter(IRhombusEquippedWidget.class);
if (rhombusEquippedWidgetAdapter != null) {
rhombusEquippedWidgetAdapter
.setVisible((Boolean) newValue);
}
return true;
}
return false;
}
};
setPropertyChangeHandler(AbstractWidgetModel.PROP_RHOMBUS,
rhombusHandler);
}
/**
* TODO: nur tempor�r verwenden! Returns the layer name
*
* @param s
* the layerName (may be null or "")
* @return The layerName (is not null or "")
*/
private String getLayerName(final Object s) {
if (s == null) {
return "DEFAULT";
} else {
if (s.toString().equals("")) {
return "DEFAULT";
} else {
return s.toString();
}
}
}
/**
* Subclasses should register handlers for property changes here.
*
* Each handler can be registered by calling
* {@link #setPropertyChangeHandler(String, IWidgetPropertyChangeHandler)}
*/
protected abstract void registerPropertyChangeHandlers();
/**
* Registers a property change handler for the specified property id.
*
* @param propertyId
* the property id
* @param handler
* the property change handler
*/
protected final void setPropertyChangeHandler(final String propertyId,
final IWidgetPropertyChangeHandler handler) {
WidgetPropertyChangeListener listener = _propertyChangeListenersById
.get(propertyId);
if (listener != null) {
listener.addHandler(handler);
}
}
/**
* {@inheritDoc}
*/
@Override
public void deactivate() {
// stop listening to the preferences
SdsUiPlugin.getCorePreferenceStore().removePropertyChangeListener(
_preferencesListener);
// remove
getWidgetModel().removePropertyChangeListener(this);
// remove the property change listeners
for (WidgetProperty property : _propertyChangeListenersByProperty
.keySet()) {
property.removePropertyChangeListener(_propertyChangeListenersByProperty
.get(property));
}
if (this._executionMode.equals(ExecutionMode.RUN_MODE)
&& !(this.getModel() instanceof DisplayModel)) {
this.getFigure().removeMouseMotionListener(_motionListener);
}
// disconnect from control system
if ((_connectionStatus == ConnectionStatus.CONNECTED)
|| (_connectionStatus == ConnectionStatus.CONNECTING)) {
disconnectFromControlSystem();
}
// de-register the mouse motion listener that was registered for
// tooltips and cursor refreshs
if (_motionListener != null) {
getFigure().removeMouseMotionListener(_motionListener);
}
super.deactivate();
}
/**
* {@inheritDoc}
*/
@Override
public final ConnectionAnchor getSourceConnectionAnchor(final Request arg0) {
return createConnectionAnchor();
}
/**
* {@inheritDoc}
*/
@Override
public final ConnectionAnchor getTargetConnectionAnchor(final Request arg0) {
return createConnectionAnchor();
}
/**
* {@inheritDoc}
*/
@Override
public final ConnectionAnchor getSourceConnectionAnchor(
final ConnectionEditPart arg0) {
return new ChopboxAnchor(getFigure());
}
/**
* {@inheritDoc}
*/
@Override
public final ConnectionAnchor getTargetConnectionAnchor(
final ConnectionEditPart arg0) {
return new ChopboxAnchor(getFigure());
}
/**
* Creates a connection anchor.
*
* @return a connection anchor
*/
private ConnectionAnchor createConnectionAnchor() {
return new ChopboxAnchor(getFigure());
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("rawtypes")
@Override
public Object getAdapter(final Class adapter) {
if (adapter == IPropertySource.class) {
return getWidgetModel().getAdapter(adapter);
}
return super.getAdapter(adapter);
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void propertyChange(final PropertyChangeEvent evt) {
String prop = evt.getPropertyName();
if (prop.equals(AbstractWidgetModel.PROP_LIVE)) {
Boolean live = (Boolean) evt.getNewValue();
handleLiveState(live);
}
}
/**
* Configures this EditPart with the given live state.
*
* @param live
* The new live state
*/
private void handleLiveState(final boolean live) {
if (live) {
if ((_connectionStatus == ConnectionStatus.DISCONNECTED)
|| (_connectionStatus == ConnectionStatus.DISCONNECTING)) {
connectToControlSystem();
}
} else {
if ((_connectionStatus == ConnectionStatus.CONNECTED)
|| (_connectionStatus == ConnectionStatus.CONNECTING)) {
disconnectFromControlSystem();
}
}
}
private void disconnectFromControlSystem() {
Runnable r = new Runnable() {
@Override
public void run() {
try {
_semaphore.acquire();
// unregister listeners explicitly from SimpleDAL (Sven
// Wende: Note: usually this should not be necessary but due
// to bugs in
// SimpleDAL we have to take care of it ourselves)
for (SimpleDalListenerInfo info : registeredSimpleDalListeners) {
try {
SimpleDALBroker broker = getBroker();
if (broker != null) {
broker.deregisterListener(info.getParameters(),
info.getListener());
}
} catch (Exception e) {
// die silently
}
}
registeredSimpleDalListeners.clear();
_connectionStatus = ConnectionStatus.DISCONNECTING;
// .. remove all (outgoing) channel listeners
for (WidgetProperty p : _outChannelListeners.keySet()) {
p.removePropertyChangeListener(_outChannelListeners
.get(p));
}
_connectionStatus = ConnectionStatus.DISCONNECTED;
} catch (Exception e) {
LOG.error(e.toString());
} finally {
_semaphore.release();
}
}
};
ExecutionService.getInstance().executeWithNormalPriority(r);
}
private void connectToControlSystem() {
_connectionStatus = ConnectionStatus.CONNECTING;
Runnable r = new Runnable() {
@Override
public void run() {
try {
_semaphore.acquire();
// we only need to connect to the control system if this
// edit part is still active (maybe the display has already
// been closed)
if (AbstractBaseEditPart.this.isActive()) {
_connectionStatus = ConnectionStatus.CONNECTING;
AbstractWidgetModel widget = getCastedModel();
// .. connect to security API
ConnectionUtilNew.connectToWidgetManagementApi(widget);
// .. connect single dynamized properties
List<WidgetProperty> properties = widget
.getProperties();
for (WidgetProperty property : properties) {
if (property.isVisible()) {
ConnectionUtilNew.connectDynamizedProperties(
property,
widget.getAllInheritedAliases(),
widget.isWriteAccessAllowed(),
AbstractBaseEditPart.this, getBroker());
}
}
// .. connect behavior
ConnectionUtilNew.connectToBehavior(widget,
AbstractBaseEditPart.this);
// .. initialize cursor states
CursorService.getInstance().applyCursor(widget);
_connectionStatus = ConnectionStatus.CONNECTED;
} else {
_connectionStatus = ConnectionStatus.DISCONNECTED;
}
} catch (InterruptedException e) {
LOG.error(e.toString());
} finally {
_semaphore.release();
}
}
};
ExecutionService.getInstance().executeWithNormalPriority(r);
}
public boolean isSelected() {
return getSelected() == SELECTED_PRIMARY || getSelected() == SELECTED;
}
/**
* Overrides default GEF implementation to circumvent an invariant check
* (value==0 || isSelectable()) that tries to ensure that only 'selectable'
* editparts can be 'selected'. SDS supports a special behavior for
* containers (GroupingContainer, LinkingContainer) for which this invariant
* may be temporarily hurt.
*/
@Override
public void setSelected(int value) {
if (getSelected() == value)
return;
selected = value;
fireSelectionChanged();
}
/**
* Overides default GEF implementation. See comment for
* {@link #setSelected(int)}.
*/
@Override
public final int getSelected() {
return selected;
}
/**
* Overides default GEF implementation. See comment for
* {@link #setSelected(int)}.
*/
@Override
public boolean isSelectable() {
boolean result = false;
if (getFigure() != null && getFigure().isShowing()) {
if (getCastedModel().isLive()) {
// in run mode, widgets are always selectable
result = true;
} else {
if (getParent() instanceof AbstractContainerEditPart) {
// check that the parent container allows for child
// selections
AbstractContainerEditPart parent = (AbstractContainerEditPart) getParent();
result = parent.allowsChildSelection();
} else {
// fallback which should usually not apply, as all SDS
// widgets have a container parent
result = true;
}
}
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public List<IProcessVariableAddress> getProcessVariableAdresses() {
return getCastedModel().getAllPvAdresses();
}
/**
* {@inheritDoc}
*/
public String getName() {
try {
AbstractWidgetModel castedModel = getCastedModel();
String property = castedModel.getMainPvAdress().getProperty();
return property;
} catch (Exception e) {
return "";
}
}
// /**
// * {@inheritDoc}
// */
// public String getTypeId() {
// return IProcessVariable.TYPE_ID;
// }
//
/**
* {@inheritDoc}
*/
@Override
public IProcessVariableAddress getPVAdress() {
return getCastedModel().getMainPvAdress();
}
/**
* {@inheritDoc}
*/
@Override
public void register(final ConnectionParameters parameters, final ChannelListener listener) {
try {
SimpleDALBroker broker = getBroker();
if (broker != null) {
// remember listeners
registeredSimpleDalListeners.add(new SimpleDalListenerInfo(parameters, listener));
// register listeners
broker.registerListener(parameters, listener);
}
} catch (InstantiationException e) {
LOG.error("Registering simple DAL listeners failed", e.toString());
} catch (CommonException e) {
LOG.error("Registering simple DAL listeners failed", e.toString());
}
}
/**
* {@inheritDoc}
*/
@Override
public void register(final WidgetProperty property,
final org.csstudio.sds.model.IPropertyChangeListener listener) {
property.addPropertyChangeListener(listener);
_outChannelListeners.put(property, listener);
}
/**
* Returns a {@link SimpleDALBroker} instance. Those instance will be
* display dependent. There will be 1 broker per display.
*
* @return the {@link SimpleDALBroker}
*/
protected SimpleDALBroker getBroker() {
SimpleDALBroker broker = getRuntimeContext().getBroker();
return broker;
}
/**
* Updates the figures enabled state. The figure is only enabled when the
* current widget and all of its parents are enabled.
*
* Additionally we disable all input widgets like sliders and buttons etc.
* in edit mode.
*
* @param f
* the figure that should be enabled or disabled
*/
protected void updateFigureEnablementState(final IFigure f) {
// when no figure is provided via parameter we query it
IFigure figure = f != null ? f : getFigure();
assert figure != null;
// in run mode the enable state depends on the widget and its parent
// widgets
boolean enabled = getWidgetModel().isEnabledRecursive();
// in edit mode some widgets (e.g. sliders and buttons) are generally
// disabled to prevent
// accidental execution of actions and to be able to drag their
// graphical representation
if (getExecutionMode() == ExecutionMode.EDIT_MODE) {
if (forceDisabledInEditMode()) {
enabled = false;
}
}
// update the figures state
figure.setEnabled(enabled);
// force all children to refresh their enable state, too
for (Object child : getChildren()) {
if (child instanceof AbstractBaseEditPart) {
((AbstractBaseEditPart) child)
.updateFigureEnablementState(null);
}
}
}
/**
* Subclasses should override this method to specify if the figure for this
* controller should be disabled in edit mode in general. Default return
* value is false.
*
* @return true if the figure for this controller should be disabled in edit
* mode in general, false otherwise
*/
protected boolean forceDisabledInEditMode() {
return false;
}
/**
* Convenience method that returns the real color for the specified model
* property (which has to be a color property!). This call does also resolve
* color variables. Callers do not need to care about Color.dispose().
*
* @param property
* the id of a color property
*
* @return the color
*/
protected Color getModelColor(final String propertyId) {
String hexOrVariable = getCastedModel().getColor(propertyId);
return SdsUiPlugin.getDefault().getColorAndFontService()
.getColor(hexOrVariable);
}
/**
* Convenience method that returns the real font for the specified model
* property (which has to be a font property!). This call does also resolve
* font variables (see {@link IFontService}. Callers do not need to care
* about Font.dispose().
*
* @param property
* the id of a font property
*
* @return the font
*/
protected Font getModelFont(final String propertyId) {
String font = getCastedModel().getFont(propertyId);
return SdsUiPlugin.getDefault().getColorAndFontService().getFont(font);
}
class PreferencesListener implements IPropertyChangeListener {
@Override
public void propertyChange(
final org.eclipse.jface.util.PropertyChangeEvent event) {
// .. handle preference that switches write access permissions for
// all open displays
if (PreferenceConstants.PROP_WRITE_ACCESS_DENIED.equals(event
.getProperty())) {
getCastedModel().setPropertyValue(
AbstractWidgetModel.PROP_WRITE_ACCESS_GRANTED,
!(Boolean) event.getNewValue());
}
}
}
/**
* Convenience handler class for color properties.
*
* @author Sven Wende
*
* @param <F>
* the figure type
*/
public abstract class ColorChangeHandler<F extends IFigure> implements IWidgetPropertyChangeHandler {
@Override
@SuppressWarnings("unchecked")
public boolean handleChange(final Object oldValue, final Object newValue, final IFigure refreshableFigure) {
assert newValue != null;
assert newValue instanceof String;
Color color = SdsUiPlugin.getDefault().getColorAndFontService().getColor((String) newValue);
if (color != null)
doHandle((F) figure, color);
return true;
}
protected abstract void doHandle(F figure, Color color);
}
/**
* Convenience handler class for font properties.
*
* @author Sven Wende
*
* @param <F>
* the figure type
*/
public abstract class FontChangeHandler<F extends IFigure> implements
IWidgetPropertyChangeHandler {
@Override
@SuppressWarnings("unchecked")
public boolean handleChange(final Object oldValue, final Object newValue, final IFigure refreshableFigure) {
assert newValue instanceof String;
Font font = SdsUiPlugin.getDefault().getColorAndFontService().getFont((String) newValue);
if (font != null)
doHandle((F) figure, font);
return true;
}
protected abstract void doHandle(F figure, Font font);
}
/**
* Keeps information for a single SimpleDAL listener that are needed to be
* able to unregister that listener from SimpleDAL.
*
* @author swende
*
*/
private static class SimpleDalListenerInfo {
private ConnectionParameters parameters;
private ChannelListener listener;
public SimpleDalListenerInfo(ConnectionParameters parameters, ChannelListener listener) {
super();
this.parameters = parameters;
this.listener = listener;
}
public ConnectionParameters getParameters() {
return parameters;
}
public ChannelListener getListener() {
return listener;
}
}
}