/* ******************************************************************************
* 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.core.ITopic;
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 ColumnStructure extends AbstractBranchStructure implements
ICreatableBranchStructureExtension, IMovableBranchStructureExtension {
protected boolean isValidStructureData(IBranchPart branch, Object data) {
return super.isValidStructureData(branch, data)
&& (data instanceof Column2);
}
protected Object createStructureData(IBranchPart branch) {
IBranchPart parent = branch.getParentBranch();
if (parent != null) {
Chart2 chart = null;
IStructure sa = parent.getBranchPolicy().getStructure(parent);
if (sa instanceof SpreadsheetColumnStructure) {
chart = ((SpreadsheetColumnStructure) sa).getChart(parent);
}
if (chart == null) {
chart = new Chart2(parent);
}
return chart.getColumn(branch.getBranchIndex());
}
Chart2 chart = new Chart2(null);
Column2 col = new Column2(branch, chart);
Row2 row = new Row2(chart, RowHead.EMPTY);
chart.setContent(col, row);
chart.setLineWidth(1);
Cell2 cell = new Cell2(chart, col, row);
col.addCell(cell);
for (IBranchPart sub : branch.getSubBranches()) {
cell.addItem(new Item2(chart, sub));
}
return col;
}
public Column2 getColumn(IBranchPart branch) {
return (Column2) super.getStructureData(branch);
}
protected void doFillPlusMinus(IBranchPart branch, IPlusMinusPart plusMinus,
LayoutInfo info) {
Point ref = info.getReference();
int x = ref.x;
Rectangle topicBounds = info.getCheckedClientArea();
int y = topicBounds.bottom();
IFigure pmFigure = plusMinus.getFigure();
Dimension size = pmFigure.getPreferredSize();
Rectangle r = new Rectangle(x - size.width / 2, y, 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) {
Rectangle area = info.getCheckedClientArea();
Column2 col = getColumn(branch);
Chart2 chart = col.getOwnedChart();
int lineWidth = chart.getLineWidth();
int cellSpacing = chart.getMinorSpacing();
int itemSpacing = col.getMinorSpacing();
IInsertion insertion = getCurrentInsertion(branch);
RowHead insHead = (RowHead) MindMapUtils.getCache(branch,
Spreadsheet.KEY_INSERTION_ROW_HEAD);
int startX = info.getReference().x - col.getPrefCellWidth() / 2;
int y = area.y + chart.getColHeadHeight() + cellSpacing / 2 + lineWidth;
List<Row2> rows = chart.getRows();
IInsertion rowIns = (IInsertion) MindMapUtils.getCache(chart.getTitle(),
Spreadsheet.CACHE_ROW_INSERTION);
for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
if (rowIns != null && rowIns.getIndex() == rowIndex) {
y += rowIns.getSize().height + chart.getMinorSpacing()
+ lineWidth;
}
Row2 row = rows.get(rowIndex);
int x = startX;
boolean insertionInRow = insertion != null
&& row.getHead().equals(insHead);
Cell2 cell = col.findCellByRow(row);
if (cell != null) {
info.add(new Rectangle(x, y, col.getPrefCellWidth(),
cell.getContentHeight()));
List<Item2> items = cell.getItems();
int num = items.size();
int itemY = y;
for (int i = 0; i < num; i++) {
Item2 item = items.get(i);
if (insertionInRow && insertion.getIndex() == i) {
Rectangle r = insertion.createRectangle(x, y);
info.add(r);
itemY += r.height + itemSpacing;
}
IBranchPart child = item.getBranch();
IFigure childFigure = child.getFigure();
Dimension size = childFigure.getPreferredSize();
int bh = 0;
if (!branch.getBoundaries().isEmpty()) {
for (IBoundaryPart boundary : branch.getBoundaries()) {
List<IBranchPart> enclosingBranches = boundary
.getEnclosingBranches();
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);
}
List<ITopic> topics = boundary.getBoundary()
.getEnclosingTopics();
if (topics.size() > 1) {
itemY += bh;
}
bh = 0;
}
if ((!enclosingBranches.isEmpty())
&& child.equals(enclosingBranches.get(
enclosingBranches.size() - 1))) {
bh = boundary.getFigure().getInsets().bottom;
}
}
}
Rectangle childBounds = new Rectangle(x, itemY, size.width,
size.height + 10);
info.put(childFigure, childBounds);
itemY += size.height + itemSpacing + bh;
}
if (insertionInRow && insertion.getIndex() == num) {
info.add(insertion.createRectangle(x, y));
}
} else if (insertionInRow) {
info.add(insertion.createRectangle(x, y));
}
y += row.getPrefCellHeight() + cellSpacing + lineWidth;
}
if (rowIns != null && rowIns.getIndex() == rows.size()) {
info.add(new Rectangle(startX, y, rowIns.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)) {
Column2 row = getColumn(branch);
if (!row.getCells().isEmpty()) {
Cell2 cell = row.getCells().get(0);
if (!cell.getItems().isEmpty()) {
Item2 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)) {
Column2 row = getColumn(branch);
Cell2 cell = row.findCellByItem(sourceChild);
if (cell != null) {
Cell2 prev = row.getPreviousCell(cell);
if (prev == null) {
if (!sequential)
return branch.getTopicPart();
}
}
} else if (GEF.REQ_NAV_UP.equals(navReqType)) {
Column2 row = getColumn(branch);
Item2 item = row.findItem(sourceChild);
if (item != null) {
Item2 prev = item.getPreviousItem();
if (prev != null)
return prev.getBranch().getTopicPart();
}
} else if (GEF.REQ_NAV_DOWN.equals(navReqType)) {
Column2 row = getColumn(branch);
Item2 item = row.findItem(sourceChild);
if (item != null) {
Item2 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) {
Column2 row = getColumn(branch);
Item2 startItem = row.findItem(startChild);
if (startItem != null) {
Cell2 cell = startItem.getOwnedCell();
Item2 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;) {
Item2 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();
Column2 col = getColumn(branch);
Chart2 chart = col.getOwnedChart();
int rowX = col.getLeft();
int rowWidth = col.getWidth();
if (pos.x > rowX && pos.x < rowX + rowWidth) {
if (!chart.hasRows()) {
int y = chart.getTitle().getFigure().getBounds().y;
int h = chart.getTitle().getFigure().getBounds().height;
int childY = key.getFigure().getBounds().y;
if (childY > y && childY < y + h)
return 0;
}
Cell2 cell = col.findCell(pos);
if (cell != null) {
if (cell.getItems().isEmpty())
return 0;
int colY = cell.getOwnedRow().getTop();
int offset = pos.y - colY;
int index = 0;
int last = cell.getItems().size() - 1;
for (Item2 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) {
Column2 col = getColumn(branch);
Chart2 chart = col.getOwnedChart();
Point pos = key.getCursorPos();
Cell2 cell = col.findCell(pos);
if (cell == null || cell.getItems().isEmpty())
return -1;
Dimension insSize = getInsSize(key.getFigure());
int y = cell.getY() + chart.getMinorSpacing() / 2;
int insHeight = insSize.height;
int spacing = col.getMinorSpacing();
int disabled = 0;
for (Item2 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) {
Column2 column = getColumn(branch);
Chart2 chart = column.getOwnedChart();
Point pos = key.getCursorPos();
Cell2 cell = column.findCell(pos);
if (cell == null || cell.getItems().isEmpty())
return -1;
Dimension insSize = getInsSize(key.getFigure());
int y = cell.getY() + chart.getMinorSpacing() / 2;
int insHeight = insSize.height;
int spacing = column.getMinorSpacing();
int disabled = 0;
for (Item2 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) {
Column2 col = getColumn(branch);
Chart2 chart = col.getOwnedChart();
Point pos = key.getCursorPos();
Cell2 cell = col.findCell(pos);
if (cell == null)
return null;
Dimension insSize = getInsSize(key.getFigure());
if (cell.getItems().isEmpty()) {
return new CellInsertion2(branch, -1, insSize,
cell.getOwnedRow().getHead());
}
int y = cell.getY() + chart.getMinorSpacing() / 2;
int insHeight = insSize.height;
int spacing = col.getMinorSpacing();//getMinorSpacing(branch);
int index = 0;
for (Item2 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 CellInsertion2(branch, index, insSize,
cell.getOwnedRow().getHead());
}
y += itemSize.height + spacing;
if (withDisabled || itemBranch.getFigure().isEnabled())
index++;
}
return new CellInsertion2(branch, withDisabled ? index : -1, insSize,
cell.getOwnedRow().getHead());
}
private Dimension getInsSize(IReferencedFigure child) {
return child.getSize();
}
public void decorateMoveInRequest(IBranchPart targetParent,
ParentSearchKey childKey, IBranchPart sourceParent,
Request request) {
RowHead rowHead = (RowHead) MindMapUtils.getCache(targetParent,
Spreadsheet.KEY_INSERTION_ROW_HEAD);
if (rowHead != null) {
request.setParameter(MindMapUI.PARAM_PROPERTY_PREFIX + Core.Labels,
new HashSet<String>(rowHead.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) {
Column2 col = getColumn(branch);
Cell2 cell = col.findCellByItem(sourceChild);
if (cell != null) {
RowHead rowHead = cell.getOwnedRow().getHead();
request.setParameter(MindMapUI.PARAM_PROPERTY_PREFIX + Core.Labels,
new HashSet<String>(rowHead.getLabels()));
}
}
public int getQuickMoveOffset(IBranchPart branch, IBranchPart child,
int direction) {
if (direction == PositionConstants.EAST) {
Column2 col = getColumn(branch);
Item2 item = col.findItem(child);
if (item != null) {
Item2 next = item.getNextItem();
if (next != null)
return next.getBranch().getBranchIndex()
- child.getBranchIndex();
}
} else if (direction == PositionConstants.WEST) {
Column2 col = getColumn(branch);
Item2 item = col.findItem(child);
if (item != null) {
Item2 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) {
Column2 col = getColumn(branch);
Cell2 cell = col.findCell(key.getCursorPos());
if (cell == null) {
return calcFirstChildPosition(branch, key);
}
List<Item2> items = cell.getItems();
Dimension inventSize = key.getInvent().getSize();
Dimension insSize = key.getFigure().getSize();
if (items.isEmpty())
return new Point(getFigureLocation(branch.getFigure()).x,
cell.getY() + (insSize.height < cell.getHeight()
? insSize.height / 2 : cell.getHeight() / 2));
IInsertion insertion = calcInsertion(branch, key);
int index = insertion == null ? -1 : insertion.getIndex();
IBranchPart sub = index == items.size()
? items.get(items.size() - 1).getBranch()
: items.get(index).getBranch();
int deltaY = (insSize.height + sub.getFigure().getSize().height) / 2;
return getFigureLocation(sub.getFigure()).getTranslated(
(inventSize.width
- sub.getTopicPart().getFigure().getSize().width) / 2,
index == 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--;
}
}
Column2 col = getColumn(branch);
Cell2 cell = col.findCell(key.getCursorPos());
List<Item2> items = cell.getItems();
Dimension inventSize = key.getInvent().getSize();
Dimension insSize = key.getFigure().getSize();
if (items.isEmpty())
return new Point(getFigureLocation(branch.getFigure()).x,
cell.getY() + (insSize.height < cell.getHeight()
? insSize.height / 2 : cell.getHeight() / 2));
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(col.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) {
Column2 col = getColumn(branch);
if (branch.equals(child.getParentBranch())) {
Cell2 cell = col.findCellByItem(child);
int count = 0;
for (int i = 0; i < col.getCells().indexOf(cell); i++)
count += col.getCells().get(i).getItems().size();
Item2 item = col.findItem(child);
return count + cell.getItems().indexOf(item);
} else {
int index = 0;
for (Cell2 cell : col.getCells()) {
for (Item2 item : cell.getItems()) {
if (!item.getBranch().getFigure().isEnabled())
return index;
index++;
}
}
}
return -1;
}
protected List<Integer> getDisableBranches(IBranchPart branch) {
List<Integer> disables = null;
Column2 col = getColumn(branch);
int index = 0;
for (Cell2 cell : col.getCells()) {
for (Item2 item : cell.getItems()) {
if (!item.getBranch().getFigure().isEnabled()) {
if (disables == null)
disables = new ArrayList<Integer>();
disables.add(index);
}
index++;
}
}
return disables;
}
}