/** * Layouts all lifelines. The lifelines are placed at a certain height and along the x-axis with a * specified distance between them. Lifelines that contain a stereotype in their name are placed * slightly higher such that the bottom of each name is flush among all lifelines. */ private void layoutLifelines() { float currentLifelineX = LIFELINE_START_X; Set<Lifeline> layoutedLifelines = new HashSet<Lifeline>(); // Layout lifelines if they have no position. Otherwise they might have been moved on the // x-axis, // which we allow to do. for (InteractionFragment fragment : specification.getFragments()) { // In case the message add event hasn't been received yet there will be no lifeline that is // covered. // This is the case when a message and a reply are added. // The structure exists, but not all commands have been executed. for (Lifeline lifeline : fragment.getCovered()) { if (!layoutedLifelines.contains(lifeline)) { LifelineView lifelineView = lifelines.get(lifeline); LayoutElement layoutElement = lifelineView.getLayoutElement(); if (layoutElement.getX() == 0) { lifelineView.getLayoutElement().setX(currentLifelineX); lifelineView.getLayoutElement().setY(LIFELINE_Y); } /** * Lifelines of metaclasses are double as high as regular lifelines. This causes problems * when dealing with combined fragments or when drawing a message from a message view of a * create message to one (as the first message). Therefore, these lifelines are moved up * so that all lifelines are flush on the bottom of their name container. * * @see issue #230 */ if (lifeline.getRepresents() instanceof StructuralFeature) { StructuralFeature structuralFeature = (StructuralFeature) lifeline.getRepresents(); if (structuralFeature.isStatic()) { float y = LIFELINE_Y - (lifelineView.getNameHeight() / 2); if (y != lifelineView.getLayoutElement().getY()) { lifelineView.getLayoutElement().setY(y); } } } currentLifelineX = currentLifelineX + lifelineView.getWidth() + LIFEINE_SPACING; layoutedLifelines.add(lifeline); } } } }
/** * Layouts all combined fragments of this view. The width is determined by the position of the * covered lifelines. */ private void layoutCombinedFragments() { for (Entry<CombinedFragment, CombinedFragmentView> entry : combinedFragments.entrySet()) { CombinedFragmentView combinedFragmentView = entry.getValue(); float minX = Float.MAX_VALUE; float maxX = -1; for (Lifeline lifeline : entry.getKey().getCovered()) { LifelineView lifelineView = lifelines.get(lifeline); Vector3D position = lifelineView.getPosition(TransformSpace.GLOBAL); minX = Math.min(minX, position.getX()); maxX = Math.max(maxX, position.getX() + lifelineView.getWidth() + LIFEINE_SPACING); /** * Take into consideration the size of other fragments as well, since they could be quite * wide. */ for (InteractionFragment fragment : lifeline.getCoveredBy()) { /** * Check whether the fragment is part of this combined fragment in its containment * hierarchy. */ if (EcoreUtil.isAncestor(entry.getKey(), fragment)) { RamRectangleComponent fragmentView = fragments.get(fragment); if (fragmentView != null) { Vector3D fragmentPosition = fragmentView.getPosition(TransformSpace.GLOBAL); maxX = Math.max( maxX, fragmentPosition.getX() + fragmentView.getWidth() + LIFEINE_SPACING); } } } } Vector3D position = combinedFragmentView.getPosition(TransformSpace.GLOBAL); position.setX(minX); combinedFragmentView.setPositionGlobal(position); combinedFragmentView.setMinimumWidth(maxX - minX); } }