| 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.
- Open Rational Rose
- Select "File Import" using type Category Files (*.cat)
- Navigate to the 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.
- Create a new package under your LogicalView describing your notation meta-model (i.e. mynotation)
- Drag the style you wish to extend from the notation meta-model imported package onto the main diagram of the package you just created.
- Create your new style class with the appropriate attributes and draw a generalization to the style you wish to extend from.
- Right click on the package and select "control mynotation"
- Save model
- Go to Eclipse, choose "New Project / Eclipse Modeling Framework / EMF Project"
- Name the project com.*.mynotation
- Select "Load from a Rose class model"
- Navigate to the rose model you just created. Hit "Next" until you get to the "Package Selection" page.
- Click "Browse"
- Navigate to notation meta-model directory (org.eclipse.gmf.runtime.notation/src/model) and in the right hand pane select notation.genmodel.
- In the bottom pane select the Notation and Ecore check-boxes under the notation root.
- Select the mynotation
- Click "Finish"
- Open the mynotation.genmodel. Right click on the root in the editor and choose "Generate Model Code" and the "Generate Edit Code" menu items.
- Make the changes in the Rose model and save. In the directory/src/model, right click on the mynotation.genmodel file and choose "reload" to reflect the changes.
- Hit "Next" in the wizard until "Finish" appears and the click that.
- Open the mynotation.genmodel. Right click on the root in the editor and choose "Generate Model Code" and the "Generate Edit Code" menu items.
- Instrument the new style in the "createStyles" function of View Factory subclass that creates the top level view you wish to store the style on.
-
Overwrite "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>
<object
class="org.eclipse.gmf.runtime.emf.core.util.IProxyEObject(org.eclipse.gmf.runtime.emf.core)"
id="YourSemanticType">
<method
name="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,
boolean
persisted) {
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">
<method
name="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.