/* ******************************************************************************
* 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.spreadsheet.structures;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.xmind.core.Core;
import org.xmind.gef.EditDomain;
import org.xmind.gef.GEF;
import org.xmind.gef.Request;
import org.xmind.gef.draw2d.IReferencedFigure;
import org.xmind.gef.event.MouseDragEvent;
import org.xmind.gef.part.IPart;
import org.xmind.gef.tool.ITool;
import org.xmind.ui.branch.AbstractBranchStructure;
import org.xmind.ui.branch.IBranchDoubleClickSupport;
import org.xmind.ui.branch.IBranchMoveSupport;
import org.xmind.ui.branch.IInsertion;
import org.xmind.ui.branch.Insertion;
import org.xmind.ui.internal.spreadsheet.RowHeadEditTool;
import org.xmind.ui.internal.spreadsheet.Spreadsheet;
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;
import org.xmind.ui.util.MindMapUtils;
public class SpreadsheetColumnStructure extends AbstractBranchStructure
implements IBranchDoubleClickSupport, IBranchMoveSupport {
private Set<IBranchPart> invalidatingBranches = null;
protected boolean isValidStructureData(IBranchPart branch, Object data) {
return super.isValidStructureData(branch, data)
&& (data instanceof Chart2);
}
protected Object createStructureData(IBranchPart branch) {
return new Chart2(branch);
}
public Chart2 getChart(IBranchPart branch) {
return (Chart2) super.getStructureData(branch);
}
protected void doFillPlusMinus(IBranchPart branch, IPlusMinusPart plusMinus,
LayoutInfo info) {
if (!plusMinus.getFigure().isVisible()) {
info.put(plusMinus.getFigure(), info.createInitBounds());
return;
}
Point ref = info.getReference();
int y = ref.y;
Rectangle topicBounds = info.getCheckedClientArea();
int x = topicBounds.right();
IFigure pmFigure = plusMinus.getFigure();
Dimension size = pmFigure.getPreferredSize();
Rectangle r = new Rectangle(x, y - size.height / 2, size.width,
size.height);
info.put(pmFigure, r);
}
protected void doFillSubBranches(IBranchPart branch,
List<IBranchPart> subBranches, LayoutInfo info) {
int majorSpacing = getMajorSpacing(branch);
int minorSpacing = getMinorSpacing(branch);
int halfMinorSpacing1 = minorSpacing / 2;
int halfMinorSpacing2 = minorSpacing - halfMinorSpacing1;
int halfMajorSpacing1 = majorSpacing / 2;
int halfMajorSpacing2 = majorSpacing - halfMajorSpacing1;
Rectangle area = info.getCheckedClientArea();
int chartHeight = area.height;
Chart2 chart = getChart(branch);
int lineWidth = chart.getLineWidth();
int x = area.x + lineWidth;
if (chart.hasRows()) {
x += chart.getRowHeadWidth() + lineWidth + minorSpacing;
} else {
x += lineWidth + majorSpacing;
}
int y = area.bottom() + halfMinorSpacing1 + lineWidth;
IInsertion ins = getCurrentInsertion(branch);
for (int i = 0; i < subBranches.size(); i++) {
if (ins != null && i == ins.getIndex()) {
Rectangle insArea = new Rectangle(x + halfMinorSpacing1, y,
ins.getSize().width, chartHeight);
info.add(insArea);
x += insArea.width + minorSpacing + lineWidth;
}
x += halfMinorSpacing1;
IBranchPart subBranch = subBranches.get(i);
IFigure subBranchFigure = subBranch.getFigure();
Dimension size = subBranchFigure.getPreferredSize();
Rectangle r = new Rectangle(x + halfMajorSpacing1, y, size.width,
size.height);
info.put(subBranchFigure, r);
x += size.width + halfMinorSpacing2;
}
if (ins != null && ins.getIndex() == subBranches.size()) {
Rectangle insArea = new Rectangle(x, y, ins.getSize().width,
chartHeight);
info.add(insArea);
}
info.addMargins(lineWidth + halfMajorSpacing2,
lineWidth + halfMinorSpacing2, lineWidth + halfMajorSpacing1,
lineWidth + halfMinorSpacing1);
}
protected void invalidateBranch(IBranchPart branch) {
super.invalidateBranch(branch);
if (!isInvalidatingBranch(branch)) {
addInvalidatingBranch(branch);
for (IBranchPart sub : branch.getSubBranches()) {
Object flag = MindMapUtils.getCache(sub,
Spreadsheet.CACHE_INVALIDATING);
if (!(flag instanceof Boolean)
|| !((Boolean) flag).booleanValue()) {
invalidateChild(sub);
}
MindMapUtils.flushCache(sub, Spreadsheet.CACHE_INVALIDATING);
}
removeInvalidatingBranch(branch);
}
}
private void invalidateChild(IBranchPart sub) {
sub.getFigure().invalidate();
}
private void removeInvalidatingBranch(IBranchPart branch) {
invalidatingBranches.remove(branch);
if (invalidatingBranches.isEmpty())
invalidatingBranches = null;
}
private void addInvalidatingBranch(IBranchPart branch) {
if (invalidatingBranches == null)
invalidatingBranches = new HashSet<IBranchPart>();
invalidatingBranches.add(branch);
}
private boolean isInvalidatingBranch(IBranchPart branch) {
return invalidatingBranches != null
&& invalidatingBranches.contains(branch);
}
public IPart calcNavigation(IBranchPart branch, String navReqType) {
if (GEF.REQ_NAV_DOWN.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_DOWN.equals(navReqType)) {
return getSubTopicPart(branch, sourceChild.getBranchIndex() + 1);
} else if (GEF.REQ_NAV_UP.equals(navReqType)) {
int sourceIndex = sourceChild.getBranchIndex();
if (!sequential && sourceIndex == 0)
return branch.getTopicPart();
return getSubTopicPart(branch, sourceIndex - 1);
}
return super.calcChildNavigation(branch, sourceChild, navReqType,
sequential);
}
public int calcChildDistance(IBranchPart branch, ParentSearchKey key) {
Point pos = key.getCursorPos();
IFigure branchFigure = branch.getFigure();
Chart2 chart = getChart(branch);
int height = chart.getColHeadHeight();
Point topLeft = branch.getTopicPart().getFigure().getBounds()
.getBottomLeft();
Rectangle r = new Rectangle(topLeft.x, topLeft.y,
branchFigure.getBounds().right() - topLeft.x, height);
if (!branch.getSubBranches().isEmpty() && !branch.isFolded()) {
if (r.contains(pos))
return 1;
}
Point childLoc = key.getFigure().getBounds().getLocation();
if (r.x < pos.x && r.right() > pos.x) {
if (childLoc.y > r.y && childLoc.y < r.bottom())
return 1;
}
return -1;
}
public IInsertion calcInsertion(IBranchPart branch, ParentSearchKey key) {
int newIndex = calcInsIndex(branch, key, true);
Dimension newSize = calcInsSize(key.getFigure());
return new Insertion(branch, newIndex, newSize);
}
protected int calcInsIndex(IBranchPart branch, ParentSearchKey key,
boolean withDisabled) {
if (branch.getSubBranches().isEmpty() || branch.isFolded())
return withDisabled ? 0 : -1;
Point pos = key.getCursorPos();
Chart2 chart = getChart(branch);
int lineWidth = chart.getLineWidth();
int majorSpacing = chart.getMajorSpacing();
int minorSpacing = chart.getMinorSpacing();
int x = branch.getFigure().getBounds().x + lineWidth * 2;
if (!chart.getRows().isEmpty()) {
x += chart.getRowHeadWidth() + majorSpacing;
}
Dimension insSize = calcInsSize(key.getFigure());
int insWidth = insSize.width + lineWidth + minorSpacing;
List<IBranchPart> subbranches = branch.getSubBranches();
int num = subbranches.size();
int ret = 0;
for (IBranchPart subBranch : branch.getSubBranches()) {
IFigure subFigure = subBranch.getFigure();
int w = subFigure.getSize().width + lineWidth + minorSpacing;
int hint = x + (w + insWidth) / 2;
if (pos.x < hint)
return ret;
if (withDisabled || subFigure.isEnabled())
ret++;
x += w;
}
return withDisabled ? num : -1;
}
private Dimension calcInsSize(IReferencedFigure child) {
return child.getSize().scale(0.8);
}
public int getRangeGrowthDirection(IBranchPart branch,
IBranchRangePart range) {
return PositionConstants.EAST;
}
public int getSummaryDirection(IBranchPart branch, ISummaryPart summary) {
return PositionConstants.SOUTH;
}
public int getSourceOrientation(IBranchPart branch) {
return PositionConstants.NONE;
}
public int getChildTargetOrientation(IBranchPart branch,
IBranchPart subBranch) {
return PositionConstants.NONE;
}
public boolean handleDoubleClick(IBranchPart branch, Point pos) {
Chart2 chart = getChart(branch);
Cell2 cell = chart.findCell(pos);
if (cell != null) {
handleDoubleClickInCell(cell);
} else {
RowHead rowHead = chart.findRowHead(pos);
if (rowHead != null) {
handleDoubleClickInColumnHead(chart, rowHead);
}
}
return true;
}
private void handleDoubleClickInColumnHead(Chart2 chart, RowHead rowHead) {
IBranchPart chartBranch = chart.getTitle();
EditDomain domain = chartBranch.getSite().getDomain();
if (domain != null) {
ITool tool = domain.getTool(Spreadsheet.TOOL_EDIT_ROW_HEAD);
if (tool != null && tool instanceof RowHeadEditTool) {
RowHeadEditTool editTool = (RowHeadEditTool) tool;
editTool.setTargetViewer(chartBranch.getSite().getViewer());
domain.setActiveTool(Spreadsheet.TOOL_EDIT_ROW_HEAD);
if (domain.getActiveTool() == editTool) {
domain.handleRequest(
new Request(GEF.REQ_EDIT)
.setPrimaryTarget(chartBranch)
.setViewer(
chartBranch.getSite().getViewer())
.setParameter(Spreadsheet.PARAM_CHART, chart)
.setParameter(Spreadsheet.PARAM_ROW_HEAD, rowHead)
.setParameter(Spreadsheet.PARAM_ROW,
chart.findRow(rowHead)));
}
}
}
}
private void handleDoubleClickInCell(Cell2 cell) {
IBranchPart rowBranch = cell.getOwnedColumn().getHead();
ITopicPart rowTopic = rowBranch.getTopicPart();
if (rowTopic == null)
return;
EditDomain domain = rowTopic.getSite().getDomain();
if (domain == null)
return;
Request request = new Request(MindMapUI.REQ_CREATE_CHILD);
request.setDomain(domain);
request.setViewer(rowTopic.getSite().getViewer());
request.setPrimaryTarget(rowTopic);
request.setParameter(MindMapUI.PARAM_WITH_ANIMATION, Boolean.TRUE);
request.setParameter(MindMapUI.PARAM_PROPERTY_PREFIX + Core.Labels,
cell.getOwnedRow().getHead().getLabels());
domain.handleRequest(request);
}
public boolean canMove(IBranchPart branch, MouseDragEvent me) {
Chart2 chart = getChart(branch);
RowHead rowHead = chart.findRowHead(me.startingLocation);
if (rowHead != null) {
MindMapUtils.setCache(branch,
Spreadsheet.CACHE_MOVE_SOURCE_ROW_HEAD, rowHead);
return true;
}
return false;
}
public String getMoveTool(IBranchPart branch, MouseDragEvent me) {
return Spreadsheet.TOOL_MOVE_ROW;
}
public int calcRowInsertionIndex(IBranchPart branch, Point pos) {
Chart2 chart = getChart(branch);
IInsertion rowIns = (IInsertion) MindMapUtils.getCache(branch,
Spreadsheet.CACHE_ROW_INSERTION);
int insHeight = rowIns == null ? 0
: rowIns.getSize().height + chart.getMinorSpacing();
List<Row2> rows = chart.getRows();
int lineWidth = chart.getLineWidth();
int y = chart.getTitle().getFigure().getBounds().y
+ chart.getTitleAreaHeight() + lineWidth
+ chart.getColHeadHeight() + chart.getMajorSpacing();
for (int index = 0; index < rows.size(); index++) {
Row2 row = rows.get(index);
int colHeight = row.getHeight();
int h = colHeight + insHeight / (rows.size() + 1);
if (pos.y < y + h / 2)
return index;
y += h + lineWidth;
}
return rows.size();
}
public int getQuickMoveOffset(IBranchPart branch, IBranchPart child,
int direction) {
if (direction == PositionConstants.EAST)
return 1;
if (direction == PositionConstants.WEST)
return -1;
return super.getQuickMoveOffset(branch, child, direction);
}
protected Point calcInsertPosition(IBranchPart branch, IBranchPart child,
ParentSearchKey key) {
List<IBranchPart> subBranches = branch.getSubBranches();
int index = calcInsIndex(branch, key, true);
IBranchPart sub = index == subBranches.size()
? subBranches.get(subBranches.size() - 1)
: subBranches.get(index);
int deltaX = 0;
if (index == subBranches.size())
deltaX = (key.getInvent().getSize().width
+ sub.getFigure().getSize().width) / 2
+ getMinorSpacing(branch);
else
deltaX = (key.getInvent().getSize().width
- sub.getFigure().getSize().width) / 2
- key.getFigure().getSize().scale(0.8d).width
- getMinorSpacing(branch);
return getFigureLocation(sub.getFigure()).getTranslated(deltaX,
(key.getFigure().getSize().height
- sub.getTopicPart().getFigure().getSize().height) / 2);
}
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)
return getFigureLocation(subBranches.get(index).getFigure())
.getTranslated((inventSize.width - subBranches.get(index)
.getTopicPart().getFigure().getSize().width) / 2,
0);
return calcInsertPosition(branch, child, key);
}
}