/* ******************************************************************************
* 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 static org.xmind.ui.style.StyleUtils.getInteger;
import java.util.ArrayList;
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.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.xmind.gef.GEF;
import org.xmind.gef.draw2d.IDecoratedFigure;
import org.xmind.gef.draw2d.IReferencedFigure;
import org.xmind.gef.draw2d.IRotatableFigure;
import org.xmind.gef.draw2d.ReferencedLayoutData;
import org.xmind.gef.draw2d.decoration.IDecoration;
import org.xmind.gef.draw2d.geometry.Geometry;
import org.xmind.gef.draw2d.geometry.PrecisionDimension;
import org.xmind.gef.draw2d.geometry.PrecisionRectangle;
import org.xmind.gef.draw2d.geometry.PrecisionRotator;
import org.xmind.gef.graphicalpolicy.IStyleSelector;
import org.xmind.gef.part.IGraphicalPart;
import org.xmind.gef.part.IPart;
import org.xmind.ui.branch.BoundaryLayoutHelper.BoundaryData;
import org.xmind.ui.decorations.IBranchConnectionDecoration;
import org.xmind.ui.decorations.IBranchConnections2;
import org.xmind.ui.decorations.ISummaryDecoration;
import org.xmind.ui.decorations.ITopicDecoration;
import org.xmind.ui.internal.figures.BoundaryFigure;
import org.xmind.ui.internal.figures.BranchFigure;
import org.xmind.ui.internal.figures.TopicFigure;
import org.xmind.ui.mindmap.IBoundaryPart;
import org.xmind.ui.mindmap.IBranchPart;
import org.xmind.ui.mindmap.IBranchRangePart;
import org.xmind.ui.mindmap.IInfoPart;
import org.xmind.ui.mindmap.ILabelPart;
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.style.StyleUtils;
import org.xmind.ui.style.Styles;
import org.xmind.ui.tools.ParentSearchKey;
import org.xmind.ui.util.MindMapUtils;
public abstract class AbstractBranchStructure implements IBranchStructure,
IBranchStructureExtension, INavigableBranchStructureExtension,
IInsertableBranchStructureExtension {
protected static final String CACHE_STRUCTURE_DATA = "org.xmind.ui.branchCache.structureData"; //$NON-NLS-1$
protected static final String CACHE_BOUNDARY_LAYOUT_HELPER = "org.xmind.ui.branchCache.boundaryLayoutHelper"; //$NON-NLS-1$
protected static class LayoutInfo extends ReferencedLayoutData {
private ReferencedLayoutData delegate;
private boolean folded;
private boolean minimized;
private Rectangle minArea;
private boolean hasBoundaryTitles;
public LayoutInfo(ReferencedLayoutData delegate, boolean folded,
boolean minimized) {
this.delegate = delegate;
this.folded = folded;
this.minimized = minimized;
this.minArea = null;
this.hasBoundaryTitles = false;
}
public boolean isFolded() {
return folded;
}
public boolean isMinimized() {
return minimized;
}
public Rectangle getMinArea() {
return minArea;
}
public void setMinArea(Rectangle minArea) {
this.minArea = minArea;
}
public void putMinArea(IFigure figure) {
if (minArea == null) {
delegate.put(figure, delegate.createInitBounds());
} else {
delegate.put(figure, minArea.getCopy());
}
}
public void add(Rectangle blankArea) {
delegate.add(blankArea);
}
public void addMargins(Insets margin) {
delegate.addMargins(margin);
}
public void addMargins(int top, int left, int bottom, int right) {
delegate.addMargins(top, left, bottom, right);
}
public Rectangle createInitBounds() {
return delegate.createInitBounds();
}
public Rectangle createInitBounds(Point ref) {
return delegate.createInitBounds(ref);
}
public Rectangle get(Object figure) {
return delegate.get(figure);
}
public Rectangle getCheckedClientArea() {
return delegate.getCheckedClientArea();
}
public Rectangle getClientArea() {
return delegate.getClientArea();
}
public Point getReference() {
return delegate.getReference();
}
public void put(IFigure figure, Rectangle preferredBounds) {
delegate.put(figure, preferredBounds);
}
public void translate(int dx, int dy) {
delegate.translate(dx, dy);
}
}
public void fillLayoutData(IBranchPart branch, ReferencedLayoutData data) {
BranchFigure figure = (BranchFigure) branch.getFigure();
boolean folded = figure.isFolded();
boolean minimized = figure.isMinimized();
LayoutInfo info = new LayoutInfo(data, folded, minimized);
fillLayoutInfo(branch, info);
}
protected void fillLayoutInfo(IBranchPart branch, LayoutInfo info) {
IStyleSelector styleSelector = branch.getBranchPolicy()
.getStyleSelector(branch);
if (styleSelector != null) {
String hideCallout = styleSelector.getStyleValue(branch,
Styles.HideCallout);
if (Boolean.parseBoolean(hideCallout) && MindMapUI.BRANCH_CALLOUT
.equals(branch.getBranchType())) {
branch.getFigure().setVisible(false);
return;
}
}
// if ("org.xmind.ui.spreadsheet".equals(branch.getBranchPolicyId()) //$NON-NLS-1$
// && MindMapUI.BRANCH_CALLOUT.equals(branch.getBranchType())) {
// branch.getFigure().setVisible(false);
// return;
// }
fillTopic(branch, info);
fillPlusMinus(branch, info);
fillLabel(branch, info);
fillInformation(branch, info);
List<IBranchPart> subBranches = branch.getSubBranches();
List<IBoundaryPart> boundaries = branch.getBoundaries();
BoundaryLayoutHelper boundaryLayoutHelper = getBoundaryLayoutHelper(
branch);
// boundaryLayoutHelper.reset(branch, this, null);
info.hasBoundaryTitles = false;
ReferencedLayoutData fakeDelegate = info.delegate.copy();
ReferencedLayoutData realDelegate = info.delegate;
info.delegate = fakeDelegate;
fillSubBranches(branch, subBranches, info);
fillBoundaries(branch, boundaries, subBranches, info);
info.delegate = realDelegate;
if (info.hasBoundaryTitles) {
Map<IFigure, Rectangle> cachedBounds = new HashMap<IFigure, Rectangle>();
for (IBoundaryPart boundary : branch.getBoundaries()) {
cachedBounds.put(boundary.getFigure(),
fakeDelegate.get(boundary.getFigure()));
}
boundaryLayoutHelper.reset(branch, this, cachedBounds);
fillSubBranches(branch, subBranches, info);
fillBoundaries(branch, boundaries, subBranches, info);
} else {
for (IBranchPart subBranch : branch.getSubBranches()) {
info.delegate.put(subBranch.getFigure(),
fakeDelegate.get(subBranch.getFigure()));
}
for (IBoundaryPart boundary : branch.getBoundaries()) {
info.delegate.put(boundary.getFigure(),
fakeDelegate.get(boundary.getFigure()));
}
}
List<IBranchPart> calloutBranches = branch.getCalloutBranches();
fillCalloutBranches(branch, calloutBranches, info);
List<ISummaryPart> summaries = branch.getSummaries();
List<IBranchPart> summaryBranches = new ArrayList<IBranchPart>(
branch.getSummaryBranches());
fillSummaries(branch, summaries, summaryBranches, subBranches, info);
fillUnhandledSummaryBranches(branch, summaryBranches, info);
addExtraSpaces(branch, info);
boundaryLayoutHelper.reset(branch, this, null);
info.hasBoundaryTitles = false;
fakeDelegate = info.delegate.copy();
realDelegate = info.delegate;
info.delegate = fakeDelegate;
info.hasBoundaryTitles = false;
fillOverallBoundary(branch, boundaries, info);
info.delegate = realDelegate;
if (info.hasBoundaryTitles) {
Map<IFigure, Rectangle> cachedBounds = new HashMap<IFigure, Rectangle>();
for (IBoundaryPart boundary : branch.getBoundaries()) {
cachedBounds.put(boundary.getFigure(),
fakeDelegate.get(boundary.getFigure()));
}
boundaryLayoutHelper.setOverallBoundary(null);
boundaryLayoutHelper.reset(branch, this, cachedBounds);
fillOverallBoundary(branch, boundaries, info);
} else {
for (IBranchPart subBranch : branch.getSubBranches()) {
info.delegate.put(subBranch.getFigure(),
fakeDelegate.get(subBranch.getFigure()));
}
for (IBoundaryPart boundary : branch.getBoundaries()) {
info.delegate.put(boundary.getFigure(),
fakeDelegate.get(boundary.getFigure()));
}
}
}
protected void fillCalloutBranches(IBranchPart branch,
List<IBranchPart> calloutBranches, LayoutInfo info) {
if (calloutBranches.isEmpty())
return;
IStyleSelector styleSelector = branch.getBranchPolicy()
.getStyleSelector(branch);
if (styleSelector != null) {
String hideCallout = styleSelector.getStyleValue(branch,
Styles.HideCallout);
if (Boolean.parseBoolean(hideCallout)) {
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;
}
}
if ((info.isFolded() || info.isMinimized())
&& minimizesSubBranchesToOnePoint()) {
if (info.isFolded() && !info.isMinimized()) {
info.setMinArea(info.createInitBounds(calcSubBranchesMinPoint(
branch, calloutBranches, info)));
}
for (IBranchPart calloutBranch : calloutBranches) {
info.putMinArea(calloutBranch.getFigure());
}
} else {
doFillCalloutBranches(branch, calloutBranches, info);
for (IBranchPart calloutBranch : calloutBranches) {
IFigure calloutBranchFigure = calloutBranch.getFigure();
if (info.get(calloutBranchFigure) == null) {
info.putMinArea(calloutBranchFigure);
}
}
}
}
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) {
//changed
Rectangle parentTopicArea = info
.get(branch.getTopicPart().getFigure());
Rectangle parentBranchArea = info.getCheckedClientArea();
IReferencedFigure calloutBranchFigure = (IReferencedFigure) calloutBranch
.getFigure();
ITopicPart calloutTopicPart = calloutBranch.getTopicPart();
if (calloutTopicPart == null)
continue;
IFigure calloutFigure = calloutTopicPart.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);
}
info.put(calloutBranchFigure, bounds);
}
}
protected void fillTopic(IBranchPart branch, LayoutInfo info) {
ITopicPart topic = branch.getTopicPart();
if (topic != null) {
if (info.isMinimized()) {
info.putMinArea(topic.getFigure());
} else {
doFillTopic(branch, topic, info);
}
}
}
protected void doFillTopic(IBranchPart branch, ITopicPart topicPart,
LayoutInfo info) {
IFigure fig = topicPart.getFigure();
if (fig instanceof IReferencedFigure) {
IReferencedFigure refFig = (IReferencedFigure) fig;
Rectangle bounds = refFig.getPreferredBounds(info.getReference());
info.put(refFig, bounds);
} else {
Dimension size = fig.getPreferredSize();
Point ref = info.getReference();
Rectangle r = new Rectangle(ref.x - size.width / 2,
ref.y - size.height / 2, size.width, size.height);
info.put(fig, r);
}
}
protected void fillPlusMinus(IBranchPart branch, LayoutInfo info) {
IPlusMinusPart plusMinus = branch.getPlusMinus();
if (plusMinus != null) {
IFigure pmFigure = plusMinus.getFigure();
if (info.isMinimized()) {
info.putMinArea(pmFigure);
} else {
doFillPlusMinus(branch, plusMinus, info);
if (info.get(pmFigure) == null) {
info.putMinArea(pmFigure);
}
}
}
}
protected abstract void doFillPlusMinus(IBranchPart branch,
IPlusMinusPart plusMinus, LayoutInfo info);
protected void fillLabel(IBranchPart branch, LayoutInfo info) {
ILabelPart label = branch.getLabel();
if (label != null) {
if (info.isMinimized() || !label.getFigure().isVisible()) {
info.putMinArea(label.getFigure());
} else {
doFillLabel(branch, label, info);
}
}
}
protected void doFillLabel(IBranchPart branch, ILabelPart label,
LayoutInfo info) {
IFigure figure = label.getFigure();
ITopicPart topicPart = branch.getTopicPart();
Rectangle area;
if (topicPart != null) {
area = info.get(topicPart.getFigure());
} else {
area = info.createInitBounds();
}
if (figure instanceof IRotatableFigure) {
IRotatableFigure f = (IRotatableFigure) figure;
double angle = f.getRotationDegrees();
if (!Geometry.isSameAngleDegree(angle, 0, 0.00001)) {
Point ref = info.getReference();
PrecisionRotator r = new PrecisionRotator();
r.setOrigin(ref.x, ref.y);
r.setAngle(angle);
PrecisionRectangle rect = r.r(new PrecisionRectangle(area));
PrecisionDimension size = f.getNormalPreferredSize(-1, -1);
rect.x += (rect.width - size.width) / 2;
rect.y = rect.bottom() - 2;
rect.width = size.width;
rect.height = size.height;
r.t(rect);
info.put(figure, rect.toDraw2DRectangle());
return;
}
}
Dimension size = figure.getPreferredSize();
Rectangle r = new Rectangle(area.x + (area.width - size.width) / 2,
area.bottom() - 2, size.width, size.height);
info.put(figure, r);
}
protected void fillInformation(IBranchPart branch, LayoutInfo info) {
IInfoPart information = branch.getInfoPart();
if (information != null) {
if (info.isMinimized() || !information.getFigure().isVisible()) {
info.putMinArea(information.getFigure());
} else {
doFillInfomation(branch, information, info);
}
}
}
protected void doFillInfomation(IBranchPart branch, IInfoPart information,
LayoutInfo info) {
IFigure figure = information.getFigure();
ITopicPart topicPart = branch.getTopicPart();
Rectangle area;
if (topicPart != null) {
area = info.get(topicPart.getFigure());
} else {
area = info.createInitBounds();
}
Dimension size = figure.getPreferredSize();
TopicFigure tf = (TopicFigure) topicPart.getFigure();
ITopicDecoration decoration = tf.getDecoration();
String shapeId = decoration.getId();
int x;
if (Styles.TOPIC_SHAPE_ROUNDEDRECT.equals(shapeId)
|| Styles.TOPIC_SHAPE_RECT.equals(shapeId)
|| Styles.TOPIC_SHAPE_UNDERLINE.equals(shapeId))
x = area.x;
else
x = area.x + (area.width - size.width) / 2;
int y = area.bottom() - 1;
if (!Styles.TOPIC_SHAPE_UNDERLINE.equals(shapeId))
y -= 1;
Rectangle r = new Rectangle(x, y, size.width, size.height);
info.put(figure, r);
}
protected void fillSubBranches(IBranchPart branch,
List<IBranchPart> subBranches, LayoutInfo info) {
if (subBranches.isEmpty())
return;
if ((info.isFolded() || info.isMinimized())
&& minimizesSubBranchesToOnePoint()) {
if (info.isFolded() && !info.isMinimized()) {
info.setMinArea(info.createInitBounds(
calcSubBranchesMinPoint(branch, subBranches, info)));
}
for (IBranchPart subBranch : subBranches) {
info.putMinArea(subBranch.getFigure());
}
} else {
doFillSubBranches(branch, subBranches, info);
for (IBranchPart subBranch : subBranches) {
IFigure subBranchFigure = subBranch.getFigure();
if (info.get(subBranchFigure) == null) {
info.putMinArea(subBranchFigure);
}
}
}
}
protected abstract void doFillSubBranches(IBranchPart branch,
List<IBranchPart> subBranches, LayoutInfo info);
protected boolean minimizesSubBranchesToOnePoint() {
return true;
}
protected Point calcSubBranchesMinPoint(IBranchPart branch,
List<IBranchPart> subBranches, LayoutInfo info) {
IPlusMinusPart plusMinus = branch.getPlusMinus();
if (plusMinus != null) {
Rectangle pmBounds = info.get(plusMinus.getFigure());
if (pmBounds != null) {
return pmBounds.getCenter();
}
}
return info.getReference();
}
protected void fillBoundaries(IBranchPart branch,
List<IBoundaryPart> boundaries, List<IBranchPart> subBranches,
LayoutInfo info) {
if (boundaries.isEmpty())
return;
if (subBranches.isEmpty() || ((info.isFolded() || info.isMinimized())
&& minimizesSubBranchesToOnePoint())) {
for (IBoundaryPart b : boundaries) {
info.putMinArea(b.getFigure());
}
} else {
doFillBoundaries(branch, boundaries, info);
}
}
protected void doFillBoundaries(IBranchPart branch,
List<IBoundaryPart> boundaries, LayoutInfo info) {
for (IBoundaryPart boundary : boundaries) {
doFillBoundary(branch, boundary, info);
}
}
protected void doFillBoundary(IBranchPart branch, IBoundaryPart boundary,
LayoutInfo info) {
BoundaryLayoutHelper helper = getBoundaryLayoutHelper(branch);
BoundaryData boundaryData = helper.getBoundaryData(boundary);
if (boundary.getFigure() != null
&& ((BoundaryFigure) boundary.getFigure()).isTitleVisible()
&& boundary.getTitle() != null) {
info.hasBoundaryTitles = true;
}
if (boundaryData.isOverall()) {
if (boundaryData != helper.getOverallBoundary()) {
info.putMinArea(boundary.getFigure());
}
return;
}
Rectangle area = null;
for (IBranchPart subBranch : boundaryData.getSubBranches()) {
Insets ins = helper.getInnerInsets(
helper.getSubBranchData(subBranch), boundaryData);
Rectangle r2 = info.get(subBranch.getFigure());
area = Geometry.union(area, r2.getExpanded(ins));
}
if (area == null) {
info.putMinArea(boundary.getFigure());
} else {
area = boundaryData.expanded(area);
info.put(boundary.getFigure(), area);
}
}
protected void fillSummaries(IBranchPart branch,
List<ISummaryPart> summaries, List<IBranchPart> summaryBranches,
List<IBranchPart> subBranches, LayoutInfo info) {
if (!summaries.isEmpty()) {
if (subBranches.isEmpty()
|| ((info.isFolded() || info.isMinimized())
&& minimizesSubBranchesToOnePoint())) {
for (ISummaryPart s : summaries) {
info.putMinArea(s.getFigure());
}
} else {
doFillSummaries(branch, summaries, summaryBranches, info);
}
}
}
private void doFillSummaries(IBranchPart branch,
List<ISummaryPart> summaries, List<IBranchPart> summaryBranches,
LayoutInfo info) {
for (ISummaryPart summary : summaries) {
doFillSummary(branch, summary, summaryBranches, info);
}
}
private void doFillSummary(IBranchPart branch, ISummaryPart summary,
List<IBranchPart> summaryBranches, LayoutInfo info) {
int direction = getSummaryDirection(branch, summary);
Rectangle area = getSummaryArea(branch, summary, direction, info);
if (area != null) {
info.put(summary.getFigure(), area);
} else {
info.putMinArea(summary.getFigure());
}
IBranchPart conclusionBranch = getConclusionBranch(branch, summary,
summaryBranches);
if (conclusionBranch != null) {
if (area == null) {
info.putMinArea(conclusionBranch.getFigure());
} else {
Insets ins = getConclusionReferenceDescription(branch, summary,
conclusionBranch);
int x, y;
switch (direction) {
case PositionConstants.NORTH:
x = area.x + area.width / 2;
y = area.y - ins.bottom;
break;
case PositionConstants.SOUTH:
x = area.x + area.width / 2;
y = area.bottom() + ins.top;
break;
case PositionConstants.WEST:
x = area.x - ins.right;
y = area.y + area.height / 2;
break;
default:
x = area.right() + ins.left;
y = area.y + area.height / 2;
}
info.put(conclusionBranch.getFigure(),
Geometry.getExpanded(x, y, ins));
}
}
}
private IBranchPart getConclusionBranch(IBranchPart branch,
ISummaryPart summary, List<IBranchPart> summaryBranches) {
IGraphicalPart part = summary.getNode();
if (part instanceof ITopicPart) {
IBranchPart conclusionBranch = ((ITopicPart) part).getOwnerBranch();
if (conclusionBranch != null
&& summaryBranches.contains(conclusionBranch)) {
summaryBranches.remove(conclusionBranch);
return conclusionBranch;
}
}
return null;
}
private Insets getConclusionReferenceDescription(IBranchPart branch,
ISummaryPart summary, IGraphicalPart conclusion) {
IFigure fig = conclusion.getFigure();
if (fig instanceof IReferencedFigure)
return ((IReferencedFigure) fig).getReferenceDescription();
Dimension size = fig.getPreferredSize();
int w = size.width / 2;
int h = size.height / 2;
return new Insets(h, w, size.height - h, size.width - w);
}
protected Rectangle getSummaryArea(IBranchPart branch, ISummaryPart summary,
int direction, ReferencedLayoutData data) {
Rectangle r = null;
for (IBranchPart subBranch : summary.getEnclosingBranches()) {
r = Geometry.union(r, data.get(subBranch.getFigure()));
}
if (r == null)
return null;
Rectangle area = data.createInitBounds();
int width = getPreferredSummaryWidth(summary);
switch (direction) {
case PositionConstants.NORTH:
area.x = r.x;
area.width = r.width;
area.y = r.y - width;
area.height = width;
break;
case PositionConstants.SOUTH:
area.x = r.x;
area.width = r.width;
area.y = r.bottom();
area.height = width;
break;
case PositionConstants.WEST:
area.x = r.x - width;
area.width = width;
area.y = r.y;
area.height = r.height;
break;
default:
area.x = r.right();
area.width = width;
area.y = r.y;
area.height = r.height;
}
IStyleSelector ss = StyleUtils.getStyleSelector(summary);
String shape = StyleUtils.getString(summary, ss, Styles.ShapeClass,
null);
int lineWidth = StyleUtils.getInteger(summary, ss, Styles.LineWidth,
shape, 1);
return area.expand(lineWidth, lineWidth);
}
private int getPreferredSummaryWidth(ISummaryPart summary) {
IFigure figure = summary.getFigure();
if (figure instanceof IDecoratedFigure) {
IDecoration decoration = ((IDecoratedFigure) figure)
.getDecoration();
if (decoration instanceof ISummaryDecoration) {
return ((ISummaryDecoration) decoration)
.getPreferredWidth(figure);
}
}
return Styles.DEFAULT_SUMMARY_WIDTH
+ Styles.DEFAULT_SUMMARY_SPACING * 2;
}
protected void fillUnhandledSummaryBranches(IBranchPart branch,
List<IBranchPart> summaryBranches, LayoutInfo info) {
if (!summaryBranches.isEmpty()) {
for (IBranchPart summaryBranch : summaryBranches) {
info.putMinArea(summaryBranch.getFigure());
}
}
}
protected void addExtraSpaces(IBranchPart branch,
ReferencedLayoutData data) {
// may be subclassed
}
public int getSummaryDirection(IBranchPart branch, ISummaryPart summary) {
return PositionConstants.EAST;
}
protected void fillOverallBoundary(IBranchPart branch,
List<IBoundaryPart> boundaries, LayoutInfo info) {
if (boundaries.isEmpty())
return;
IBoundaryPart boundary = branch.getBoundaries()
.get(boundaries.size() - 1);
if (boundary.getFigure() != null
&& ((BoundaryFigure) boundary.getFigure()).isTitleVisible()
&& boundary.getTitle() != null) {
info.hasBoundaryTitles = true;
}
BoundaryLayoutHelper helper = getBoundaryLayoutHelper(branch);
BoundaryData overallBoundary = helper.getOverallBoundary();
if (overallBoundary == null)
return;
if (info.isMinimized()) {
info.putMinArea(overallBoundary.boundaryFigure);
} else {
Rectangle area = info.getCheckedClientArea();
area = overallBoundary.expanded(area.getCopy());
info.put(overallBoundary.boundaryFigure, area);
}
}
public int getRangeGrowthDirection(IBranchPart branch,
IBranchRangePart range) {
return PositionConstants.SOUTH;
}
public void invalidate(IGraphicalPart part) {
if (part instanceof IBranchPart) {
invalidateBranch((IBranchPart) part);
}
}
protected void invalidateBranch(IBranchPart branch) {
MindMapUtils.flushCache(branch, CACHE_STRUCTURE_DATA);
MindMapUtils.flushCache(branch, CACHE_BOUNDARY_LAYOUT_HELPER);
ITopicPart topic = branch.getTopicPart();
if (topic != null) {
IFigure topicFigure = topic.getFigure();
if (topicFigure != null) {
topicFigure.invalidate();
}
}
}
protected BoundaryLayoutHelper getBoundaryLayoutHelper(IBranchPart branch) {
BoundaryLayoutHelper helper = (BoundaryLayoutHelper) MindMapUtils
.getCache(branch, CACHE_BOUNDARY_LAYOUT_HELPER);
if (helper == null) {
helper = new BoundaryLayoutHelper();
helper.reset(branch, this, null);
MindMapUtils.setCache(branch, CACHE_BOUNDARY_LAYOUT_HELPER, helper);
}
return helper;
}
protected Dimension getBorderedSize(IBranchPart branch,
IBranchPart subBranch) {
return getBoundaryLayoutHelper(branch).getBorderedSize(subBranch);
}
protected int getMinorSpacing(IBranchPart branch) {
return getInteger(branch,
branch.getBranchPolicy().getStyleSelector(branch),
Styles.MinorSpacing, 5);
}
protected int getMajorSpacing(IBranchPart branch) {
return StyleUtils.getMajorSpacing(branch, 5);
}
protected Object getStructureData(IBranchPart branch) {
Object data = MindMapUtils.getCache(branch, CACHE_STRUCTURE_DATA);
if (!isValidStructureData(branch, data)) {
data = createStructureData(branch);
if (data != null) {
MindMapUtils.setCache(branch, CACHE_STRUCTURE_DATA, data);
}
}
return data;
}
protected Object createStructureData(IBranchPart branch) {
return null;
}
protected boolean isValidStructureData(IBranchPart branch, Object data) {
return data != null;
}
// public void calculateSubBranchInsets(BoundaryData boundary,
// SubBranchData subBranch, Insets insets) {
// }
/*
* Subclass may extend this method.
*
* @seeorg.xmind.ui.mindmap.graphicalpolicies.IBranchStructure#
* calcSourceOrientation(org.xmind.ui.parts.IBranchPart)
*/
public int getSourceOrientation(IBranchPart branch) {
return PositionConstants.NONE;
}
/*
* Subclass may extend this method.
*
* @seeorg.xmind.ui.mindmap.graphicalpolicies.IBranchStructure#
* calcChildTargetOrientation(org.xmind.ui.parts.IBranchPart,
* org.xmind.ui.parts.IBranchPart)
*/
public int getChildTargetOrientation(IBranchPart branch,
IBranchPart subBranch) {
return PositionConstants.NONE;
}
// /*
// * Subclass may extend this method.
// *
// * @see org.xmind.ui.graphicalpolicies.IBranchStructure#calcChildTargetOrientation(org.xmind.ui.parts.IBranchPart,
// * org.xmind.gef.draw2d.IReferencedFigure)
// */
// public int calcChildTargetOrientation(IBranchPart branch,
// IReferencedFigure childFigure) {
// return PositionConstants.NONE;
// }
/*
* Subclass may extend this method.
*
* @seeorg.xmind.ui.mindmap.graphicalpolicies.IBranchStructure#
* calcChildIndex(org.xmind.ui.tools.ParentSearchKey)
*/
public int calcChildIndex(IBranchPart branch, ParentSearchKey key) {
return calcInsIndex(branch, key, false);
}
/*
* Subclass may extend this method.
*
* @seeorg.xmind.ui.mindmap.graphicalpolicies.IBranchStructure#
* calcChildDistance(org.xmind.ui.parts.IBranchPart,
* org.xmind.ui.tools.ParentSearchKey)
*/
public int calcChildDistance(IBranchPart branch, ParentSearchKey key) {
return -1;
}
public IInsertion calcInsertion(IBranchPart branch, ParentSearchKey key) {
return null;
}
public IPart calcNavigation(IBranchPart branch, String navReqType) {
return null;
}
public IPart calcChildNavigation(IBranchPart branch,
IBranchPart sourceChild, String navReqType, boolean sequential) {
if (GEF.REQ_NAV_BEGINNING.equals(navReqType)) {
return getSubTopicPart(branch, 0);
} else if (GEF.REQ_NAV_END.equals(navReqType)) {
return getSubTopicPart(branch, branch.getSubBranches().size() - 1);
}
return null;
}
public void calcSequentialNavigation(IBranchPart branch,
IBranchPart startChild, IBranchPart endChild,
List<IBranchPart> results) {
addSubBranches(branch, startChild.getBranchIndex(),
endChild.getBranchIndex(), results);
}
public void calcTraversableBranches(IBranchPart branch,
IBranchPart sourceChild, List<IBranchPart> results) {
addSubBranch(branch, sourceChild.getBranchIndex() + 1, results);
results.add(branch);
addSubBranch(branch, sourceChild.getBranchIndex() - 1, results);
}
public void calcTraversableChildren(IBranchPart branch,
List<IBranchPart> results) {
addSubBranches(branch, 0, branch.getSubBranches().size() - 1, results);
}
protected void addSubBranches(IBranchPart branch, IBranchPart fromChild,
IBranchPart toChild, List<IBranchPart> results) {
addSubBranches(branch, branch.getSubBranches().indexOf(fromChild),
branch.getSubBranches().indexOf(toChild), results);
}
protected void addSubBranches(IBranchPart branch, int fromIndex,
int toIndex, List<IBranchPart> results) {
boolean decreasing = toIndex < fromIndex;
for (int i = fromIndex; decreasing ? i >= toIndex : i <= toIndex;) {
addSubBranch(branch, i, results);
if (decreasing) {
i--;
} else {
i++;
}
}
}
protected void addSubBranch(IBranchPart branch, int index,
List<IBranchPart> results) {
if (index < 0 || index >= branch.getSubBranches().size())
return;
results.add(branch.getSubBranches().get(index));
}
protected IBranchPart getSubBranch(IBranchPart branch, int index) {
if (index >= 0 && index < branch.getSubBranches().size()) {
return branch.getSubBranches().get(index);
}
return null;
}
protected ITopicPart getSubTopicPart(IBranchPart branch, int index) {
IBranchPart subBranch = getSubBranch(branch, index);
if (subBranch != null)
return subBranch.getTopicPart();
return null;
}
protected IInsertion getCurrentInsertion(IBranchPart branch) {
return (IInsertion) MindMapUtils.getCache(branch,
IInsertion.CACHE_INSERTION);
}
public int getQuickMoveOffset(IBranchPart branch, IBranchPart child,
int direction) {
return 0;
}
protected Point getChildRef(IBranchPart branch, Point branchRef,
ParentSearchKey key) {
return key.getCursorPos();
}
public Point calcInsertionPosition(IBranchPart branch, IBranchPart child,
ParentSearchKey key) {
if (child == null)
return calcInsertPosition(branch, child, key);
if (getOldIndex(branch, child) == -1) {
return calcInsertPosition(branch, child, key);
} else {
return calcMovePosition(branch, child, key);
}
}
public boolean isBranchMoved(IBranchPart branch, IBranchPart child,
ParentSearchKey key) {
int index = calcInsIndex(branch, key, true);
List<Integer> disables = getDisableBranches(branch);
return !(disables != null
&& (disables.contains(index) || disables.contains(index - 1)));
}
protected int calcInsIndex(IBranchPart branch, ParentSearchKey key,
boolean withDisabled) {
return -1;
}
protected Point calcInsertPosition(IBranchPart branch, IBranchPart child,
ParentSearchKey key) {
List<IBranchPart> subBranches = branch.getSubBranches();
if (subBranches.isEmpty())
return calcFirstChildPosition(branch, key);
int index = calcInsIndex(branch, key, true);
int minorSpacing = getMinorSpacing(branch);
Dimension insSize = key.getFigure().getSize();
Dimension inventSize = key.getInvent().getSize();
List<IBoundaryPart> boundaries = branch.getBoundaries();
if (index == 0) {
IBranchPart sub = subBranches.get(0);
Rectangle bounds = sub.getFigure().getBounds();
if (!boundaries.isEmpty()) {
for (IBoundaryPart boundary : boundaries) {
Rectangle bBounds = boundary.getFigure().getBounds();
List<IBranchPart> enclosingBranches = boundary
.getEnclosingBranches();
if ((!enclosingBranches.isEmpty())
&& sub.equals(enclosingBranches.get(0)))
bounds = bBounds.contains(bounds) ? bBounds : bounds;
}
}
int x;
if (key.getCursorPos().x > 0)
x = bounds.x + inventSize.width / 2;
else
x = bounds.right() - inventSize.width / 2;
int y = bounds.y - minorSpacing - insSize.height / 2;
return new Point(x, y);
}
if (index == subBranches.size()) {
IBranchPart sub = subBranches.get(subBranches.size() - 1);
Rectangle bounds = sub.getFigure().getBounds();
if (!boundaries.isEmpty()) {
for (IBoundaryPart boundary : boundaries) {
Rectangle bBounds = boundary.getFigure().getBounds();
List<IBranchPart> enclosingBranches = boundary
.getEnclosingBranches();
if ((!enclosingBranches.isEmpty())
&& sub.equals(enclosingBranches
.get(enclosingBranches.size() - 1)))
bounds = bBounds.contains(bounds) ? bBounds : bounds;
}
}
int x;
if (key.getCursorPos().x > 0)
x = bounds.x + inventSize.width / 2;
else
x = bounds.right() - inventSize.width / 2;
int y = bounds.bottom() + minorSpacing + insSize.height / 2;
return new Point(x, y);
}
return calcInventPosition(subBranches.get(index - 1),
subBranches.get(index), key, key.getCursorPos().x > 0);
}
protected Point calcMovePosition(IBranchPart branch, IBranchPart child,
ParentSearchKey key) {
List<IBranchPart> subBranches = branch.getSubBranches();
List<Integer> disables = getDisableBranches(branch);
int index = calcInsIndex(branch, key, true);
int oldIndex = getOldIndex(branch, child);
if (disables != null) {
if (disables.contains(index - 1)) {
index--;
oldIndex = index;
} else if (disables.contains(index)) {
oldIndex = index;
}
}
Dimension inventSize = key.getInvent().getSize();
if (index == oldIndex) {
IBranchPart sub = subBranches.get(index);
int delta = getTopicSize(sub).width / 2 - inventSize.width / 2;
return getFigureLocation(sub.getFigure()).getTranslated(
key.getCursorPos().x < 0 ? delta : -delta, 0);
}
return calcInsertPosition(branch, child, key);
}
protected Point calcFirstChildPosition(IBranchPart branch,
ParentSearchKey key) {
int x = getTopicSize(branch).width / 2 + getMajorSpacing(branch)
+ key.getInvent().getSize().width / 2;
return getFigureLocation(branch.getFigure())
.getTranslated(key.getCursorPos().x < 0 ? -x : x, 0);
}
protected Point calcInventPosition(IBranchPart orientation,
IBranchPart assist, ParentSearchKey key, boolean isRightOrUp) {
int minorSpacing = getMinorSpacing(orientation.getParentBranch());
Dimension insSize = key.getFigure().getSize();
Dimension inventSize = key.getInvent().getSize();
Rectangle oriBounds = orientation.getFigure().getBounds();
Rectangle assBounds = assist.getFigure().getBounds();
Rectangle uBounds = oriBounds;
Rectangle dBounds = assBounds;
List<IBoundaryPart> boundaries = orientation.getParentBranch()
.getBoundaries();
if (!boundaries.isEmpty()) {
for (IBoundaryPart boundary : boundaries) {
List<IBranchPart> enclosingBranches = boundary
.getEnclosingBranches();
Rectangle bBounds = boundary.getFigure().getBounds();
if ((!enclosingBranches.isEmpty()) && orientation.equals(
enclosingBranches.get(enclosingBranches.size() - 1)))
uBounds = bBounds.contains(uBounds) ? bBounds : uBounds;
if ((!enclosingBranches.isEmpty())
&& assist.equals(enclosingBranches.get(0)))
dBounds = bBounds.contains(dBounds) ? bBounds : dBounds;
}
}
boolean isBefourBounds;
Rectangle bounds;
if (uBounds.equals(oriBounds)) {
bounds = uBounds;
isBefourBounds = false;
} else if (dBounds.equals(assBounds)) {
bounds = dBounds;
isBefourBounds = true;
} else {
if (isRightOrUp) {
if (uBounds.x > dBounds.x) {
bounds = uBounds;
isBefourBounds = false;
} else {
bounds = dBounds;
isBefourBounds = true;
}
} else {
if (uBounds.right() < dBounds.right()) {
bounds = uBounds;
isBefourBounds = false;
} else {
bounds = dBounds;
isBefourBounds = true;
}
}
}
int x;
if (isRightOrUp)
x = bounds.x + inventSize.width / 2;
else
x = bounds.right() - inventSize.width / 2;
int y;
if (isBefourBounds)
y = bounds.y - minorSpacing - insSize.height / 2;
else
y = bounds.bottom() + minorSpacing + insSize.height / 2;
return new Point(x, y);
}
protected int getOldIndex(IBranchPart branch, IBranchPart child) {
List<IBranchPart> subBranches = branch.getSubBranches();
if (branch.equals(child.getParentBranch()))
return child.getBranchIndex();
for (IBranchPart sub : subBranches) {
if (!sub.getFigure().isEnabled())
return sub.getBranchIndex();
}
return -1;
}
protected Point getFigureLocation(IFigure figure) {
if (figure instanceof IReferencedFigure)
return ((IReferencedFigure) figure).getReference();
else
return figure.getBounds().getLocation();
}
protected Dimension getTopicSize(IBranchPart branch) {
if (branch == null)
return new Dimension();
return branch.getTopicPart().getFigure().getSize();
}
protected List<Integer> getDisableBranches(IBranchPart branch) {
List<IBranchPart> subBranches = branch.getSubBranches();
List<Integer> disables = null;
for (int i = 0; i < subBranches.size(); i++) {
IBranchPart sub = subBranches.get(i);
if (!sub.getFigure().isEnabled()) {
if (disables == null)
disables = new ArrayList<Integer>();
disables.add(i);
}
}
return disables;
}
}