Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
GEF/Troubleshooting Guide
HowTo's
How do I instrument domain model changes that support undo/redo
You can define custom request types that represent model changes for your domain. An example would be how the logic example increments / decrements the LED model item. The IncrementDecrementAction creates a new request constant --> ... request = new Request(INCREMENT_REQUEST); ...
Then the request is sent to the selected objects on the diagram surface. If the selected object is an LEDEditPart, then it has a custom EditPolicy installed on its EditPolicy.COMPONENT_ROLE (LEDEditPolicy) that is used to handle model changes. This EditPolicy is instrumented to understand the INCREMENT_REQUEST in its getCommand method. It then returns a custom command that can handle undo/redo that will get executed on the command stack.
@see LEDEditPolicy#IncrementDecrementCommand
If the request is not understood by the selected EditPart (i.e. no EditPolicy installed on EditPolicy.COMPONENT_ROLE), then a null command is returned. This can indicate to the action that it should be disabled for the user.
How do I do move an element from one container to another
If you're moving an element from one container into another, first you need to consider the model changes that persist the container changes. To do that, an EditPolicy for removing the model element on the source (ContainerEditPolicy) is needed and an EditPolicy for adding the model element (XYLayoutEditPolicy).
If we consider moving a LED from the diagram to a Circuit: First to remove it from the owned container using the OrphanChildCommand in Logic designer which will remove it from the first container. @see LogicContainerEditPolicy#getOrphanChildrenCommand
Then it is added to the target container using an AddCommand @see LogicXYLayoutEditPolicy#getAddCommand
These instrument the model changes. Then the EditParts will listen to the model changes accordingly. Both the DiagramEditPart and CircuitEditPart are LogicContainerEditPart which override the method getModelChildren. Subsequent calls to refreshChildren will synchronize the EditPart children with the model children. Alternatively, for performance they can listen to the model changes specifically which will invoke addChild or removeChild based on the corresponding event. @see LogicEditPart@propertyChange
How do I do "X" when the User double-clicks?
Mouse events are handled by a Draw2D event dispatcher. When using GEF, a DomainEventDispatcher is used which allows heterogeneous handling of mouse events. Mouse presses in general can either be processed by a Figure, or by a Tool. A double-click is also a click, so the first event is a MouseDown, followed by a DoubleClick event. When a MouseDown is dispatched, the event dispatcher will continue to send events to either the Figure or Tool until the Mouse is released. So this means if the Tool processes MouseDown, it continues to get DoubleClick, dragging, and MouseUp. If a Figure consumes the MouseDown event, it will receive these events.
The GEF SelectionTool already processes double-click and will send a SelectionRequest to the target EditPart under the mouse of the type REQ_OPEN. The request is sent by calling EditPart.performRequest(Request). This is the preferred way to do something in response to double-clicks.
Often, GEF clients want to know the location of the context menu so that, for instance, new parts can be created or pasted at that location. The context menu can be brought up via the keyboard (Shift+F10) and there's no guarantee that the mouse cursor will be over the editor (or even the active window) when the context menu comes up. Hence, we recommend to do this in a location-neutral manner. For instance, new parts can be added to the top-left visible corner of the selected container, or the location can be determined based on the location of the part that was copied.
How do I solve Draw2D rendering problems with very large figures
When drawing very large figures with Draw2D, there is sometimes rendering problems on X-Window environments. This is due to a system method call 16 bits limitation (for example we can't draw a line which length is > 32000). To avoid this, client applications using Draw2D should override the default paint methods of Draw2D figures to only ask natively to draw "the visible part" of the figure. Snippet of such code could be found in bugzilla #176984.
Common Mistakes
Freeform Figures
Freeform is a special concept which requires several special implementation classes to work properly. A freeform figure may extend in any direction. Because of this feature, the concept of bounds and preferred size do not apply. The following rules must be followed:
- FreeformViewport. The normal Viewport may not contain a freeform figure.
- XYLayout must not be used as the layout for a freeform figure. Use FreeformLayout instead. Failure to do so may result in infinite loops.
A freeform layered pane must contain freeform layers. For convenience, FreeformLayer can be used as a normal layer. This allows for specialized layers to inherit from it for use with or without freeform functionality.
Implementing Connection Anchors
When implementing your own custom connection anchors, it is important to understand the coordinate systems in Draw2d. An anchor must always deal in absolute coordinates. The anchor's figure's bounds are not absolute. When scrolling or zooming, the figure's bounds must be converted to absolute for both the anchor's location and its reference point. Here is some example code from ChopBoxAnchor:
public Point getReferencePoint() {
Point ref = getBox().getCenter(); getOwner().translateToAbsolute(ref); return ref;
}
Deleting Connections
For applications with multiple levels of containers, delete can be tricky. When objects (i.e. "nodes") in a diagram are deleted, it is necessary to remove, in the very least, the connections between the items being deleted, including contained parts such as children, and any items remaining in the diagram. The recommended place to do this is in the command which deletes the object. Care must be taken in the implementation because both ends of the connection may be deleted using multiple selection. To avoid deleting the connection twice, the application should lazily determine which connections must be deleted. Do this inside the execute() of the command. On undo, the connection will get restored just once. For an example implementation, see DeleteCommand in the Logic example.
Background Threads and Updates
Draw2d, just like Swing, is not thread-safe. Do not call repaint(), revalidate(), or any other method on a figure from a thread other than the Main (UI) thread. One symptom of doing this is a NullPointerException in the DeferredUpdateManager. Use asnycExec(Runnable) or syncExec(Runnable) to perform the changes on the proper thread. Since GEF is built on UI frameworks such as Draw2d, it also is not thread-safe. EditParts should not be updated from background or "job" threads. Use asnycExec(Runnable) or syncExec(Runnable) to perform the changes on the proper thread.