blob: 4308ad085de3c26b0df29cc45fc3a83ba3175af9 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2002, 2007 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.diagram.ui.editparts;
import java.util.HashMap;
import java.util.Iterator;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.ConnectionLocator;
import org.eclipse.draw2d.Cursors;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.transaction.RunnableWithResult;
import org.eclipse.gef.AccessibleEditPart;
import org.eclipse.gef.DragTracker;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.editparts.AbstractConnectionEditPart;
import org.eclipse.gef.editparts.AbstractEditPart;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.editpolicies.ResizableEditPolicy;
import org.eclipse.gmf.runtime.common.core.util.Log;
import org.eclipse.gmf.runtime.common.core.util.Trace;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.EditPolicyRoles;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.NonResizableLabelEditPolicy;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.VisibilityComponentEditPolicy;
import org.eclipse.gmf.runtime.diagram.ui.figures.LabelLocator;
import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIDebugOptions;
import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIPlugin;
import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIStatusCodes;
import org.eclipse.gmf.runtime.diagram.ui.internal.editpolicies.LabelSnapBackEditPolicy;
import org.eclipse.gmf.runtime.diagram.ui.internal.figures.ResizableLabelLocator;
import org.eclipse.gmf.runtime.diagram.ui.internal.util.LabelViewConstants;
import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramColorRegistry;
import org.eclipse.gmf.runtime.diagram.ui.label.ILabelDelegate;
import org.eclipse.gmf.runtime.diagram.ui.tools.DragEditPartsTrackerEx;
import org.eclipse.gmf.runtime.draw2d.ui.figures.ConstrainedToolbarLayout;
import org.eclipse.gmf.runtime.draw2d.ui.geometry.PointListUtilities;
import org.eclipse.gmf.runtime.notation.NotationPackage;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.ui.views.properties.IPropertySource;
/**
* Controller for all label that deals with interactions bewteen
* the label figure the underline editpart
*
* @author jcorchis
*/
public class LabelEditPart extends TopGraphicEditPart {
private String semanticHint = null;
/**
* Map which maintains the default offsets for labels.
* Each plugin which overrides LabelEditPart and wants to have snap back action
* support must provide put an entry into this map. The entry consists of the view's
* creation hint (key) and a <code>Point</code> (value) which constains the label's default
* offset.
*/
private static HashMap snapBackMap = new HashMap();
/**
* Registers the default snap back position for this label that is retrievable from
* the editpolicy that creates the command to move the label back to it's original position.
* Subclasses of <code>LabelEditPart</code> should call this to register their labels default
* position.
*
* @param propertyName <code>String</code> that is usually the property identifier for the label or
* it can be any unique identifier the label subclass desires.
* @param offset <code>Point</code> that is the offset Point position from the keypoint.
*/
public static void registerSnapBackPosition(String propertyName, Point offset) {
snapBackMap.put(propertyName, offset);
}
/**
* Retrieves the default snap back position for this label that was registered using using
* the {@link LabelEditPart#registerSnapBackPosition(String, Point)} method.
*
* @param propertyName <code>String</code> that is usually the property identifier for the label or
* it can be any unique identifier the label subclass desires.
* @return offset <code>Point</code> that is the offset Point position from the keypoint.
*/
public static Point getSnapBackPosition(String propertyName) {
return (Point)snapBackMap.get(propertyName);
}
/**
* constructor
* @param view this edit part's view
*/
public LabelEditPart(View view) {
super(view);
}
protected IFigure createFigure() {
IFigure label = new Figure();
label.setCursor(Cursors.ARROW);
label.setLayoutManager(new ConstrainedToolbarLayout());
return label;
}
/**
* Returns the model's semantic hint. This is used to calculate the reference
* point for the label's figure.
* @return the semantic type
*/
protected String getSemanticType() {
if (semanticHint == null) {
try {
semanticHint = ((String) getEditingDomain().runExclusive(
new RunnableWithResult.Impl() {
public void run() {
setResult(((View) getModel()).getType());
}
}));
} catch (InterruptedException e) {
Trace.catching(DiagramUIPlugin.getInstance(),
DiagramUIDebugOptions.EXCEPTIONS_CATCHING, getClass(),
"getSemanticType", e); //$NON-NLS-1$
Log.error(DiagramUIPlugin.getInstance(),
DiagramUIStatusCodes.IGNORED_EXCEPTION_WARNING,
"getSemanticType", e); //$NON-NLS-1$
}
}
return semanticHint;
}
public void refresh() {
super.refresh();
refreshBounds();
}
/**
* Updates the locator based on the changes to the offset.
*/
public void refreshBounds() {
// try to handle both of resizable and nonresizable labels
if (isResizable()){
handleResizableRefreshBounds();
} else {
handleNonResizableRefreshBoundS();
}
}
/**
* handles non resizable lable refresh bounds
*/
private void handleNonResizableRefreshBoundS() {
int dx = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getLocation_X()))
.intValue();
int dy = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getLocation_Y()))
.intValue();
Point offset = new Point(dx, dy);
if (getParent() instanceof AbstractConnectionEditPart) {
((AbstractGraphicalEditPart) getParent()).setLayoutConstraint(this,
getFigure(), new LabelLocator(
((AbstractConnectionEditPart) getParent())
.getConnectionFigure(), offset, getKeyPoint()));
} else {
getFigure().getParent().setConstraint( getFigure(), new LabelLocator(
getFigure().getParent(),
offset, getKeyPoint()));
}
}
/**
* handles resizable lable refresh bounds
*/
private void handleResizableRefreshBounds() {
int dx = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getLocation_X()))
.intValue();
int dy = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getLocation_Y()))
.intValue();
int width = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getSize_Width()))
.intValue();
int height = ((Integer) getStructuralFeatureValue(NotationPackage.eINSTANCE.getSize_Height()))
.intValue();
Rectangle rectangle = new Rectangle(dx,dy,width,height);
if (getParent() instanceof AbstractConnectionEditPart) {
((AbstractGraphicalEditPart) getParent()).setLayoutConstraint(this,
getFigure(), new ResizableLabelLocator(
((AbstractConnectionEditPart) getParent())
.getConnectionFigure(), rectangle, getKeyPoint()));
} else {
getFigure().getParent().setConstraint( getFigure(), new ResizableLabelLocator(
getFigure().getParent(),
rectangle, getKeyPoint()));
}
}
/**
* check if the edit part had a resizable edit policy installed or not
* @return true is resizable edit policy is installed otherwise false
*/
private boolean isResizable() {
EditPolicy editPolicy = getEditPolicy(EditPolicy.PRIMARY_DRAG_ROLE);
if(editPolicy instanceof ResizableEditPolicy )
return true;
return false;
}
/**
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart#createDefaultEditPolicies()
*/
protected void createDefaultEditPolicies() {
super.createDefaultEditPolicies();
installEditPolicy(
EditPolicy.PRIMARY_DRAG_ROLE,
new NonResizableLabelEditPolicy());
installEditPolicy(
EditPolicy.COMPONENT_ROLE,
new VisibilityComponentEditPolicy());
installEditPolicy(
EditPolicyRoles.SNAP_FEEDBACK_ROLE,
new LabelSnapBackEditPolicy());
}
/** Return a {@link DragTracker} instance. */
public DragTracker getDragTracker(Request request) {
return new DragEditPartsTrackerEx(this) {
protected boolean isMove() {
return true;
}
};
}
/**
* Method isSnapBackNeeded.
* @return boolean
*
* returns false only if the current location of the figure
* is not default one
*/
public boolean isSnapBackNeeded() {
return true;
}
/**
*
* @see AbstractEditPart#refreshVisuals()
*/
protected void refreshVisuals() {
super.refreshVisuals();
refreshForegroundColor();
}
/**
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart#handlePropertyChangeEvent(java.beans.PropertyChangeEvent)
*/
protected void handleNotificationEvent(Notification notification) {
Object feature = notification.getFeature();
if ( NotationPackage.eINSTANCE.getLocation_X().equals(feature)
|| NotationPackage.eINSTANCE.getLocation_Y().equals(feature)
|| NotationPackage.eINSTANCE.getSize_Width().equals(feature)
|| NotationPackage.eINSTANCE.getSize_Height().equals(feature)) {
refreshBounds();
}else if (NotationPackage.eINSTANCE.getLineStyle_LineColor().equals(feature)){
Integer c = (Integer) notification.getNewValue();
setForegroundColor(DiagramColorRegistry.getInstance().getColor(c));
}
else
super.handleNotificationEvent(notification);
}
protected void addNotationalListeners() {
super.addNotationalListeners();
addListenerFilter("PrimaryView", this, getPrimaryView()); //$NON-NLS-1$
}
protected void removeNotationalListeners() {
super.removeNotationalListeners();
removeListenerFilter("PrimaryView"); //$NON-NLS-1$
}
/**
* @see org.eclipse.gef.editparts.AbstractEditPart#getAccessibleEditPart()
*/
protected AccessibleEditPart getAccessibleEditPart() {
if (accessibleEP == null)
accessibleEP = new AccessibleGraphicalEditPart() {
public void getName(AccessibleEvent e) {
e.result = getAccessibleText();
}
};
return accessibleEP;
}
/**
* Concatenates the text of all the text compartment children of
* this editpart to be used as the accessible text.
* @return String the string to be used as the accessible text
*/
protected String getAccessibleText() {
StringBuilder accessibleStringBuilder = new StringBuilder();
for (Iterator iter = getChildren().iterator(); iter.hasNext();) {
IGraphicalEditPart ep = (IGraphicalEditPart) iter.next();
ILabelDelegate label = (ILabelDelegate) ep
.getAdapter(ILabelDelegate.class);
if (label != null) {
accessibleStringBuilder.append(label.getText() + " "); //$NON-NLS-1$
}
}
return accessibleStringBuilder.toString();
}
/**
* Returns an object which is an instance of the given class
* associated with this object. If there are specific class types
* that should be delegated to the connection editpart they must
* be explicitely handled here (e.g. <code>IPropertySource</code>
* is delegated to the connection editpart so that the property pages
* reflect the connection when the label is selected). Otherwise,
* the adapter from this editpart is first retrieved; if this is
* null, then the adapter from the connection editpart is returned.
* This means if a client asks for <code>IView</code> the label view
* will be returned, but if a client asks for <code>IPrimaryView</code>
* the owner view will be returned.
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter(Class key) {
if (key == IPropertySource.class) {
return getParent().getAdapter(key);
}
Object adapterFromSuper = super.getAdapter(key);
if (adapterFromSuper == null) {
return getParent().getAdapter(key);
}
return adapterFromSuper;
}
/**
* Returns a ConnectionLocator.MIDDLE as the key point.
* @return the key point
*/
public int getKeyPoint() {
return ConnectionLocator.MIDDLE;
}
/**
* Returns a <code>Point</code> located on the parent which is
* used by the LabelEditPart to orient itself.
* @return the anchorPoint
*/
public Point getReferencePoint() {
if (getParent() instanceof AbstractConnectionEditPart) {
switch (getKeyPoint()) {
case ConnectionLocator.TARGET:
return calculateRefPoint(LabelViewConstants.SOURCE_LOCATION);
case ConnectionLocator.SOURCE:
return calculateRefPoint(LabelViewConstants.TARGET_LOCATION);
case ConnectionLocator.MIDDLE:
return calculateRefPoint(LabelViewConstants.MIDDLE_LOCATION);
default:
return calculateRefPoint(LabelViewConstants.MIDDLE_LOCATION);
}
}
return ((AbstractGraphicalEditPart)getParent()).getFigure().getBounds().getTopLeft();
}
/**
* Calculates a point located at a percentage of the connection
* @param percent
* @return the point
*/
private Point calculateRefPoint(int percent) {
if (getParent() instanceof AbstractConnectionEditPart) {
PointList ptList = ((Connection)((ConnectionEditPart)getParent()).getFigure()).getPoints();
Point refPoint = PointListUtilities.calculatePointRelativeToLine(ptList, 0, percent, true);
return refPoint;
} else if (getParent() instanceof GraphicalEditPart) {
return ((AbstractGraphicalEditPart)getParent()).getFigure().getBounds().getTopLeft();
}
return null;
}
}