/* * 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.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.csstudio.sds.internal.model.ILayerModelListener; import org.csstudio.sds.internal.model.Layer; import org.csstudio.sds.internal.model.LayerSupport; import org.csstudio.sds.model.AbstractWidgetModel; import org.csstudio.sds.model.ContainerModel; import org.csstudio.sds.ui.CheckedUiRunnable; import org.csstudio.sds.ui.internal.commands.AddWidgetCommand; import org.csstudio.sds.ui.internal.commands.DeleteWidgetsCommand; import org.csstudio.sds.ui.internal.commands.OrphanChildCommand; import org.csstudio.sds.ui.internal.commands.SetSelectionCommand; import org.csstudio.sds.ui.internal.editor.WidgetCreationUtil; import org.csstudio.sds.ui.internal.editor.dnd.DropPvRequest; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.draw2d.IFigure; import org.eclipse.gef.CompoundSnapToHelper; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPolicy; import org.eclipse.gef.GraphicalEditPart; import org.eclipse.gef.Request; import org.eclipse.gef.SnapToGeometry; import org.eclipse.gef.SnapToGrid; import org.eclipse.gef.SnapToGuides; import org.eclipse.gef.SnapToHelper; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.CompoundCommand; import org.eclipse.gef.editparts.AbstractEditPart; import org.eclipse.gef.editparts.AbstractGraphicalEditPart; import org.eclipse.gef.editpolicies.ComponentEditPolicy; import org.eclipse.gef.editpolicies.ContainerEditPolicy; import org.eclipse.gef.editpolicies.SnapFeedbackPolicy; import org.eclipse.gef.requests.CreateRequest; import org.eclipse.gef.requests.GroupRequest; import org.eclipse.gef.rulers.RulerProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * EditPart implementation with container semantics. Can be used as controller * base class for widgets that are derived from {@link ContainerModel}. * * @author Sven Wende * */ public abstract class AbstractContainerEditPart extends AbstractBaseEditPart implements IAdaptable, ILayerModelListener { private static final Logger LOG = LoggerFactory.getLogger(AbstractContainerEditPart.class); /** * Flag which indicates that the layers are already initialized on the * content pane. */ private boolean _layersInitialized = false; public AbstractContainerEditPart() { } /** * {@inheritDoc} */ @Override public void activate() { super.activate(); // listen to the layer model getContainerModel().getLayerSupport().addLayerModelListener(this); } /** * {@inheritDoc} */ @Override public void deactivate() { // listen to the layer model getContainerModel().getLayerSupport().removeLayerModelListener(this); super.deactivate(); } /** * {@inheritDoc} */ @Override public IFigure getContentPane() { IFigure f = super.getContentPane(); // initialize the layers if ((f instanceof LayeredWidgetPane) && !_layersInitialized) { LayeredWidgetPane layeredWidgetPane = (LayeredWidgetPane) f; int i = 0; for (Layer layer : getContainerModel().getLayerSupport().getLayers()) { layeredWidgetPane.addLayer(layer.getId(), i); i++; } _layersInitialized = true; } return f; } /** * Returns the container model, which is managed by this controller. This is * for convinience only. The method returns the same object as * {@link #getModel()} or {@link #getWidgetModel()}. * * @return the container model */ public final ContainerModel getContainerModel() { return (ContainerModel) getModel(); } /** * Overidden to suppport layers. Layers are only supported if the figure for * this editpart used a {@link LayeredWidgetPane} as its content pane. * * {@inheritDoc} */ @Override protected final void addChildVisual(final EditPart childEditPart, final int index) { IFigure child = ((GraphicalEditPart) childEditPart).getFigure(); IFigure contentPane = getContentPane(); AbstractWidgetModel widgetModel = (AbstractWidgetModel) childEditPart.getModel(); if (contentPane instanceof LayeredWidgetPane) { LayerSupport layerSupport = getContainerModel().getLayerSupport(); Layer layer = layerSupport.findLayer(widgetModel.getLayer()); if (!layerSupport.isLayerId(widgetModel.getLayer()) && layerSupport.isLayerName(widgetModel.getLayer())) { widgetModel.setPropertyValue(AbstractWidgetModel.PROP_LAYER, layer.getId()); } ((LayeredWidgetPane) contentPane).addWidget(layer.getId(), child, index); } else { super.addChildVisual(childEditPart, index); } } /** * Overidden to suppport layers. Layers are only supported if the figure for * this editpart used a {@link LayeredWidgetPane} as its content pane. * * {@inheritDoc} */ @Override protected final void removeChildVisual(final EditPart childEditPart) { IFigure child = ((GraphicalEditPart) childEditPart).getFigure(); IFigure contentPane = getContentPane(); if (contentPane instanceof LayeredWidgetPane) { ((LayeredWidgetPane) contentPane).removeWidget(child); } else { super.removeChildVisual(childEditPart); } } /** * {@inheritDoc} */ @Override protected void createEditPolicies() { installEditPolicy(EditPolicy.COMPONENT_ROLE, new ComponentEditPolicy() { @Override protected Command createDeleteCommand(final GroupRequest deleteRequest) { ContainerModel model = (ContainerModel) getHost().getParent().getModel(); AbstractWidgetModel widgetModel = (AbstractWidgetModel) getHost().getModel(); return new DeleteWidgetsCommand(getViewer(), model, Arrays.asList(widgetModel)); } @Override public Command getCommand(final Request request) { return super.getCommand(request); } @Override public EditPart getTargetEditPart(final Request request) { return super.getTargetEditPart(request); } }); installEditPolicy(EditPolicy.NODE_ROLE, null); installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, null); installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, null); // to prevent selection and manipulation of widgets in the the Run Mode, // the layout role is only installed for the Edit Mode installEditPolicy(EditPolicy.LAYOUT_ROLE, getExecutionMode() == ExecutionMode.EDIT_MODE ? new ModelXYLayoutEditPolicy() : null); installEditPolicy(EditPolicy.CONTAINER_ROLE, new ContainerEditPolicy() { @Override public Command getCommand(final Request request) { if (DropPvRequest.REQ_DROP_PV.equals(request.getType())) { DropPvRequest r = (DropPvRequest) request; ContainerModel container = (ContainerModel) getHost().getModel(); CompoundCommand cmd = new CompoundCommand(); AbstractWidgetModel widgetModel = WidgetCreationUtil.createAndPreconfigureWidget(null, r.getDroppedProcessVariables()); if (widgetModel != null) { //TODO (jhatje): is this the right way to get the relative position? ModelXYLayoutEditPolicy editPolicy = (ModelXYLayoutEditPolicy) getEditPolicy(EditPolicy.LAYOUT_ROLE); editPolicy.getRelativePosition(r.getLocation()); widgetModel.setLocation(r.getLocation().x, r.getLocation().y); widgetModel.setLayer(container.getLayerSupport().getActiveLayer().getId()); cmd.add(new AddWidgetCommand(container, widgetModel)); cmd.add(new SetSelectionCommand(getViewer(), widgetModel)); return cmd; } } return super.getCommand(request); } @Override protected Command getCreateCommand(final CreateRequest request) { return null; } @Override public Command getOrphanChildrenCommand(final GroupRequest request) { List parts = request.getEditParts(); CompoundCommand result = new CompoundCommand(""); //$NON-NLS-1$ for (int i = 0; i < parts.size(); i++) { ContainerModel container = (ContainerModel) getHost().getModel(); AbstractWidgetModel widget = (AbstractWidgetModel) ((EditPart) parts.get(i)).getModel(); OrphanChildCommand orphan = new OrphanChildCommand(container, widget); result.add(orphan); } return result.unwrap(); } @Override public EditPart getTargetEditPart(final Request request) { if (DropPvRequest.REQ_DROP_PV.equals(request.getType())) { return getHost(); } return super.getTargetEditPart(request); } }); installEditPolicy("Snap Feedback", new SnapFeedbackPolicy()); //$NON-NLS-1$ } /** * {@inheritDoc} */ @Override protected final synchronized List getModelChildren() { List<AbstractWidgetModel> modelChildren = getContainerModel().getWidgets(); return modelChildren; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public final Object getAdapter(final Class adapter) { if (adapter == SnapToHelper.class) { List snapStrategies = new ArrayList(); Boolean val = (Boolean) getViewer().getProperty(RulerProvider.PROPERTY_RULER_VISIBILITY); if ((val != null) && val.booleanValue()) { snapStrategies.add(new SnapToGuides(this)); } val = (Boolean) getViewer().getProperty(SnapToGeometry.PROPERTY_SNAP_ENABLED); if ((val != null) && val.booleanValue()) { snapStrategies.add(new SnapToGeometry(this)); } val = (Boolean) getViewer().getProperty(SnapToGrid.PROPERTY_GRID_ENABLED); if ((val != null) && val.booleanValue()) { snapStrategies.add(new SnapToGrid(this)); } if (snapStrategies.size() == 0) { return null; } if (snapStrategies.size() == 1) { return snapStrategies.get(0); } SnapToHelper[] ss = new SnapToHelper[snapStrategies.size()]; for (int i = 0; i < snapStrategies.size(); i++) { ss[i] = (SnapToHelper) snapStrategies.get(i); } return new CompoundSnapToHelper(ss); } // else if (adapter == AutoexposeHelper.class) { // return new ViewportAutoexposeHelper(this); // } return super.getAdapter(adapter); } /** * {@inheritDoc} */ @Override public final synchronized void propertyChange(final PropertyChangeEvent evt) { super.propertyChange(evt); new CheckedUiRunnable() { @Override protected void doRunInUi() { String prop = evt.getPropertyName(); if (prop.equals(ContainerModel.PROP_CHILD_ADDED)) { refreshChildren(); } else if (prop.equals(ContainerModel.PROP_CHILDREN_ADDED)) { refreshChildren(); } else if (prop.equals(ContainerModel.PROP_CHILD_REMOVED)) { refreshChildren(); } else if (prop.equals(ContainerModel.PROP_CHILDREN_REMOVED)) { refreshChildren(); } else if (prop.equals(ContainerModel.PROP_ORDER_CHANGED)) { refreshChildren(); } else if (prop.equals(ContainerModel.PROP_CHILDREN_SELECTED)) { List<AbstractWidgetModel> widgets = (List<AbstractWidgetModel>) evt.getNewValue(); List<EditPart> eps = new ArrayList<EditPart>(); for (AbstractWidgetModel w : widgets) { EditPart ep = findEditPart(w); if (ep != null) { eps.add(ep); } } getRoot().getViewer().setSelection(new StructuredSelection(eps)); } } }; } @Override protected void refreshChildren() { super.refreshChildren(); } private EditPart findEditPart(final AbstractWidgetModel model) { EditPart result = null; Iterator<EditPart> it = getChildren().iterator(); while (it.hasNext() && (result == null)) { EditPart p = it.next(); if (p.getModel().equals(model)) { result = p; } } return result; } /** * {@inheritDoc} */ @Override public final void layerChanged(final org.csstudio.sds.internal.model.Layer layer, final String property) { new CheckedUiRunnable() { @Override protected void doRunInUi() { if (getContentPane() instanceof LayeredWidgetPane) { final LayeredWidgetPane layeredWidgetPane = (LayeredWidgetPane) getContentPane(); if (property.equals(LayerSupport.PROP_LAYER_MOVED)) { layeredWidgetPane.moveLayer(layer.getId(), getContainerModel().getLayerSupport().getLayerIndex(layer)); } else if (property.equals(LayerSupport.PROP_LAYER_ADDED)) { layeredWidgetPane.addLayer(layer.getId(), getContainerModel().getLayerSupport().getLayerIndex(layer)); layeredWidgetPane.setVisibility(layer.getId(), layer.isVisible()); } else if (property.equals(LayerSupport.PROP_LAYER_REMOVED)) { layeredWidgetPane.removeLayer(layer.getId(), getContainerModel().getLayerSupport().getDefaultLayer().getId()); } else if (property.equals(org.csstudio.sds.internal.model.Layer.PROP_VISIBLE)) { layeredWidgetPane.setVisibility(layer.getId(), layer.isVisible()); } } } }; } /** * Called by child editparts when their current layer has changed. * * Note: Layers are only supported if the figure for this editpart used a * {@link LayeredWidgetPane} as its content pane. Otherwise this call won�t * have any effect. * * @param childEditPart * the child editpart * @param oldLayerName * the old name of the layer * @param newLayerName * the new name of the layer */ protected final void handleLayerChanged(final AbstractGraphicalEditPart childEditPart, final String oldLayerName, final String newLayerName) { if (getContentPane() instanceof LayeredWidgetPane) { LayeredWidgetPane contentPane = (LayeredWidgetPane) getContentPane(); if (!contentPane.hasLayer(newLayerName)) { contentPane.moveWidget(childEditPart.getFigure(), oldLayerName, getContainerModel().getLayerSupport().getDefaultLayer().getId()); LOG.warn("Tried to move widget into not existing layer '" + newLayerName + "'"); } else { contentPane.moveWidget(childEditPart.getFigure(), oldLayerName, newLayerName); } } } /** * Returns true, if children of this container may be selected. * * @return true, if children of this container may be selected */ public final boolean allowsChildSelection() { return isSelectable() && determineChildrenSelectability(); } // FIXME:SW:Comment protected abstract boolean determineChildrenSelectability(); /** * Helper method that checks if any direct or indirect child (e.g. a widget * in a contained container) is selected. * * @return true, if any direct or indirect child is selected */ protected final boolean isAnyChildSelected() { for (Object child : getChildren()) { if (child instanceof AbstractContainerEditPart) { AbstractContainerEditPart cep = (AbstractContainerEditPart) child; if (cep.isSelected() || cep.isAnyChildSelected()) { return true; } } else if (child instanceof AbstractEditPart) { AbstractBaseEditPart ep = (AbstractBaseEditPart) child; if (ep.isSelected()) { return true; } } } return false; } }