Before I start with my first post, I should mention that most of the help you can find it on http://wiki.eclipse.org/GMP/Graphiti wiki page, and on http://www.eclipse.org/forums/index.php/f/187/ forum.
I assume that the you already know something about Eclipse Graphiti, if not please read the Graphiti developer guide from here.
Let me start with my first tutorial, about how to create a collapsible feature, and how to use it in your diagram. I won't provide the model classes, but I will use the generic name BusinessObject which will indicate your model class.
First you need to create a custom feature that will do the collapsing and extending of your shapes. Lets call this feature
CollapseFeature
and this will override the AbstractCustomFeature
. The two most important functions that need to be overridden are canExecute
, and execute
. In the first one you tell whether the target shape can be collapsed or not. Here is the complete java code of the CollapseFeature
class.import java.util.Iterator; import org.eclipse.graphiti.features.IFeatureProvider; import org.eclipse.graphiti.features.IResizeShapeFeature; import org.eclipse.graphiti.features.context.IContext; import org.eclipse.graphiti.features.context.ICustomContext; import org.eclipse.graphiti.features.context.impl.ResizeShapeContext; import org.eclipse.graphiti.features.custom.AbstractCustomFeature; import org.eclipse.graphiti.mm.algorithms.Ellipse; import org.eclipse.graphiti.mm.pictograms.Anchor; import org.eclipse.graphiti.mm.pictograms.Connection; import org.eclipse.graphiti.mm.pictograms.ContainerShape; import org.eclipse.graphiti.mm.pictograms.PictogramElement; import org.eclipse.graphiti.mm.pictograms.Shape; import org.eclipse.graphiti.services.Graphiti; public class CollapseFeature extends AbstractCustomFeature{ public CollapseFeature(IFeatureProvider fp) { super(fp); } @Override public boolean canExecute(ICustomContext context) { boolean ret = false; PictogramElement[] pes = context.getPictogramElements(); if (pes != null && pes.length == 1) { Object bo = getBusinessObjectForPictogramElement(pes[0]); //Add more of the objects that collapse here if (bo instanceof BusinessObject) { ret = true; } } return ret; } @Override public boolean isAvailable(IContext context) { return true; } public void execute(ICustomContext context) { PictogramElement[] pes = context.getPictogramElements(); if (pes != null && pes.length == 1) { Object bo = getBusinessObjectForPictogramElement(pes[0]); if(bo instanceof BusinessObject){ collapseShape(pes[0]); } } } /** * Collapse the shape for the BusinessObject * * @param pe PictogamElement for the shape of the object */ public void collapseShape(PictogramElement pe){ ContainerShape cs = (ContainerShape) pe; int width = pe.getGraphicsAlgorithm().getWidth(); int height = pe.getGraphicsAlgorithm().getHeight(); int changeWidth = 0; int changeHeight = 0; boolean visible = false; if(Graphiti.getPeService().getPropertyValue(pe, "isCollapsed") == null || Graphiti.getPeService().getPropertyValue(pe, "isCollapsed").equals("false")){ Graphiti.getPeService().setPropertyValue(pe, "initial_width", String.valueOf(width)); Graphiti.getPeService().setPropertyValue(pe, "initial_height", String.valueOf(height)); visible = false; }else if(Graphiti.getPeService().getPropertyValue(pe, "isCollapsed") != null && Graphiti.getPeService().getPropertyValue(pe, "isCollapsed").equals("true")){ changeWidth = Integer.parseInt(Graphiti.getPeService().getPropertyValue(pe, "initial_width")); changeHeight = Integer.parseInt(Graphiti.getPeService().getPropertyValue(pe, "initial_height")); Graphiti.getPeService().setPropertyValue(pe, "isCollapsed", "false"); visible = true; } ResizeShapeContext context1 = new ResizeShapeContext(cs); context1.setSize(changeWidth, changeHeight); context1.setLocation(cs.getGraphicsAlgorithm().getX(), cs.getGraphicsAlgorithm().getY()); IResizeShapeFeature rsf = getFeatureProvider().getResizeShapeFeature(context1); if (rsf.canExecute(context1)) { rsf.execute(context1); } if(!visible){ Graphiti.getPeService().setPropertyValue(pe, "isCollapsed", "true"); } //visible/invisible all the children makeChildrenInvisible(cs, visible); } /** * Recursive function that makes all the children inside a shape visible/invisible * * @param cs ContainerShape * @param visible true/false */ public void makeChildrenInvisible(ContainerShape cs, boolean visible){ if(cs.getChildren().isEmpty()){ return; }else{ Iteratoriter = cs.getChildren().iterator(); while (iter.hasNext()) { Shape shape = iter.next(); if(shape instanceof ContainerShape){ //It is another shape makeChildrenInvisible((ContainerShape) shape, visible); shape.setVisible(visible); Anchor anchr = shape.getAnchors().get(0); boolean initVisible = false; //Check whether the initial shape is visible or not for(Shape shape1 : ((ContainerShape) shape).getChildren()){ if(shape1.getGraphicsAlgorithm() instanceof Ellipse){ initVisible = shape1.isVisible(); } } for(int i=0; i < anchr.getIncomingConnections().size(); i++){ Connection conn = anchr.getIncomingConnections().get(i); if(initVisible){ //Change visibility only to visible connections conn.setVisible(visible); for(int j=0; j< conn.getConnectionDecorators().size(); j++){ conn.getConnectionDecorators().get(j).setVisible(visible); } } } for(int i=0; i < anchr.getOutgoingConnections().size(); i++){ Connection conn = anchr.getOutgoingConnections().get(i); conn.setVisible(visible); for(int j=0; j< conn.getConnectionDecorators().size(); j++){ conn.getConnectionDecorators().get(j).setVisible(visible); } } } } } } }
Most of the job is done in the private function
collapseShape
. This function saves the initial width and initial height of the shape, and also saves a state if this shape is collapsed already or not.
After that it creates a ResizeShapeContext
and calls it for this shapes. You should know each feature can have its own ResizeShapeFeature
defined in the DialogueFeatureProvider
. The code from line 88-95 does just that. It gets the ResizeShapeFeature
if it is defined for this context (this specific feature), if not it uses the default one, and it executes the resizing.
The line 96-98 is very important to be after the resizing, and I will tell you later why.The trickiest function here is
makeChildrenInvisible
which recursively goes for each children of the shape and sets its visibility accordingly. It also sets the visibility of the incoming and outgoing connections. In the incoming connections I firs check if they are visible. This is because I have a artificial connection from a shape within the same container shape. You can ignore this part, and the part that checks whether this connection is visible or not. But maybe this will give some inspirations for your code :) (Line 125-130).Next part, is to define a context button in the button pad based on this custom feature. This is done in your
DialogueToolBehaviourProvider
class in the function getContextButtonPad
, with the following code:
@Override public IContextButtonPadData getContextButtonPad(IPictogramElementContext context) { IContextButtonPadData data = super.getContextButtonPad(context); PictogramElement pe = context.getPictogramElement(); EObject bo = (EObject) getFeatureProvider().getBusinessObjectForPictogramElement(pe); // 1. set the generic context buttons // note, that we do not add 'remove' (just as an example) setGenericContextButtons(data, pe, CONTEXT_BUTTON_DELETE | CONTEXT_BUTTON_UPDATE ); // 2. set the collapse button CustomContext cc = new CustomContext(new PictogramElement[] { pe }); ICustomFeature[] cf = getFeatureProvider().getCustomFeatures(cc); for (int i = 0; i < cf.length; i++) { ICustomFeature iCustomFeature = cf[i]; if (iCustomFeature instanceof CollapseFeature && iCustomFeature.canExecute(cc)) { String image = IPlatformImageConstants.IMG_EDIT_COLLAPSE; String collapseExpand = "Collapse"; if(Boolean.parseBoolean(Graphiti.getPeService().getPropertyValue(pe, "isCollapsed"))){ image = IPlatformImageConstants.IMG_EDIT_EXPAND; collapseExpand = "Expand"; } String name = ""; if(bo instanceof BusinessObject){ BusinessObject bo2= (BusinessObject)bo; if(bo2!=null && bo2.getName()!=null){ name = bo2.getName(); } } IContextButtonEntry collapseButton = new ContextButtonEntry(iCustomFeature, cc); collapseButton.setDescription(collapseExpand+" "+name); collapseButton.setText(collapseExpand); collapseButton.setIconId(image); data.setCollapseContextButton(collapseButton); break; } } return data; }
The thing I mention earlier, why you should have the property isCollapsed set to true is to be able to show the collapse and expand images. I had problems if they are not in that order.
This is how it looks finally on a running diagram:
Figure 1: Not collapsed shape |
Figure 2: Collapsed shape |
Maybe the above code is not generic enough and it is more suitable for my needs, but I am sure you will be able to adjust it to your needs. If not, don't hesitate to drop me a question.
That is all for today. I am very happy to read your comments and answer your questions if you have any.
Till the next time.
No comments:
Post a Comment