/* ****************************************************************************** * 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.ColumnHeadEditTool; 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 SpreadsheetStructure extends AbstractBranchStructure implements IBranchDoubleClickSupport, IBranchMoveSupport { private Set<IBranchPart> invalidatingBranches = null; protected boolean isValidStructureData(IBranchPart branch, Object data) { return super.isValidStructureData(branch, data) && (data instanceof Chart); } protected Object createStructureData(IBranchPart branch) { return new Chart(branch); } public Chart getChart(IBranchPart branch) { return (Chart) 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 chartWidth = area.width; Chart chart = getChart(branch); int lineWidth = chart.getLineWidth(); int y = area.bottom() + lineWidth; if (chart.hasColumns()) { y += chart.getColumnHeadHeight() + lineWidth + majorSpacing; } int x = area.x; IInsertion ins = getCurrentInsertion(branch); for (int i = 0; i < subBranches.size(); i++) { if (ins != null && i == ins.getIndex()) { Rectangle insArea = new Rectangle(x, y + halfMinorSpacing1, chartWidth, ins.getSize().height); info.add(insArea); y += insArea.height + minorSpacing + lineWidth; } y += halfMinorSpacing1; IBranchPart subBranch = subBranches.get(i); IFigure subBranchFigure = subBranch.getFigure(); Dimension size = subBranchFigure.getPreferredSize(); Rectangle r = new Rectangle( x + halfMajorSpacing1 - halfMinorSpacing1, y, size.width, size.height); info.put(subBranchFigure, r); y += size.height + halfMinorSpacing2 + lineWidth; } if (ins != null && ins.getIndex() == subBranches.size()) { Rectangle insArea = new Rectangle(x, y + halfMinorSpacing1, chartWidth, ins.getSize().height); 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(); Chart chart = getChart(branch); int width = chart.getRowHeadWidth(); Point topLeft = branch.getTopicPart().getFigure().getBounds() .getBottomLeft(); Rectangle r = new Rectangle(topLeft.x, topLeft.y, width, branchFigure.getBounds().bottom() - topLeft.y); 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(); Chart chart = getChart(branch); int lineWidth = chart.getLineWidth(); int majorSpacing = chart.getMajorSpacing(); int minorSpacing = chart.getMinorSpacing(); int y = branch.getFigure().getBounds().y + chart.getTitleAreaHeight() + lineWidth * 2; if (!chart.getColumns().isEmpty()) { y += chart.getColumnHeadHeight() + majorSpacing; } Dimension insSize = calcInsSize(key.getFigure()); int insHeight = insSize.height + lineWidth + minorSpacing; List<IBranchPart> subbranches = branch.getSubBranches(); int num = subbranches.size(); int ret = 0; for (IBranchPart subBranch : branch.getSubBranches()) { IFigure subFigure = subBranch.getFigure(); int h = subFigure.getSize().height + lineWidth + minorSpacing; int hint = y + (h + insHeight) / 2; if (pos.y < hint) return ret; if (withDisabled || subFigure.isEnabled()) ret++; y += h; } return withDisabled ? num : -1; } private Dimension calcInsSize(IReferencedFigure child) { return child.getSize().scale(0.8); } public int getRangeGrowthDirection(IBranchPart branch, IBranchRangePart range) { return PositionConstants.SOUTH; } public int getSummaryDirection(IBranchPart branch, ISummaryPart summary) { return PositionConstants.EAST; } 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) { Chart chart = getChart(branch); Cell cell = chart.findCell(pos); if (cell != null) { handleDoubleClickInCell(cell); } else { ColumnHead colHead = chart.findColumnHead(pos); if (colHead != null) { handleDoubleClickInColumnHead(chart, colHead); } } return true; } private void handleDoubleClickInColumnHead(Chart chart, ColumnHead colHead) { IBranchPart chartBranch = chart.getTitle(); EditDomain domain = chartBranch.getSite().getDomain(); if (domain != null) { ITool tool = domain.getTool(Spreadsheet.TOOL_EDIT_COLUMN_HEAD); if (tool != null && tool instanceof ColumnHeadEditTool) { ColumnHeadEditTool editTool = (ColumnHeadEditTool) tool; editTool.setTargetViewer(chartBranch.getSite().getViewer()); domain.setActiveTool(Spreadsheet.TOOL_EDIT_COLUMN_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_COLUMN_HEAD, colHead) .setParameter(Spreadsheet.PARAM_COLUMN, chart.findColumn(colHead))); } } } } private void handleDoubleClickInCell(Cell cell) { IBranchPart rowBranch = cell.getOwnedRow().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.getOwnedColumn().getHead().getLabels()); domain.handleRequest(request); } public boolean canMove(IBranchPart branch, MouseDragEvent me) { Chart chart = getChart(branch); ColumnHead colHead = chart.findColumnHead(me.startingLocation); if (colHead != null) { MindMapUtils.setCache(branch, Spreadsheet.CACHE_MOVE_SOURCE_COLUMN_HEAD, colHead); return true; } return false; } public String getMoveTool(IBranchPart branch, MouseDragEvent me) { return Spreadsheet.TOOL_MOVE_COLUMN; } public int calcColumnInsertionIndex(IBranchPart branch, Point pos) { Chart chart = getChart(branch); IInsertion colIns = (IInsertion) MindMapUtils.getCache(branch, Spreadsheet.CACHE_COLUMN_INSERTION); int insWidth = colIns == null ? 0 : colIns.getSize().width + chart.getMinorSpacing(); List<Column> cols = chart.getColumns(); int lineWidth = chart.getLineWidth(); int x = chart.getTitle().getFigure().getBounds().x + lineWidth + chart.getRowHeadWidth() + chart.getMinorSpacing() + lineWidth; for (int index = 0; index < cols.size(); index++) { Column col = cols.get(index); //x += insWidth * index / (cols.size() + 1); int colWidth = col.getWidth(); int w = colWidth + insWidth / (cols.size() + 1); if (pos.x < x + w / 2) return index; x += w + lineWidth; } return cols.size(); } 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); } protected Point calcInsertPosition(IBranchPart branch, IBranchPart child, ParentSearchKey key) { List<IBranchPart> subBranches = branch.getSubBranches(); int index = calcInsIndex(branch, key, true); if (subBranches.isEmpty()) { return calcFirstChildPosition(branch, key); } IBranchPart sub = index == subBranches.size() ? subBranches.get(subBranches.size() - 1) : subBranches.get(index); int deltaY = (key.getFigure().getSize().height + sub.getFigure().getSize().height) / 2; return getFigureLocation(sub.getFigure()).getTranslated( (key.getInvent().getSize().width - sub.getTopicPart().getFigure().getSize().width) / 2, index == subBranches.size() ? deltaY + getMajorSpacing(branch) / 2 : -deltaY); } 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); } }