package org.xmind.ui.internal.branch; import java.util.Iterator; import java.util.List; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.PositionConstants; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Insets; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.xmind.gef.GEF; import org.xmind.gef.draw2d.IReferencedFigure; import org.xmind.gef.draw2d.decoration.IDecoration; import org.xmind.gef.draw2d.geometry.HorizontalFlipper; import org.xmind.gef.draw2d.geometry.ITransformer; import org.xmind.gef.part.IPart; import org.xmind.ui.branch.AbstractBranchStructure; import org.xmind.ui.branch.BoundaryLayoutHelper; import org.xmind.ui.branch.IInsertion; import org.xmind.ui.branch.Insertion; import org.xmind.ui.decorations.IBranchConnectionDecoration; import org.xmind.ui.decorations.IBranchConnections2; import org.xmind.ui.mindmap.IBranchPart; import org.xmind.ui.mindmap.IBranchRangePart; import org.xmind.ui.mindmap.IPlusMinusPart; import org.xmind.ui.mindmap.ISummaryPart; import org.xmind.ui.mindmap.ITopicPart; import org.xmind.ui.mindmap.MindMapUI; import org.xmind.ui.tools.ParentSearchKey; public class TimelineHorizontalStructure extends AbstractBranchStructure { private ITransformer t = new HorizontalFlipper(); private boolean upwards; public TimelineHorizontalStructure(boolean upwards) { this.upwards = upwards; } @Override protected void doFillPlusMinus(IBranchPart branch, IPlusMinusPart plusMinus, LayoutInfo info) { Point ref = info.getReference(); t.setOrigin(ref); Rectangle topicBounds = info.getCheckedClientArea(); topicBounds = t.tr(topicBounds); IFigure pmFigure = plusMinus.getFigure(); Dimension size = pmFigure.getPreferredSize(); int x = ref.x - size.width / 2; int y = isUpwards(branch) ? topicBounds.y() - size.height : topicBounds.bottom(); Rectangle r = new Rectangle(x, y, size.width, size.height); info.put(pmFigure, r); } @Override protected void doFillSubBranches(IBranchPart branch, List<IBranchPart> subBranches, LayoutInfo info) { int majorSpacing = getMajorSpacing(branch); int minorSpacing = getMinorSpacing(branch); Point ref = info.getReference(); t.setOrigin(ref); Rectangle refBounds = info.getCheckedClientArea(); refBounds = t.tr(refBounds); int x = ref.x + majorSpacing; int totalHeight = calcTotalChildrenHeight(branch, minorSpacing, true); int y = isUpwards(branch) ? refBounds.y() - totalHeight - majorSpacing : refBounds.bottom() + majorSpacing; IInsertion insertion = getCurrentInsertion(branch); BoundaryLayoutHelper helper = getBoundaryLayoutHelper(branch); int num = subBranches.size(); for (int i = 0; i < num; i++) { if (insertion != null && i == insertion.getIndex()) { Rectangle r = insertion.createRectangle(x, y); info.add(r); y += r.height + minorSpacing; } IBranchPart subBranch = subBranches.get(i); IFigure subBranchFigure = subBranch.getFigure(); Insets ins = helper.getInsets(subBranch); Dimension size = subBranchFigure.getPreferredSize(); Rectangle r = new Rectangle(x + ins.left, y + (isUpwards(subBranch) ? ins.bottom : ins.top), size.width, size.height); info.put(subBranchFigure, r); y += size.height + ins.getHeight() + minorSpacing; } if (insertion != null && num == insertion.getIndex()) { Dimension insSize = insertion.getSize(); if (insSize != null) { Rectangle r = new Rectangle(x, y, insSize.width, insSize.height); info.add(r); } } } protected void doFillCalloutBranches(IBranchPart branch, List<IBranchPart> calloutBranches, LayoutInfo info) { if (branch.isCentral()) { for (IBranchPart calloutBranch : calloutBranches) { calloutBranch.getFigure().setVisible(false); } IBranchConnections2 calloutConnections = branch .getCalloutConnections(); for (int index = 0; index < calloutBranches.size(); index++) { IDecoration decoration = calloutConnections .getDecoration(index); if (decoration instanceof IBranchConnectionDecoration) { ((IBranchConnectionDecoration) decoration) .setVisible(branch.getFigure(), false); } } return; } IBranchPart parentBranch = branch.getParentBranch(); int orientation = getChildTargetOrientation(parentBranch, branch); boolean left = PositionConstants.EAST == orientation; boolean right = PositionConstants.WEST == orientation; boolean south = PositionConstants.NORTH == orientation; boolean north = PositionConstants.SOUTH == orientation; for (IBranchPart calloutBranch : calloutBranches) { Rectangle parentTopicArea = info .get(branch.getTopicPart().getFigure()); Rectangle parentBranchArea = info.getCheckedClientArea(); IReferencedFigure calloutBranchFigure = (IReferencedFigure) calloutBranch .getFigure(); ITopicPart calloutPart = calloutBranch.getTopicPart(); if (calloutPart == null) continue; IFigure calloutFigure = calloutPart.getFigure(); Dimension calloutSize = calloutFigure.getPreferredSize(); //over parent topic center org.xmind.core.util.Point position = calloutBranch.getTopic() .getPosition(); if (position == null) position = new org.xmind.core.util.Point(); boolean originPosition = position.x == 0 && position.y == 0; int offsetX = originPosition ? 0 : position.x; int offsetY = originPosition ? -calloutSize.height / 2 - parentTopicArea.height / 2 - 10 : position.y; boolean upDown = offsetY < 0; int dummyCalloutX = parentTopicArea.getCenter().x + offsetX - calloutSize.width / 2; if (left) { //limit left movable boundary int dx = (dummyCalloutX + calloutSize.width) - (parentTopicArea.x + parentTopicArea.width); offsetX = dx > 0 ? offsetX - dx : offsetX; } else if (right) { //limit right movable boundary int dx = dummyCalloutX - parentTopicArea.x; offsetX = dx < 0 ? offsetX - dx : offsetX; } Point reference = info.getReference(); Point translated = reference.getTranslated(offsetX, offsetY); Rectangle bounds = calloutBranchFigure .getPreferredBounds(translated); int subRectX = left || south || north ? parentBranchArea.x : parentBranchArea.x + parentTopicArea.width; int subRectY = left || right || north ? parentBranchArea.y : parentBranchArea.y + parentTopicArea.height; int subRectWidth = left || right ? parentBranchArea.width - parentTopicArea.width : parentBranchArea.width; int subRectHeight = left || right ? parentBranchArea.height : parentBranchArea.height - parentTopicArea.height; Rectangle subRect = new Rectangle(subRectX, subRectY, subRectWidth, subRectHeight); boolean touchSub = subRect.touches(bounds); boolean touchParentTopic = bounds.touches(parentTopicArea); if (touchSub) { int y = upDown ? subRect.y - bounds.height - 10 : subRect.bottom() + 10; bounds.setY(y); } else if (touchParentTopic) { int y = upDown ? parentTopicArea.y - bounds.height - 10 : parentTopicArea.bottom() + 10; bounds.setY(y); } int y = isUpwards(branch) ? Math.min(parentBranchArea.y, bounds.bottom()) - calloutBranch.getFigure().getPreferredSize().height : Math.max(parentBranchArea.bottom(), bounds.y); bounds.setY(y); info.put(calloutBranchFigure, bounds); } } private int calcTotalChildrenHeight(IBranchPart branch, int minorSpacing, boolean withInsertion) { int totalHeight = 0; BoundaryLayoutHelper helper = getBoundaryLayoutHelper(branch); Iterator<IBranchPart> it = branch.getSubBranches().iterator(); while (it.hasNext()) { IBranchPart subBranch = it.next(); totalHeight += subBranch.getFigure().getPreferredSize().height + helper.getInsets(subBranch).getHeight(); if (it.hasNext()) totalHeight += minorSpacing; } if (withInsertion) { IInsertion ins = getCurrentInsertion(branch); if (ins != null) { Dimension insSize = ins.getSize(); if (insSize != null) totalHeight += minorSpacing + insSize.height; } } return totalHeight; } public int getSourceOrientation(IBranchPart branch) { if (isUpwards(branch)) return PositionConstants.NORTH; return PositionConstants.SOUTH; } public int getChildTargetOrientation(IBranchPart branch, IBranchPart subBranch) { return PositionConstants.WEST; } public IPart calcNavigation(IBranchPart branch, String navReqType) { if (!branch.getSubBranches().isEmpty()) { if (GEF.REQ_NAV_RIGHT.equals(navReqType)) return getSubTopicPart(branch, 0); } return super.calcNavigation(branch, navReqType); } public IPart calcChildNavigation(IBranchPart branch, IBranchPart sourceChild, String navReqType, boolean sequential) { if (GEF.REQ_NAV_UP.equals(navReqType)) { return getSubTopicPart(branch, sourceChild.getBranchIndex() - 1); } else if (GEF.REQ_NAV_DOWN.equals(navReqType)) { return getSubTopicPart(branch, sourceChild.getBranchIndex() + 1); } else if (!sequential) { if (GEF.REQ_NAV_LEFT.equals(navReqType)) { return branch.getTopicPart(); } } return super.calcChildNavigation(branch, sourceChild, navReqType, sequential); } @Override public int calcChildDistance(IBranchPart branch, ParentSearchKey key) { IFigure branchFigure = branch.getFigure(); IReferencedFigure topicFigure = (IReferencedFigure) branch .getTopicPart().getFigure(); Point ref = topicFigure.getReference(); t.setOrigin(ref); Point childRef = t.tp(getChildRef(branch, ref, key)); Rectangle branchBounds = t.tr(branchFigure.getBounds()); Rectangle topicBounds = t.tr(topicFigure.getBounds()); Rectangle childBounds = t.tr(key.getFigure().getBounds()); int dx = childRef.x - ref.x; int dy = isUpwards(branch) ? topicBounds.y() - childRef.y : childRef.y - topicBounds.bottom(); if (dy > 0 && childBounds.x <= ref.x) { if (childRef.x < branchBounds.right() + MindMapUI.SEARCH_RANGE / 2) { return Math.abs(dx) + Math.abs(dy); } int d = dx * dx + dy * dy; return d; } return super.calcChildDistance(branch, key); } protected int calcInsIndex(IBranchPart branch, ParentSearchKey key, boolean withDisabled) { if (branch.getSubBranches().isEmpty() || branch.isFolded()) return withDisabled ? 0 : -1; ITopicPart topic = branch.getTopicPart(); if (topic == null) return withDisabled ? 0 : -1; IFigure topicFigure = topic.getFigure(); Point ref = ((IReferencedFigure) topicFigure).getReference(); t.setOrigin(ref); Point childRef = t.tp(getChildRef(branch, ref, key)); Dimension insSize = calcInsSize(key.getFigure()); int insHeight = insSize.height; int minorSpacing = getMinorSpacing(branch); Rectangle topicBounds = t.t(topicFigure.getBounds()); int y = 0; if (isUpwards(branch)) { y = topicBounds.y(); if (!branch.getSubBranches().isEmpty()) { IPlusMinusPart pm = branch.getPlusMinus(); if (pm != null) y -= pm.getFigure().getSize().height; y -= calcTotalChildrenHeight(branch, minorSpacing, true); } } else { y = topicBounds.bottom(); if (!branch.getSubBranches().isEmpty()) { IPlusMinusPart plusMinus = branch.getPlusMinus(); if (plusMinus != null) { y += plusMinus.getFigure().getSize().height; } } } int ret = calcInsIndex(branch, y, childRef, insHeight, minorSpacing, withDisabled); return ret; } private int calcInsIndex(IBranchPart branch, int startY, Point childRef, int insHeight, int spacing, boolean withDisabled) { int ret = 0; int sum = 0; List<IBranchPart> subBranches = branch.getSubBranches(); int num = subBranches.size(); for (IBranchPart subBranch : subBranches) { IFigure subFigure = subBranch.getFigure(); int h = getBorderedSize(branch, subBranch).height; int hint = startY + sum + (insHeight + h + spacing) / 2; if (childRef.y < hint) { return ret; } sum += h + spacing; if (withDisabled || subFigure.isEnabled()) ret++; } return withDisabled ? num : -1; } private Dimension calcInsSize(IReferencedFigure child) { return child.getSize().scale(0.8); } public IInsertion calcInsertion(IBranchPart branch, ParentSearchKey key) { int newIndex = calcInsIndex(branch, key, true); Dimension newSize = calcInsSize(key.getFigure()); return new Insertion(branch, newIndex, newSize); } public int getSummaryDirection(IBranchPart branch, ISummaryPart summary) { return PositionConstants.EAST; } public int getRangeGrowthDirection(IBranchPart branch, IBranchRangePart range) { if (isUpwards(branch)) return PositionConstants.NORTH; return PositionConstants.SOUTH; } public int getQuickMoveOffset(IBranchPart branch, IBranchPart child, int direction) { if (direction == PositionConstants.SOUTH) return 1; if (direction == PositionConstants.NORTH) return -1; return super.getQuickMoveOffset(branch, child, direction); } @Override protected Point calcFirstChildPosition(IBranchPart branch, ParentSearchKey key) { int x = getMajorSpacing(branch) + key.getInvent().getSize().width / 2; int y = branch.getFigure().getSize().height / 2 + getMajorSpacing(branch) + key.getFigure().getSize().height / 2; return getFigureLocation(branch.getFigure()).getTranslated(x, isUpwards(branch) ? -y : y); } private boolean isUpwards(IBranchPart branch) { return upwards; } }