/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.branch; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; 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.Rectangle; import org.eclipse.swt.SWT; import org.xmind.gef.draw2d.ReferencedLayoutData; import org.xmind.gef.draw2d.geometry.Geometry; import org.xmind.ui.internal.figures.BoundaryFigure; import org.xmind.ui.mindmap.IBoundaryPart; import org.xmind.ui.mindmap.IBranchPart; public class BoundaryLayoutHelper { public static interface ISubBranchInsetsCalculator { void calculateSubBranchInsets(BoundaryData boundary, SubBranchData subBranch, Insets insets); } public static class BoundaryData implements Comparable<BoundaryData> { public IBoundaryPart boundary; public IFigure boundaryFigure; private Insets prefInsets; private List<IBranchPart> subBranches; private Map<IBranchPart, Insets> branchBorders = new HashMap<IBranchPart, Insets>(); private int startIndex = -1; private int endIndex = -1; private int direction; private boolean overall; public BoundaryData(IBoundaryPart boundary, int direction, Rectangle prefBoundaryBounds) { this.boundary = boundary; this.boundaryFigure = boundary.getFigure(); this.prefInsets = boundaryFigure.getInsets(); if (prefBoundaryBounds != null && ((BoundaryFigure) boundary.getFigure()).isTitleVisible() && boundary.getTitle() != null && boundary.getTitle().getFigure() != null) { Dimension s = boundary.getTitle().getFigure().getPreferredSize( prefBoundaryBounds.width, SWT.DEFAULT); this.prefInsets = new Insets(this.prefInsets); this.prefInsets.top = Math.max(s.height, this.prefInsets.top); this.prefInsets.left += 5; } // System.out.println("PrefInsets: " + this.prefInsets); // if (boundaryFigure instanceof ITitledFigure) { // ITextFigure title = ((ITitledFigure) boundaryFigure).getTitle(); // if (boundary.getBoundary().hasTitle()) { // Dimension s = title.getPreferredSize(); // prefInsets.top = Math.max(s.height, prefInsets.top); // } // } this.subBranches = boundary.getEnclosingBranches(); this.direction = direction; this.overall = boundary.getBoundary().isMasterBoundary(); } public boolean isOverall() { return overall; } public int getDirection() { return direction; } public Insets createInsets() { return new Insets(prefInsets); } public Rectangle expanded(Rectangle rect) { return rect.expand(prefInsets); } public List<IBranchPart> getSubBranches() { return subBranches; } public void setSubBranchInsets(IBranchPart subBranch, Insets ins) { branchBorders.put(subBranch, ins); } public Insets getSubBranchInsets(IBranchPart subBranch) { return branchBorders.get(subBranch); } public boolean isEmpty() { return subBranches.isEmpty(); } public IBranchPart getFirst() { return isEmpty() ? null : subBranches.get(0); } public IBranchPart getLast() { return isEmpty() ? null : subBranches.get(subBranches.size() - 1); } public boolean isFirst(SubBranchData subBranch) { return isFirst(subBranch.subBranch); } public boolean isLast(SubBranchData subBranch) { return isLast(subBranch.subBranch); } public boolean isFirst(IBranchPart subBranch) { return subBranch == getFirst(); } public boolean isLast(IBranchPart subBranch) { return subBranch == getLast(); } public boolean contains(BoundaryData another) { return subBranches.containsAll(another.subBranches); } public boolean contains(IBranchPart subBranch) { return subBranches.contains(subBranch); } public boolean contains(SubBranchData subBranch) { return contains(subBranch.subBranch); } public int getStartIndex() { if (startIndex < 0 && !isEmpty()) { startIndex = getFirst().getBranchIndex(); } return startIndex; } public int getEndIndex() { if (endIndex < 0 && !isEmpty()) { endIndex = getLast().getBranchIndex(); } return endIndex; } public int compareTo(BoundaryData that) { if (this.isOverall()) return 100; if (that.isOverall()) return -100; if (this.contains(that)) return 10; if (that.contains(this)) return -10; if (this.isEmpty()) return -100; if (that.isEmpty()) return 100; return this.getStartIndex() - that.getStartIndex(); } } public static class SubBranchData { public IBranchPart subBranch; public List<BoundaryData> boundaries = new ArrayList<BoundaryData>(); private Insets insets = null; public SubBranchData(IBranchPart subBranch, BoundaryData[] allBoundaries) { this.subBranch = subBranch; for (BoundaryData b : allBoundaries) { if (b.contains(subBranch)) { boundaries.add(b); } } } public boolean isEmpty() { return boundaries.isEmpty(); } public Insets getInsets() { if (insets == null) { insets = calcInnerInsets(null); } return insets; } public Insets calcInnerInsets(BoundaryData boundary) { Insets insets = null; BoundaryData last = null; Insets lastLevelIns = null; for (BoundaryData b : boundaries) { if (last != null && boundary != null && !boundary.contains(last)) { lastLevelIns = null; break; } if (b == boundary) break; if (last != null && b.contains(last)) { insets = Geometry.add(insets, lastLevelIns); lastLevelIns = null; } last = b; Insets ins = b.getSubBranchInsets(subBranch); lastLevelIns = Geometry.union(lastLevelIns, ins); } if (lastLevelIns != null) insets = Geometry.add(insets, lastLevelIns); return insets == null ? IFigure.NO_INSETS : insets; } } private Map<IBoundaryPart, BoundaryData> boundaries = new HashMap<IBoundaryPart, BoundaryData>(); private Map<IBranchPart, SubBranchData> subBranches = new HashMap<IBranchPart, SubBranchData>(); private BoundaryData overallBoundary = null; public BoundaryLayoutHelper() { } public void reset(IBranchPart branch, IBranchStructureExtension algorithm, Map<IFigure, Rectangle> prefBounds) { boundaries.clear(); subBranches.clear(); for (IBoundaryPart boundary : branch.getBoundaries()) { BoundaryData boundaryData = new BoundaryData(boundary, algorithm.getRangeGrowthDirection(branch, boundary), prefBounds == null ? null : prefBounds.get(boundary.getFigure())); boundaries.put(boundary, boundaryData); // if (boundaryData.isOverall() // && (overallBoundary == null || MindMapUI.BRANCH_FLOATING // .equals(branch.getBranchType()))) { // overallBoundary = boundaryData; // } if (boundaryData.isOverall() && getOverallBoundary() == null) { setOverallBoundary(boundaryData); } } if (!isEmpty()) { BoundaryData[] allBoundaries = boundaries.values() .toArray(new BoundaryData[0]); Arrays.sort(allBoundaries); for (IBranchPart subBranch : branch.getSubBranches()) { SubBranchData data = new SubBranchData(subBranch, allBoundaries); if (!data.isEmpty()) { subBranches.put(subBranch, data); } } } for (IBoundaryPart b : branch.getBoundaries()) { BoundaryData boundary = getBoundaryData(b); for (IBranchPart s : boundary.subBranches) { SubBranchData subBranch = getSubBranchData(s); boundary.setSubBranchInsets(subBranch.subBranch, createSubBranchInsets(boundary, subBranch)); } } } public BoundaryData getOverallBoundary() { return overallBoundary; } protected Insets createSubBranchInsets(BoundaryData boundary, SubBranchData subBranch) { Insets ins = boundary.createInsets(); switch (boundary.getDirection()) { case PositionConstants.EAST: if (!boundary.isFirst(subBranch)) { ins.left = 0; } if (!boundary.isLast(subBranch)) { ins.right = 0; } break; case PositionConstants.WEST: if (!boundary.isFirst(subBranch)) { ins.right = 0; } if (!boundary.isLast(subBranch)) { ins.left = 0; } break; case PositionConstants.SOUTH: if (!boundary.isFirst(subBranch)) { ins.top = 0; } if (!boundary.isLast(subBranch)) { ins.bottom = 0; } break; case PositionConstants.NORTH: if (!boundary.isFirst(subBranch)) { ins.bottom = 0; } if (!boundary.isLast(subBranch)) { ins.top = 0; } break; } return ins; } public boolean isEmpty() { return boundaries.isEmpty(); } public boolean hasSubBranch(IBranchPart subBranch) { return subBranches.containsKey(subBranch); } public SubBranchData getSubBranchData(IBranchPart subBranch) { return subBranches.get(subBranch); } public BoundaryData getBoundaryData(IBoundaryPart boundary) { return boundaries.get(boundary); } public Insets getInsets(IBranchPart subBranch) { SubBranchData data = getSubBranchData(subBranch); return data != null ? data.getInsets() : IFigure.NO_INSETS; } public Rectangle getBorderedBounds(IBranchPart subBranch, ReferencedLayoutData data) { Rectangle r = data.get(subBranch.getFigure()); if (r != null) { r = r.getExpanded(getInsets(subBranch)); } return r; } public Dimension getBorderedSize(IBranchPart subBranch) { return getBorderedSize(subBranch, -1, -1); } public Dimension getBorderedSize(IBranchPart subBranch, int wHint, int hHint) { Dimension s = subBranch.getFigure().getPreferredSize(wHint, hHint); Insets ins = getInsets(subBranch); s = new Dimension(s.width + ins.getWidth(), s.height + ins.getHeight()); return s; } public Insets getInnerInsets(SubBranchData subBranch, BoundaryData boundary) { return subBranch.calcInnerInsets(boundary); } public void setOverallBoundary(BoundaryData overallBoundary) { this.overallBoundary = overallBoundary; } }