/* ****************************************************************************** * 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.ArrayList; import java.util.HashSet; import java.util.List; 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.GEF; import org.xmind.gef.Request; import org.xmind.gef.draw2d.IReferencedFigure; import org.xmind.gef.draw2d.ReferencedLayoutData; import org.xmind.gef.graphicalpolicy.IStructure; import org.xmind.gef.part.IPart; import org.xmind.ui.branch.AbstractBranchStructure; import org.xmind.ui.branch.ICreatableBranchStructureExtension; import org.xmind.ui.branch.IInsertion; import org.xmind.ui.branch.IMovableBranchStructureExtension; import org.xmind.ui.branch.Insertion; import org.xmind.ui.internal.spreadsheet.Spreadsheet; import org.xmind.ui.mindmap.IBoundaryPart; 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.MindMapUI; import org.xmind.ui.tools.ParentSearchKey; import org.xmind.ui.util.MindMapUtils; public class RowStructure extends AbstractBranchStructure implements ICreatableBranchStructureExtension, IMovableBranchStructureExtension { protected boolean isValidStructureData(IBranchPart branch, Object data) { return super.isValidStructureData(branch, data) && (data instanceof Row); } protected Object createStructureData(IBranchPart branch) { IBranchPart parent = branch.getParentBranch(); if (parent != null) { Chart chart = null; IStructure sa = parent.getBranchPolicy().getStructure(parent); if (sa instanceof SpreadsheetStructure) { chart = ((SpreadsheetStructure) sa).getChart(parent); } if (chart == null) { chart = new Chart(parent); } return chart.getRow(branch.getBranchIndex()); } //TODO make row data available even without parent branch Chart chart = new Chart(null); Row row = new Row(branch, chart); Column col = new Column(chart, ColumnHead.EMPTY); chart.setContent(row, col); chart.setLineWidth(1); Cell cell = new Cell(chart, row, col); row.addCell(cell); for (IBranchPart sub : branch.getSubBranches()) { cell.addItem(new Item(chart, sub)); } return row; } public Row getRow(IBranchPart branch) { return (Row) super.getStructureData(branch); } protected void doFillPlusMinus(IBranchPart branch, IPlusMinusPart plusMinus, LayoutInfo info) { 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) { // overrides fillSubBranches() instead as below: } protected void fillSubBranches(IBranchPart branch, List<IBranchPart> subBranches, LayoutInfo info) { // super.fillSubBranches(branch, subBranches, info); Rectangle area = info.getCheckedClientArea(); Row row = getRow(branch); Chart chart = row.getOwnedChart(); int lineWidth = chart.getLineWidth(); int cellSpacing = chart.getMinorSpacing(); int itemSpacing = row.getMinorSpacing(); IInsertion insertion = getCurrentInsertion(branch); ColumnHead insHead = (ColumnHead) MindMapUtils.getCache(branch, Spreadsheet.KEY_INSERTION_COLUMN_HEAD); int startY = info.getReference().y - row.getPrefCellHeight() / 2; int x = area.x + chart.getRowHeadWidth() + cellSpacing + lineWidth; List<Column> columns = chart.getColumns(); IInsertion colIns = (IInsertion) MindMapUtils.getCache(chart.getTitle(), Spreadsheet.CACHE_COLUMN_INSERTION); for (int colIndex = 0; colIndex < columns.size(); colIndex++) { if (colIns != null && colIns.getIndex() == colIndex) { x += colIns.getSize().width + chart.getMinorSpacing() + lineWidth; } Column col = columns.get(colIndex); int y = startY; boolean insertionInColumn = insertion != null && col.getHead().equals(insHead); Cell cell = row.findCellByColumn(col); if (cell != null) { info.add(new Rectangle(x, y, col.getPrefCellWidth(), cell.getContentHeight())); List<Item> items = cell.getItems(); int num = items.size(); for (int i = 0; i < num; i++) { Item item = items.get(i); if (insertionInColumn && insertion.getIndex() == i) { Rectangle r = insertion.createRectangle(x, y); info.add(r); y += r.height + itemSpacing; } IBranchPart child = item.getBranch(); IFigure childFigure = child.getFigure(); Dimension size = childFigure.getPreferredSize(); int bw = 0; int bh = 0; List<IBoundaryPart> boundaries = branch.getBoundaries(); if (!boundaries.isEmpty()) { for (IBoundaryPart boundary : boundaries) { List<IBranchPart> enclosingBranches = boundary .getEnclosingBranches(); if (enclosingBranches.contains(child)) { bw = boundary.getFigure().getInsets().left - 1; if (boundary.getTitle() != null && boundary .getTitle().getFigure() != null) bw += 5; } if ((!enclosingBranches.isEmpty()) && child.equals(enclosingBranches.get(0))) { bh = boundary.getFigure().getInsets().top; if (boundary.getTitle() != null && boundary .getTitle().getFigure() != null) { Dimension s = boundary.getTitle() .getFigure().getPreferredSize(); bh = Math.max(bh, s.height); } // y += bh; bh = 0; } if ((!enclosingBranches.isEmpty()) && child.equals(enclosingBranches.get( enclosingBranches.size() - 1))) { bh = boundary.getFigure().getInsets().bottom; } } } int headWidth = item.getPrefColumnHead() .getPrefSize().width; Rectangle childBounds = new Rectangle(x + bw, y, (size.width > headWidth ? size.width : headWidth), size.height); info.put(childFigure, childBounds); y += size.height + itemSpacing + bh + itemSpacing; } if (insertionInColumn && insertion.getIndex() == num) { info.add(insertion.createRectangle(x, y)); } } else if (insertionInColumn) { info.add(insertion.createRectangle(x, y)); } x += col.getPrefCellWidth() + cellSpacing + lineWidth; } if (colIns != null && colIns.getIndex() == columns.size()) { info.add(new Rectangle(x, startY, colIns.getSize().width, 1)); } } public void fillLayoutData(IBranchPart branch, ReferencedLayoutData data) { super.fillLayoutData(branch, data); MindMapUtils.flushCache(branch, Spreadsheet.CACHE_INVALIDATING); } protected void invalidateBranch(IBranchPart branch) { super.invalidateBranch(branch); MindMapUtils.setCache(branch, Spreadsheet.CACHE_INVALIDATING, Boolean.TRUE); } public int getSourceOrientation(IBranchPart branch) { return PositionConstants.NONE; } public int getChildTargetOrientation(IBranchPart branch, IBranchPart subBranch) { return PositionConstants.NONE; } public int getRangeGrowthDirection(IBranchPart branch, IBranchRangePart range) { return PositionConstants.SOUTH; } public int getSummaryDirection(IBranchPart branch, ISummaryPart summary) { return PositionConstants.EAST; } public IPart calcNavigation(IBranchPart branch, String navReqType) { if (GEF.REQ_NAV_RIGHT.equals(navReqType)) { Row row = getRow(branch); if (!row.getCells().isEmpty()) { Cell cell = row.getCells().get(0); if (!cell.getItems().isEmpty()) { Item item = cell.getItems().get(0); return item.getBranch().getTopicPart(); } } } return super.calcNavigation(branch, navReqType); } public IPart calcChildNavigation(IBranchPart branch, IBranchPart sourceChild, String navReqType, boolean sequential) { if (GEF.REQ_NAV_LEFT.equals(navReqType)) { Row row = getRow(branch); Cell cell = row.findCellByItem(sourceChild); if (cell != null) { Cell prev = row.getPreviousCell(cell); if (prev == null) { if (!sequential) return branch.getTopicPart(); } } } else if (GEF.REQ_NAV_UP.equals(navReqType)) { Row row = getRow(branch); Item item = row.findItem(sourceChild); if (item != null) { Item prev = item.getPreviousItem(); if (prev != null) return prev.getBranch().getTopicPart(); } } else if (GEF.REQ_NAV_DOWN.equals(navReqType)) { Row row = getRow(branch); Item item = row.findItem(sourceChild); if (item != null) { Item next = item.getNextItem(); if (next != null) return next.getBranch().getTopicPart(); } } return super.calcChildNavigation(branch, sourceChild, navReqType, sequential); } public void calcSequentialNavigation(IBranchPart branch, IBranchPart startChild, IBranchPart endChild, List<IBranchPart> results) { Row row = getRow(branch); Item startItem = row.findItem(startChild); if (startItem != null) { Cell cell = startItem.getOwnedCell(); Item endItem = row.findItem(endChild); if (endItem != null && cell == endItem.getOwnedCell()) { int startIndex = cell.getItemIndex(startItem); int endIndex = cell.getItemIndex(endItem); if (startIndex >= 0 && endIndex >= 0) { boolean decreasing = endIndex < startIndex; for (int i = startIndex; decreasing ? i >= endIndex : i <= endIndex;) { Item item = cell.getItems().get(i); results.add(item.getBranch()); if (decreasing) i--; else i++; } } } } super.calcSequentialNavigation(branch, startChild, endChild, results); } public int calcChildDistance(IBranchPart branch, ParentSearchKey key) { Point pos = key.getCursorPos(); Row row = getRow(branch); Chart chart = row.getOwnedChart(); int rowY = row.getTop(); int rowHeight = row.getHeight(); if (pos.y > rowY && pos.y < rowY + rowHeight) { if (!chart.hasColumns()) { int x = chart.getTitle().getFigure().getBounds().x; int w = chart.getTitle().getFigure().getBounds().width; int childX = key.getFigure().getBounds().x; if (childX > x && childX < x + w) return 0; } Cell cell = row.findCell(pos); if (cell != null) { if (cell.getItems().isEmpty()) return 0; int colX = cell.getOwnedColumn().getLeft(); int offset = pos.x - colX; int index = 0; int last = cell.getItems().size() - 1; for (Item item : cell.getItems()) { Rectangle itemBounds = item.getBranch().getFigure() .getBounds(); if (index == 0) { if (pos.y < itemBounds.y) return 0; } if (index == last) { if (pos.y > itemBounds.bottom()) return 0; } if (pos.x < itemBounds.x) return 0; Rectangle itemTopicBounds = item.getBranch().getTopicPart() .getFigure().getBounds(); if (pos.x < itemTopicBounds.right()) { return offset; } index++; } return offset; } } return -1; } public int calcChildIndex(IBranchPart branch, ParentSearchKey key) { Row row = getRow(branch); Chart chart = row.getOwnedChart(); Point pos = key.getCursorPos(); Cell cell = row.findCell(pos); if (cell == null || cell.getItems().isEmpty()) return -1; Dimension insSize = getInsSize(key.getFigure()); int y = row.getTop() + chart.getMinorSpacing() / 2; int insHeight = insSize.height; int spacing = row.getMinorSpacing(); int disabled = 0; for (Item item : cell.getItems()) { IBranchPart itemBranch = item.getBranch(); Dimension itemSize = itemBranch.getFigure().getSize(); int hint = y + (itemSize.height + insHeight) / 2; if (pos.y < hint) { return itemBranch.getBranchIndex() - disabled; } y += itemSize.height + spacing; if (!itemBranch.getFigure().isEnabled()) disabled++; } return -1; } protected int calcInsIndex(IBranchPart branch, ParentSearchKey key, boolean withDisabled) { Row row = getRow(branch); Chart chart = row.getOwnedChart(); Point pos = key.getCursorPos(); Cell cell = row.findCell(pos); if (cell == null || cell.getItems().isEmpty()) return -1; Dimension insSize = getInsSize(key.getFigure()); int y = row.getTop() + chart.getMinorSpacing() / 2; int insHeight = insSize.height; int spacing = row.getMinorSpacing(); int disabled = 0; for (Item item : cell.getItems()) { IBranchPart itemBranch = item.getBranch(); Dimension itemSize = itemBranch.getFigure().getSize(); int hint = y + (itemSize.height + insHeight) / 2; if (pos.y < hint) { return getOldIndex(branch, itemBranch) - disabled; } y += itemSize.height + spacing; if (!itemBranch.getFigure().isEnabled() && !withDisabled) disabled++; } return -1; } public IInsertion calcInsertion(IBranchPart branch, ParentSearchKey key) { return calcInsertion(branch, key, true); } private Insertion calcInsertion(IBranchPart branch, ParentSearchKey key, boolean withDisabled) { Row row = getRow(branch); Chart chart = row.getOwnedChart(); Point pos = key.getCursorPos(); Cell cell = row.findCell(pos); if (cell == null) return null; Dimension insSize = getInsSize(key.getFigure()); if (cell.getItems().isEmpty()) { return new CellInsertion(branch, -1, insSize, cell.getOwnedColumn().getHead()); } int y = row.getTop() + chart.getMinorSpacing() / 2; int insHeight = insSize.height; int spacing = row.getMinorSpacing();//getMinorSpacing(branch); int index = 0; for (Item item : cell.getItems()) { IBranchPart itemBranch = item.getBranch(); Dimension itemSize = itemBranch.getFigure().getSize(); int hint = y + (itemSize.height + insHeight) / 2; if (pos.y < hint) { return new CellInsertion(branch, index, insSize, cell.getOwnedColumn().getHead()); } y += itemSize.height + spacing; if (withDisabled || itemBranch.getFigure().isEnabled()) index++; } return new CellInsertion(branch, withDisabled ? index : -1, insSize, cell.getOwnedColumn().getHead()); } private Dimension getInsSize(IReferencedFigure child) { return child.getSize(); } public void decorateMoveInRequest(IBranchPart targetParent, ParentSearchKey childKey, IBranchPart sourceParent, Request request) { ColumnHead colHead = (ColumnHead) MindMapUtils.getCache(targetParent, Spreadsheet.KEY_INSERTION_COLUMN_HEAD); if (colHead != null) { request.setParameter(MindMapUI.PARAM_PROPERTY_PREFIX + Core.Labels, new HashSet<String>(colHead.getLabels())); } else { request.setParameter(MindMapUI.PARAM_PROPERTY_PREFIX + Core.Labels, new HashSet<String>()); } } public void decorateMoveOutRequest(IBranchPart sourceParent, ParentSearchKey childKey, IBranchPart targetParent, Request request) { request.setParameter(MindMapUI.PARAM_PROPERTY_PREFIX + Core.Labels, null); } public void decorateCreateRequest(IBranchPart branch, IBranchPart sourceChild, Request request) { Row row = getRow(branch); Cell cell = row.findCellByItem(sourceChild); if (cell != null) { ColumnHead colHead = cell.getOwnedColumn().getHead(); request.setParameter(MindMapUI.PARAM_PROPERTY_PREFIX + Core.Labels, new HashSet<String>(colHead.getLabels())); } } public int getQuickMoveOffset(IBranchPart branch, IBranchPart child, int direction) { if (direction == PositionConstants.SOUTH) { Row row = getRow(branch); Item item = row.findItem(child); if (item != null) { Item next = item.getNextItem(); if (next != null) return next.getBranch().getBranchIndex() - child.getBranchIndex(); } } else if (direction == PositionConstants.NORTH) { Row row = getRow(branch); Item item = row.findItem(child); if (item != null) { Item next = item.getPreviousItem(); if (next != null) return next.getBranch().getBranchIndex() - child.getBranchIndex(); } } return super.getQuickMoveOffset(branch, child, direction); } protected Point calcInsertPosition(IBranchPart branch, IBranchPart child, ParentSearchKey key) { Row row = getRow(branch); Cell cell = row.findCell(key.getCursorPos()); if (cell == null) { return calcFirstChildPosition(branch, key); } List<Item> items = cell.getItems(); Dimension inventSize = key.getInvent().getSize(); Dimension insSize = key.getFigure().getSize(); if (items.isEmpty()) return new Point( cell.getX() + inventSize.width / 2 + row.getOwnedChart().getLineWidth(), getFigureLocation(branch.getFigure()).y - (cell.getHeight() - insSize.height > 0 ? (cell.getHeight() - insSize.height) / 2 : 0)); IInsertion insertion = calcInsertion(branch, key); int itemIndex = insertion == null ? -1 : insertion.getIndex(); IBranchPart sub = itemIndex == items.size() ? items.get(items.size() - 1).getBranch() : items.get(itemIndex).getBranch(); int deltaY = (insSize.height + sub.getFigure().getSize().height) / 2; return getFigureLocation(sub.getFigure()).getTranslated( (inventSize.width - sub.getTopicPart().getFigure().getSize().width) / 2, itemIndex == items.size() ? deltaY : -deltaY); } protected Point calcMovePosition(IBranchPart branch, IBranchPart child, ParentSearchKey key) { List<Integer> disables = getDisableBranches(branch); int index = calcInsIndex(branch, key, true); int oldIndex = getOldIndex(branch, child); IInsertion insertion = calcInsertion(branch, key); int itemIndex = insertion == null ? -1 : insertion.getIndex(); if (disables != null) { if (disables.contains(index)) { oldIndex = index; } else if (disables.contains(index - 1) && itemIndex != 0) { index--; oldIndex = index; itemIndex--; } } Row row = getRow(branch); Cell cell = row.findCell(key.getCursorPos()); List<Item> items = cell.getItems(); Dimension inventSize = key.getInvent().getSize(); Dimension insSize = key.getFigure().getSize(); if (items.isEmpty()) return new Point( cell.getX() + inventSize.width / 2 + row.getOwnedChart().getLineWidth(), getFigureLocation(branch.getFigure()).y - (cell.getHeight() - insSize.height > 0 ? (cell.getHeight() - insSize.height) / 2 : 0)); if (index == oldIndex || (index == -1 && !items.get(items.size() - 1) .getBranch().getFigure().isEnabled())) { IBranchPart sub = items.get( itemIndex == items.size() ? items.size() - 1 : itemIndex) .getBranch(); if (cell.equals(row.findCellByItem(sub))) return getFigureLocation(sub.getFigure()) .getTranslated((inventSize.width - sub.getTopicPart() .getFigure().getSize().width) / 2, 0); } return calcInsertPosition(branch, child, key); } public boolean isBranchMoved(IBranchPart branch, IBranchPart child, ParentSearchKey key) { return true; } protected int getOldIndex(IBranchPart branch, IBranchPart child) { Row row = getRow(branch); if (branch.equals(child.getParentBranch())) { Cell cell = row.findCellByItem(child); int count = 0; for (int i = 0; i < row.getCells().indexOf(cell); i++) { count += row.getCells().get(i).getItems().size(); } Item item = row.findItem(child); return count + cell.getItems().indexOf(item); } else { int index = 0; for (Cell cell : row.getCells()) { for (Item item : cell.getItems()) { if (!item.getBranch().getFigure().isEnabled()) return index; index++; } } } return -1; } protected List<Integer> getDisableBranches(IBranchPart branch) { List<Integer> disables = null; Row row = getRow(branch); int index = 0; for (Cell cell : row.getCells()) { for (Item item : cell.getItems()) { if (!item.getBranch().getFigure().isEnabled()) { if (disables == null) disables = new ArrayList<Integer>(); disables.add(index); } index++; } } return disables; } }