/* ******************************************************************************
* 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 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.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();
Rectangle childBounds = new Rectangle(x, y, size.width,
size.height);
info.put(childFigure, childBounds);
y += size.height + 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();//getMinorSpacing(branch);
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;
}
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);
}
}