Version: 0.1 | Date: August 11, 2005 |
This guide is a repository for questions on how to accomplish specific tasks within the diagram services layer.
Each shape node view class (Node) will typically need to install a set of styles that allow a certain look of the notation to be persisted. Usually the default style ShapeStyle will be adequate since this covers colors, text, etc. However, it may be useful to add additional styles and/or have custom styles not accounted for in the notation meta-model (org.eclipse.gmf.runtime.notation). For instance, a domain editor may wish to toggle the display of a particular shape to have different looks. The property to store this is notational and consequently should be part of the notation meta-model.
Create a new notation meta-model sub-class. Since the property is typically an attribute of a node, this can be represented in a Style subclass.
-
-
- org.eclipse.gmf.runtime.notation plug-in and fine in the source directories the cat file \src\rosemodel\org.eclipse.gmf.runtime.notation.Notation.cat and the org.eclipse.emf.Ecore.cat files.
- LogicalView describing your notation meta-model (i.e. mynotation)
-
-
- mynotation"
-
-
- mynotation
-
- Hit "Next" until you get to the "Package Selection" page.
-
- org.eclipse.gmf.runtime.notation/src/model) and in the right hand pane select notation.genmodel.
- Ecore check-boxes under the notation root.
- mynotation
-
- mynotation.genmodel. Right click on the root in the editor and choose "Generate Model Code" and the "Generate Edit Code" menu items.
- In the directory/src/model, right click on the mynotation.genmodel file and choose "reload" to reflect the changes.
-
- mynotation.genmodel. Right click on the root in the editor and choose "Generate Model Code" and the "Generate Edit Code" menu items.
- createStyles" function of View Factory subclass that creates the top level view you wish to store the style on.
-
refreshVisuals" and "handlePropertyChangeEvent" functions in the
EditPart class associated with the
notation view and create new function "refreshValue" called within the
"refreshVisuals" override which will perform the update of the
figures based on the style change.
Ex 1. Using the semantic element as the element type to provide against.
<extension
point="org.eclipse.gmf.runtime.diagram.ui.viewProviders">
<viewProvider
class="<YourFullyQualfiedClass">
<Priority
name="Highest">
</Priority>
<object
class="<YourFullyQualfiedSemanticClassToProvideFor>"
id="Nodes">
</object>
<method name=?getSomeMethodCallValue(?)? notValue=?null?>
<context> viewClass="org.eclipse.gmf.runtime.notation.Node"
semanticHints=""
elements="Nodes">
</context>
</viewProvider>
</extension>
Ex 2. Alternative is to check against the proxy interface and retrieve the type ID. This is useful if you wish to provide your view against unresolved elements. When an element is a proxy you wouldn't be able to make a conditional check against some semantic property because it wouldn't be accessible in the case of an unresolved reference. If the element were unresolved the other overridden provider would kick-in instead.
<extension
point="org.eclipse.gmf.runtime.diagram.ui.viewProviders">
<viewProvider
class="<YourFullyQualfiedClass">
<Priority
name="Highest">
</Priority>
<objectclass="org.eclipse.gmf.runtime.emf.core.util.IProxyEObject(org.eclipse.gmf.runtime.emf.core)"id="YourSemanticType">
<methodname="getProxyClassID()"value="<YourSemanticType>">
</method>
</object>
<context> viewClass="org.eclipse.gmf.runtime.notation.Node"
semanticHints=""
elements="Nodes">
</context>
</viewProvider>
</extension>
i.e.
protected Class getNodeViewClass(IAdaptable semanticAdapter,
View containerView, String semanticHint) {
Element el = getSemanticElement(semanticAdapter);
If (el != null) {
...
If (? /* check condition)
Return <YourNewViewClass.class>
}
return null;
}
protected void decorateView(View containerView, View view,
IAdaptable semanticElement, String semanticHint, int index,
booleanpersisted) {
super.decorateView(containerView, semanticAdapter, semanticHint, index, persisted);
View subView =
ViewUtil.getChildBySemanticHint(containerView, <MySemanticHintString>);
if (subView!= null) {
subView.setVisible(false);
}
}
Alternatively you could override the method initializeFromPreferences to retrieve values
from the users preference store and then initialize settings
accordingly. However,
this will get called before population of the contained views, so
it can only be used to initialize top level view settings. i.e. fill / outline color.
Often in a domain application it is useful to control shape appearances at a global level. This alleviates user management of the individual shapes appearance and avoids persistence issues.
The way to accomplish this is to have a global workspace preference that your EditPart listens to and responds accordingly.
1. First you need to add a listener on your EditPart controller (please refer to Eclipse on-line help for adding an application specific preference store). To do this add a nested class in your EditPart that implements the IPropertyChangeListener interface for listening to the Preference store.
/**
* Listener for the PreferenceStore.
* Listen and respond for changes to the
* preference store value.
*
*/
protected class PreferencePropertyChangeListener
implements IPropertyChangeListener {
public void propertyChange(PropertyChangeEvent event) {
// if the property is not the event we're interested in then
// do nothing, return
if (event
.getProperty()
.equals(<MyPreferenceIdentifier>)) {
/* call appropriate refresh method */
refreshGlobalPreferenceAttribute();
getFigure().repaint();
}
}
}
2. Next you need to add this new class as a listener to the property store.
/**
* Initializes the preferenceStore property change
* listener.
*/
private void initPreferenceStoreListener() {
preferenceListener = new PreferencePropertyChangeListener();
IPreferenceStore preferenceStore = (IPreferenceStore) getDiagramPreferencesHint().getPreferenceStore();
preferenceStore.addPropertyChangeListener(preferenceListener);
}
protected void addNotationalListeners() {
super.addNotationalListeners();
initPreferenceStoreListener();
}
3. Then you need to handle the property change event in a method. This requires retrieving the preference global value from the store and then making the appropriate changes to the figure to reflect the global value.
/**
* Refreshes this classifier node figure's gradient fill to reflect
* the preference store value for gradient fill.
*/
protected void refreshGlobalPreferenceAttribute() {
IPreferenceStore preferenceStore = (IPreferenceStore) getDiagramPreferencesHint().getPreferenceStore();
//refresh gradient
boolean myGlobalPreference = true;
myGlobalPreference = preferenceStore.getBoolean( <MyPreferenceIdentifier>);
?
/* do figure synchronization */
getFigure().repaint();
}
To accomplish this you need to hook into the controller of the connection which is the EditPart which synchronizes the model and figure worlds. You need to create a new EditPart provider which is registered against the EditPartService that will override the existing EditPart provider to provide a new EditPart for the connection shape you're interested in.
Similar to the View service extensions, the EditPart provider consists of 3 different components.
1. The xml descriptor specification of the extension in the plug-in.xml file.
2. The provider class which is specified by the xml and provides the mapping of the type to your EditPart class
3. The actual EditPart class that will override the existing behavior.
First the xml:
</extension>
<extension
point="org.eclipse.gmf.runtime.diagram.ui.editpartProviders">
<editpartProvider
class="com.<myPath>.MyOverrideProvider">
<Priority
name="Low">
</Priority>
<object
class="org.eclipse.gmf.runtime.notation.Edge"
id="MyConnectionOverride">
<method
name="getElement()">
<value class="<class path to semantic element>"/>
</method>
</object>
<context
views="MyConnectionOverride">
</context>
</editpartProvider>
</extension>
In this descriptor we are specifying the provider class that and a priority level that the provider will be at relative to other providers. In this case, we know that the existing provider for the relationship is at the "Lowest" priority, so we can provide ours at one level higher - i.e. "Low". The object tag lets us specify the view category we're supplying the EditPart for and the criteria under which our provider will be loaded. In this case, we chose a broad criteria and our provider will be loaded when the connection is of the semantic relationship we expect. In the actual java code of our provider class we can restrict the actual criteria to be based on a more strict criteria, such as some aspect or property of the semantic relationship.
Next the provider class is fairly straightforward. It extends from the abstract class AbstractEditPartProvider and overrides the method for retrieving the connection editpart. In the following example, it's providing a custom EditPart when the shape has a particular keyword installed. More commonly, you would switch based on the semantic type which is set to the value you've defined in your IElementType.
public class MyEditPartProvider extends AbstractEditPartProvider {
protected Class getEdgeEditPartClass(View view) {
EObject el = view.getElement();
if (el != null && el instanceof <MySemanticElement class> ) {
if (? <some condition is satisified> )
return MyCustomEditPart.class;
}
}
return null;
}
}
Finally we can look at the EditPart class itself. In this case we are only interested in overriding the look of the connection. Consequently, we have to override the method that creates the figure in the connection edit part. This method is the createConnectionFigure(). Then we simply use the appropriate Draw2d API's to instantiate a different polyline figure with the look we desire:
public class MyCustomEditPart extends OriginalEditPart {
/**
* @param view
*/
public MyCustomEditPart (View view) {
super(view);
// TODO Auto-generated constructor stub
}
protected Connection createConnectionFigure() {
PolylineConnectionEx conn = new PolylineConnectionEx();
conn.setLineStyle(Graphics.LINE_SOLID);
OpenArrowDecoration sourceDecorative = new OpenArrowDecoration();
sourceDecorative.setTemplate(OpenArrowDecoration.TRIANGLE_TIP);
sourceDecorative.setScale(MapMode.DPtoLP(10),MapMode.DPtoLP(5));
sourceDecorative.setLineStyle(Graphics.LINE_SOLID);
conn.setSourceDecoration(sourceDecorative);
return conn;
}
}
Then when you create the relationship which fits the new critera of the xml and provider, then it will render using the new figure you created in the custom EditPart.
It is possible to view the same notation data in 2 different viewers. Since GEF / GMF implements the MFC design pattern, it is a simple matter to view the same data in different viewers. The "Model" (Notation) is the same in both viewers and synchronized with different "View" (Figure) data through unique controllers (EditParts) in each viewer.
Creating a read-only
viewer of an existing diagram
If you wish to have a read-only view of the diagram, this is
probably well suited to be displayed in an Eclipse View instead of
a full-fledged editor.
In your implementation of the IViewPart the createPartControl would create the Viewer to display the diagram. The DiagramEditPart is retrieved and explicitly set to disable the EditMode capability. It is a perquisite that a DiagramView object is ?in-hand?. This could be retrieved by invoking an action from the Diagram element in the Model Explorer.
// the assumption of this method is that the DiagramView has been pre-loaded
private GraphicalViewer viewer;
private GraphicalViewer viewer;
public class TraceDiagramGraphicalViewer extends DiagramGraphicalViewer {
// no implementation. This class is extended from
// DiagramGraphicalViewer for type information only.
}
public void createPartControl(Composite comp) {
viewer = new TraceDiagramGraphicalViewer();
viewer.createControl(comp);
viewer.getControl().setBackground(ColorConstants.listBackground);
DiagramEditDomain editDomain = new DiagramEditDomain(null);
editDomain.setCommandStack(new DiagramCommandStack(editDomain));
viewer.setEditDomain(editDomain);
viewer.setRootEditPart(new DiagramRootEditPart());
viewer.setEditPartFactory(EditPartService.getInstance());
viewer.setContents(GeoDiagramEditor.diagView));
viewer.flush();
// now disable editing
Assert.isTrue(viewer.getContents() instanceof DiagramEditPart);
DiagramEditPart diagEP = (DiagramEditPart) viewer.getContents();
diagEP.disableEditMode();
In the example #Creating a
read-only viewer of an existing diagram it is necessary to
create a new GraphicalViewer class
(TraceDiagramGraphicalViewer) for the
type information to distinguish EditParts hosted in a regular editor vs. a
custom Viewer to allow for animation. Changing the colors of
individual EditParts can be achieved
dynamically by installing an EditPolicy
on the EditParts condition on them
being owned by the new Viewer class above.
The EditPolicy will then listen to the
appropriate condition and change the color of the EditPart's figures accordingly.
First we need to define an EditPolicyProvider that will install this new
editpolicy. The Extension code in the
plugin.xml would look something like
the following:
<extension
id="TraceEditPolicyProvider"
name="TraceEditPolicyProvider"
point="org.eclipse.gmf.runtime.diagram.ui.editpolicyProviders">
<editpolicyProvider
class="<package namespace>.TraceDiagramEditPolicyProvider">
<Priority
name="Low">
</Priority>
<object
class="org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart"
id="TraceEditPart">
<methodname="getViewer()">
<value class="="<package namespace>.TraceDiagramGraphicalViewer"/>
</method>
</object>
<context
editparts="TraceEditPart">
</context>
</editpolicyProvider>
</extension>
public class TraceDiagramEditPolicy extends GraphicalEditPolicy {
private class TraceMouseMotionListener extends MouseMotionListener.Stub {
private Color color;
/**
* @see com.ibm.etools.draw2d.MouseMotionListener#mouseEntered(MouseEvent)
*/
public void mouseEntered(MouseEvent me) {
color = getHostFigure().getForegroundColor();
getHostFigure().setForegroundColor(new Color(null, new RGB(255, 0, 0)));
getHostFigure().invalidate();
}
/**
* @see com.ibm.etools.draw2d.MouseMotionListener#mouseExited(MouseEvent)
*/
public void mouseExited(MouseEvent me) {
getHostFigure().setForegroundColor(color);
getHostFigure().invalidate();
}
}
/** mouse motion listener for the owner shape and handles */
private TraceMouseMotionListener myMouseListener = new TraceMouseMotionListener();
/**
*
* @see org.eclipse.gef.EditPolicy#activate()
*/
public void activate() {
super.activate();
getHostFigure().addMouseMotionListener(myMouseListener);
}
/**
*
* @see org.eclipse.gef.EditPolicy#deactivate()
*/
public void deactivate() {
getHostFigure().removeMouseMotionListener(myMouseListener);
super.deactivate();
}
}
Example (in LEDEditPart):
/**
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart#getPrimaryDragEditPolicy()
*/
public EditPolicy getPrimaryDragEditPolicy() {
return new NonResizableEditPolicyEx();
}
Copyright (c) 2000,2005 IBM Corporation and others. All Rights Reserved.