/* ******************************************************************************
* 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.internal.mindmap;
import static org.xmind.ui.util.MindMapUtils.findBranch;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FigureListener;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.graphics.Cursor;
import org.xmind.core.Core;
import org.xmind.core.ISheet;
import org.xmind.core.ISummary;
import org.xmind.core.ITopic;
import org.xmind.core.event.CoreEvent;
import org.xmind.core.event.ICoreEventRegister;
import org.xmind.gef.GEF;
import org.xmind.gef.IViewer;
import org.xmind.gef.draw2d.AbstractAnchor;
import org.xmind.gef.draw2d.IAnchor;
import org.xmind.gef.draw2d.geometry.Geometry;
import org.xmind.gef.draw2d.geometry.PrecisionPoint;
import org.xmind.gef.part.IPart;
import org.xmind.gef.part.IPartListener;
import org.xmind.gef.part.IRequestHandler;
import org.xmind.gef.part.PartEvent;
import org.xmind.gef.policy.NullEditPolicy;
import org.xmind.gef.service.IBendPointsFeedback;
import org.xmind.gef.service.IFeedback;
import org.xmind.gef.util.GEFUtils;
import org.xmind.ui.internal.decorators.SummaryDecorator;
import org.xmind.ui.internal.figures.SummaryFigure;
import org.xmind.ui.internal.graphicalpolicies.SummaryGraphicalPolicy;
import org.xmind.ui.mindmap.IBranchPart;
import org.xmind.ui.mindmap.INodePart;
import org.xmind.ui.mindmap.IRangeListener;
import org.xmind.ui.mindmap.ISelectionFeedbackHelper;
import org.xmind.ui.mindmap.ISummaryPart;
import org.xmind.ui.mindmap.MindMapUI;
import org.xmind.ui.mindmap.RangeEvent;
import org.xmind.ui.util.MindMapUtils;
public class SummaryPart extends MindMapPartBase implements FigureListener,
ISummaryPart {
private static class SummarySourceAnchor extends AbstractAnchor {
private SummarySourceAnchor(IFigure owner) {
super(owner);
}
public PrecisionPoint getLocation(double x, double y, double expansion) {
return Geometry.getChopBoxLocation(x, y, getOwner().getBounds(),
expansion);
}
@Override
public PrecisionPoint getLocation(int orientation, double expansion) {
IFigure owner = getOwner();
Rectangle r = owner.getBounds();
if (orientation == PositionConstants.NORTH) {
return new PrecisionPoint(r.getTopLeft()).translate(0,
-expansion);
} else if (orientation == PositionConstants.SOUTH) {
return new PrecisionPoint(r.getBottomRight()).translate(0,
expansion);
} else if (orientation == PositionConstants.WEST) {
return new PrecisionPoint(r.getBottomLeft()).translate(
-expansion, 0);
}
return new PrecisionPoint(r.getTopRight()).translate(expansion, 0);
}
}
private static class SummaryTargetAnchor extends AbstractAnchor {
private SummaryTargetAnchor(IFigure owner) {
super(owner);
}
public PrecisionPoint getLocation(double x, double y, double expansion) {
return Geometry.getChopBoxLocation(x, y, getOwner().getBounds(),
expansion);
}
@Override
public PrecisionPoint getLocation(int orientation, double expansion) {
Rectangle r = getOwner().getBounds();
if (orientation == PositionConstants.NORTH) {
return new PrecisionPoint(r.getTopRight()).translate(0,
-expansion);
} else if (orientation == PositionConstants.SOUTH) {
return new PrecisionPoint(r.getBottomLeft()).translate(0,
expansion);
} else if (orientation == PositionConstants.WEST) {
return new PrecisionPoint(r.getTopLeft()).translate(-expansion,
0);
}
return new PrecisionPoint(r.getBottomRight()).translate(expansion,
0);
}
}
private IAnchor sourceAnchor = null;
private IAnchor targetAnchor = null;
private IFigure enclosure = null;
private INodePart node = null;
private List<IBranchPart> enclosingBranches = null;
private List<IRangeListener> rangeListeners = null;
private IPartListener parentListener = new IPartListener() {
public void childRemoving(PartEvent event) {
if (event.child instanceof IBranchPart) {
IBranchPart branch = (IBranchPart) event.child;
refreshEnclosure();
if (branch.getTopicPart() == getNode())
setNode(null);
}
}
public void childAdded(PartEvent event) {
if (event.child instanceof IBranchPart) {
IBranchPart branch = (IBranchPart) event.child;
String topicType = branch.getTopic().getType();
if (ITopic.ATTACHED.equals(topicType)) {
refreshEnclosure();
} else if (ITopic.SUMMARY.equals(topicType)) {
if (MindMapUtils.equals(getSummary().getTopicId(), branch
.getTopic().getId())) {
setNode((INodePart) branch.getTopicPart());
}
}
}
}
};
// private IStatusListener nodeStatusListener = new IStatusListener() {
// public void statusChanged(StatusEvent event) {
// if (event.key == GEF.PART_SELECTED
// || event.key == GEF.PART_PRESELECTED) {
// getStatus().setStatus(event.key, event.newValue);
// }
// }
// };
private ISelectionChangedListener viewerListener = new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
syncStatus(isNodeSingleSelected());
}
private boolean isNodeSingleSelected() {
IViewer viewer = getSite().getViewer();
if (viewer == null)
return false;
ISelection selection = viewer.getSelection();
if (!selection.isEmpty()
&& !(selection instanceof IStructuredSelection))
return false;
IStructuredSelection ss = (IStructuredSelection) selection;
if (ss.size() == 1
&& getSummaryTopic().equals(ss.getFirstElement()))
return true;
if (!ss.isEmpty()
&& !(ss.size() == 1 && ss.getFirstElement() instanceof ISheet))
return false;
return getSummaryTopic().equals(viewer.getPreselected());
}
};
private boolean cursorOverHandle = false;
public SummaryPart() {
setDecorator(SummaryDecorator.getInstance());
setGraphicalPolicy(SummaryGraphicalPolicy.getInstance());
}
protected IFigure createFigure() {
return new SummaryFigure();
}
/*
* (non-Javadoc)
*
* @see org.xmind.ui.internal.parts.ISummaryPart#getSummary()
*/
public ISummary getSummary() {
if (getModel() instanceof SummaryViewerModel)
return ((SummaryViewerModel) getModel()).getSummary();
return null;
// return (ISummary) getRealModel();
}
public ITopic getSummaryTopic() {
return (ITopic) getRealModel();
}
/*
* (non-Javadoc)
*
* @see org.xmind.ui.internal.parts.ISummaryPart#getOwnedBranch()
*/
public IBranchPart getOwnedBranch() {
if (getParent() instanceof IBranchPart)
return (IBranchPart) getParent();
return null;
}
public void setParent(IPart parent) {
if (getParent() instanceof BranchPart) {
getParent().removePartListener(parentListener);
((BranchPart) getParent()).removeSummary(this);
}
super.setParent(parent);
if (getParent() instanceof BranchPart) {
((BranchPart) getParent()).addSummary(this);
getParent().addPartListener(parentListener);
}
}
@SuppressWarnings("unchecked")
public Object getAdapter(Class adapter) {
if (adapter.isAssignableFrom(ISummary.class))
return getSummary();
if (adapter.isAssignableFrom(ITopic.class))
return getSummaryTopic();
return super.getAdapter(adapter);
}
protected void declareEditPolicies(IRequestHandler reqHandler) {
super.declareEditPolicies(reqHandler);
reqHandler.installEditPolicy(GEF.ROLE_SELECTABLE,
NullEditPolicy.getInstance());
reqHandler.installEditPolicy(GEF.ROLE_DELETABLE,
MindMapUI.POLICY_DELETABLE);
reqHandler.installEditPolicy(GEF.ROLE_EDITABLE,
MindMapUI.POLICY_EDITABLE);
reqHandler.installEditPolicy(GEF.ROLE_MODIFIABLE,
MindMapUI.POLICY_MODIFIABLE);
// reqHandler.installEditPolicy(GEF.ROLE_CREATABLE,
// MindMapUI.POLICY_SUMMARY_CREATABLE);
reqHandler.installEditPolicy(GEF.ROLE_MOVABLE,
NullEditPolicy.getInstance());
}
protected void registerCoreEvents(Object source, ICoreEventRegister register) {
super.registerCoreEvents(source, register);
ISummary summary = getSummary();
register.setNextSourceFrom(summary);
register.register(Core.StartIndex);
register.register(Core.EndIndex);
register.register(Core.TopicRefId);
register.register(Core.Style);
}
public void handleCoreEvent(CoreEvent event) {
String type = event.getType();
if (Core.StartIndex.equals(type) || Core.EndIndex.equals(type)) {
refresh();
fireRangeChanged();
// getOwnedBranch().getFigure().invalidate();
// getOwnedBranch().treeUpdate(true);
} else if (Core.TopicRefId.equals(type)) {
setNode(findConclusionNode());
refresh();
} else if (Core.Style.equals(type)) {
update();
} else {
super.handleCoreEvent(event);
}
}
/*
* (non-Javadoc)
*
* @see org.xmind.ui.internal.parts.ISummaryPart#getEnclosingBranches()
*/
public List<IBranchPart> getEnclosingBranches() {
if (enclosingBranches == null) {
enclosingBranches = fillEnclosingBranches(new ArrayList<IBranchPart>());
for (IBranchPart subBranch : enclosingBranches) {
subBranch.getFigure().addFigureListener(this);
}
}
return enclosingBranches;
}
private List<IBranchPart> fillEnclosingBranches(List<IBranchPart> list) {
ISummary s = getSummary();
List<ITopic> subtopics = s.getEnclosingTopics();
if (!subtopics.isEmpty()) {
IViewer viewer = getSite().getViewer();
for (ITopic t : subtopics) {
IBranchPart branch = findBranch(viewer.findPart(t));
if (branch != null)
list.add(branch);
}
}
return list;
}
private void flushEnclosingBranches() {
if (enclosingBranches != null) {
for (IBranchPart subBranch : enclosingBranches) {
subBranch.getFigure().removeFigureListener(this);
}
enclosingBranches = null;
}
}
/*
* (non-Javadoc)
*
* @seeorg.xmind.ui.mindmap.IBranchRangePart#encloses(org.xmind.ui.mindmap.
* IBranchPart)
*/
public boolean encloses(IBranchPart subbranch) {
return getSummary().encloses(subbranch.getTopic());
}
public void refresh() {
refreshEnclosure();
super.refresh();
}
@Override
public void addNotify() {
setNode(findConclusionNode());
super.addNotify();
for (IBranchPart enclosingBranch : getEnclosingBranches()) {
enclosingBranch.getFigure().revalidate();
enclosingBranch.update();
}
}
@Override
public void removeNotify() {
super.removeNotify();
setNode(null);
}
protected void onActivated() {
super.onActivated();
IViewer viewer = getSite().getViewer();
if (viewer != null) {
viewer.addSelectionChangedListener(viewerListener);
viewer.addPreSelectionChangedListener(viewerListener);
}
}
protected void onDeactivated() {
IViewer viewer = getSite().getViewer();
if (viewer != null) {
viewer.removeSelectionChangedListener(viewerListener);
viewer.removePreSelectionChangedListener(viewerListener);
}
super.onDeactivated();
}
private INodePart findConclusionNode() {
ISummary s = getSummary();
ITopic topic = s.getTopic();
if (topic != null) {
IPart topicPart = getSite().getViewer().findPart(topic);
if (topicPart instanceof INodePart) {
return (INodePart) topicPart;
}
}
return null;
}
private void refreshEnclosure() {
flushEnclosingBranches();
updateEnclosure();
}
public void figureMoved(IFigure source) {
updateEnclosure();
update();
getFigure().revalidate();
}
protected IFigure getEnclosure() {
if (enclosure == null) {
enclosure = new Figure();
}
return enclosure;
}
private void updateEnclosure() {
Rectangle r = null;
for (IBranchPart enclosingBranch : getEnclosingBranches()) {
r = Geometry.union(r, enclosingBranch.getFigure().getBounds());
}
if (r == null)
r = new Rectangle();
getEnclosure().setBounds(r);
}
/*
* (non-Javadoc)
*
* @see org.xmind.ui.internal.parts.ISummaryPart#getSourceAnchor()
*/
public IAnchor getSourceAnchor() {
if (sourceAnchor == null) {
IFigure enclosure2 = getEnclosure();
sourceAnchor = new SummarySourceAnchor(enclosure2);
}
return sourceAnchor;
}
/*
* (non-Javadoc)
*
* @see org.xmind.ui.internal.parts.ISummaryPart#getTargetAnchor()
*/
public IAnchor getTargetAnchor() {
if (targetAnchor == null) {
IFigure enclosure2 = getEnclosure();
targetAnchor = new SummaryTargetAnchor(enclosure2);
}
return targetAnchor;
}
/*
* (non-Javadoc)
*
* @see org.xmind.ui.internal.parts.ISummaryPart#getConclusionAnchor()
*/
public IAnchor getNodeAnchor() {
if (node != null) {
return node.getTargetAnchor(this);
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.xmind.ui.internal.parts.ISummaryPart#getConclusionPart()
*/
public INodePart getNode() {
return node;
}
/*
* (non-Javadoc)
*
* @see org.xmind.ui.internal.parts.ISummaryPart#setConclusionPart(org
* .xmind.gef.part.IGraphicalPart)
*/
public void setNode(INodePart part) {
if (part == this.node)
return;
// if (this.conclusionNode != null) {
// unhookNode(this.conclusionNode);
// }
this.node = part;
// if (this.conclusionNode != null) {
// hookNode(this.conclusionNode);
// }
update();
syncStatus(false);
if (part != null) {
IBranchPart branch = MindMapUtils.findBranch(part);
if (branch != null) {
branch.treeUpdate(false);
} else {
part.update();
}
}
}
// private void hookNode(INodePart node) {
// node.getStatus().addStatusListener(nodeStatusListener);
// getStatus().setStatus(GEF.PART_SELECTED,
// node.getStatus().isStatus(GEF.PART_SELECTED));
// getStatus().setStatus(GEF.PART_PRESELECTED,
// node.getStatus().isStatus(GEF.PART_PRESELECTED));
// }
//
// private void unhookNode(INodePart node) {
// getStatus().dePreSelect();
// getStatus().deSelect();
// node.getStatus().removeStatusListener(nodeStatusListener);
// }
private void syncStatus(boolean nodeSingleSelected) {
boolean oldPreselected = getStatus().isPreSelected();
boolean preSelected = getNode() != null
&& (getNode().getStatus().isSelected() ? cursorOverHandle
: nodeSingleSelected
&& getNode().getStatus().isPreSelected());
// || (nodeSingleSelected && getNode() != null && getNode()
// .getStatus().isPreSelected());
boolean selected = nodeSingleSelected && getNode() != null
&& getNode().getStatus().isSelected();
getStatus().setStatus(GEF.PART_PRESELECTED, preSelected);
getStatus().setStatus(GEF.PART_SELECTED, selected);
if (oldPreselected == getStatus().isPreSelected()) {
updateFeedback();
}
}
@Override
protected ISelectionFeedbackHelper createSelectionFeedbackHelper() {
return new SelectionFeedbackHelper();
}
protected IFeedback createFeedback() {
return new SummaryFeedback(this);
}
public boolean containsPoint(Point position) {
return super.containsPoint(position)
|| (getFeedback() != null && getFeedback().containsPoint(
position));
}
public IPart findAt(Point position) {
IPart ret = super.findAt(position);
if (ret == this) {
if (getFeedback() == null || !getFeedback().containsPoint(position)) {
cursorOverHandle = false;
return getNode();
} else {
cursorOverHandle = true;
}
} else {
cursorOverHandle = false;
}
return ret;
}
// protected void updateFeedback() {
// if (getFeedbackPart() instanceof SummaryFeedback) {
// ((SummaryFeedback) getFeedbackPart())
// .setGrowthDirection(calcGrowthDirection());
// }
// super.updateFeedback();
// }
//
// private int calcGrowthDirection() {
// IBranchPart branch = getOwnedBranch();
// IStructureAlgorithm sa = branch.getBranchPolicy()
// .getStructureAlgorithm(branch);
// if (sa instanceof IBranchStructureAlgorithm) {
// return ((IBranchStructureAlgorithm) sa).calcChildGrowthDirection(
// branch, this);
// }
// return PositionConstants.NONE;
// }
public Cursor getCursor(Point pos) {
if (getFeedback() instanceof IBendPointsFeedback) {
int orientation = ((IBendPointsFeedback) getFeedback())
.getOrientation(pos);
if (orientation != PositionConstants.NONE)
return GEFUtils.getPositionCursor(orientation);
}
return super.getCursor(pos);
}
public void addRangeListener(IRangeListener listener) {
if (rangeListeners == null)
rangeListeners = new ArrayList<IRangeListener>();
rangeListeners.add(listener);
}
public void removeRangeListener(IRangeListener listener) {
if (rangeListeners != null)
rangeListeners.remove(listener);
}
protected void fireRangeChanged() {
if (rangeListeners == null)
return;
RangeEvent event = new RangeEvent(this);
for (Object o : rangeListeners.toArray()) {
((IRangeListener) o).rangeChanged(event);
}
}
}