Projects Tools Projects Hosted Projects
|
by Ivo Vigan vigan (at) ifi.unizh.ch
Abstract
This tutorial discusses the topic of developing labels which belong to a connection and can be moved and resized by user interactions. Since GEF doesn't offer a solution for this concern, everything has to be implemented at your own. At first glance one might think this problem doesn't require a whole tutorial. But if one delves into this task, several problems appear which are discussed in the next sections. It must emphasized, that the following tutorial doesn't explain the general steps of creating an GEF application, but only the steps associated with the label. Suppose that we want to create a graphical editor in which we can draw class or objects connected by associations. The associations are annotated with two role names and the corresponding cardinalities. Each cardinality/role pair should be movable and resizable in an independent way (but is also useful if one likes to create direct editable labels of a connection). Therefore, suppose the following class diagram shows the part of an architecture for such an editor which implements these cardinality/role pairs as labels.
1. Creating a suitable Locator for controlling the lables locationThe cause of the first obstacle lies in the fact, that a PolyLineConnection only offers the possibility to place figures added to the connection by using a delegating layout manager, which is not capable of moving and resizing figures by user interaction. To place a figure, a layout manager needs to know the constraints of the figure; constraints are data attached to each figure that gives additional guidance to the layout manager. While the delegating layout manager expects its figures to have a constraint which implements the Locator interface, the XYLayoutmanager - which is used to move and resize figures - requires the figures to have its constraint in form of a rectangle.What we therefore need, is an object which handles both types of constraints. This class is called DelegatingLocator.
1.1 DelegatingLocatorThe main function this object is placing the labels according to the endpoints of the polyline and placing the labels according to the offset, the user determined by dragging the label. This class can handle constraint changes in rectangle and locator form and sets the correct bounds of the label. Therefore, 3 Rectangles are required:
Methods: The setConstraint method is being called from the refreshVisuals() method of the RoleEditPart. The constraints to be set - in from of a Rectangle - are retrieved from the RoleModel by the getLocation method. The calculateOffset sets the coordinates of the polylineRect. Therefore, the endPointLocator, an instance of the BlindLocator, is used to calculate the location. The BlindLocator knows the Connection and the end to which it is associated:
The relocate method is called from the layout manager each time something relevant changes. This method relocates the label according to the offset Rectanlge and the polyline rectangle, both stored in the DelegatingLocator object. The actual relocating is done by the setBounds method of the IFigure class which is called in this relocate mehod. It's important, that the relocate method fires a property change Message which is caught by the RoleEditPart. Eachtime the the RoleEditPart receives a this Message, the RoleModels location has to be updated. This is done in the updateModelLocation() method of the RoleEditPart by calling the setLocation() method of the RoleModel.
1.2 BlindLocatorThe DelegatingLocator uses a BlindLocator to keep the labels position aware of the association endpoint positions. A ConnectionEndpointLocator has a method private IFigure getConnectionOwner() which wants to know both the start- and endpoint anchor of the connection. The locator used in the DelegatingLocator doesn't know anything about anchors. Therefore the Class BlindLocator is simply a copy of the Class ConnectionEndpointLocator with the only difference, that getConnectionOwner() returns null. Unfortunately, this has to be solved like this, since too many methods used for relocating are private methods of the ConnectionEndpointLocator.
1.3 AssociationEditPartIntroducing DelegatingLocator and BlindLocator solves the problem of different constraint types (Locator/Rectangle). The things that are left to do, are implementing the EditParts of the corresponding Association and the corresponding EditPolicy, so the user can interact with the label. This EditPart instantiates two RoleEditPart objects which act as children. This is done in the createChild method.
Furthermore, a DelegatingLayoutEditPolicy has to be installed by the Key of EditPolicy.LAYOUT_ROLE in the createEditPolicies() method. This policy handles the move and resize requests.
1.4 RoleEditPartThe RoleEditPart allows users to perform operations on the RoleLabel. The most important method is the refreshVisuals() method. This method is called as soon as the model has changed. In this case the setBounds() method of the DelegatingLocator is called, since the location of the label has to be updated. Beside the call of the refreshVisuals() method, the setLayoutConstraint(RoleEditpart, RoleLabel, DelegatingLocator) method of the AssociationEditPart has to be called. By this call, the RoleEditParts' constraints are applied to the LayoutManager. This is what causes the childs to get updated during the next revalidation cycle.
The second method is needed to update the location saved in the model, each time the RoleLabel gets relocated by the BlindLocator. This is necessary, since the DelegatingLocator doens't know the RoleModel (according to the MVC pattern). Therefore the RoleEditPart (= Controller) updates the model in the propertyChange() method.
1.5 DelegatingLayoutEditPolicyAs I denoted under 1.3, the DelegatingLayoutEditPolicy is needed to create the VisualizableModelElementSetConstraintCommand whenever a ChangeBoundsRequest occures. This class extends the ConstrainedLayoutEditPolicy. The only overridden method is the createChangeConstraintCommand() method, which returns a new VisualizableModelElementSetConstraintCommand.
1.6 VisualizableModelElementSetConstraintCommandThis Command actually changes the location and/or size of the label. This is done by calling the setLocation() and setSize() methods of the role model.
1.7 MyPanningSelectionToolThis class extends the PanningSelectionTool. The method handleButtonDown() is the only method which has to be overwritten, since we have to use our own DragTracker if we are moving a role label. handleButtonDown() is the only place where we can set the DragTracker. The reason for using our own DragTracker is the following: By default, if a RoleLabel is moved, as soon as the mouse button is released, an AddCommand gets created since the parent of the role label changed from AssociationEditPart to the main diagramm container. But this is exactly what we don't want to happen. We want our RoleEditPart to always be the child of the AssociationEditPart. So what we have to change is the criteria in the DragTracker by which it is decided if an operation is a move- or add operation. These changes are discussed under 1.8.
1.8 MyDragTrackerThis class extends the DragTracker class. As mentioned above, the move/add criteria of the DragTracker has to be changed. This is done by overwriting the isMove() method. This method has to return true under any circumstances if the source EditPart is an instance of the class RoleEditPart.
Last Modified 2/20/06 11:43 PM | Hide Tools |