/* * 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.decorations; import java.util.ArrayList; import java.util.List; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.widgets.Display; import org.xmind.gef.draw2d.IDecoratedFigure; import org.xmind.gef.draw2d.decoration.AbstractDecoration; import org.xmind.gef.draw2d.decoration.ICorneredDecoration; import org.xmind.gef.draw2d.decoration.IDecoration; import org.xmind.gef.draw2d.geometry.PrecisionLine; import org.xmind.gef.draw2d.geometry.PrecisionRectangle; import org.xmind.gef.draw2d.graphics.Path; import org.xmind.gef.graphicalpolicy.IStructure; import org.xmind.gef.graphicalpolicy.IStyleSelector; import org.xmind.gef.part.IGraphicalPart; import org.xmind.ui.branch.IInsertion; import org.xmind.ui.decorations.IBranchDecoration; import org.xmind.ui.decorations.ITopicDecoration; import org.xmind.ui.internal.spreadsheet.Spreadsheet; import org.xmind.ui.internal.spreadsheet.structures.Chart; import org.xmind.ui.internal.spreadsheet.structures.Column; import org.xmind.ui.internal.spreadsheet.structures.ColumnHead; import org.xmind.ui.internal.spreadsheet.structures.Row; import org.xmind.ui.internal.spreadsheet.structures.SpreadsheetStructure; import org.xmind.ui.mindmap.IBranchPart; import org.xmind.ui.mindmap.ITitleTextPart; import org.xmind.ui.mindmap.ITopicPart; import org.xmind.ui.mindmap.MindMapUI; import org.xmind.ui.resources.ColorUtils; import org.xmind.ui.style.StyleUtils; import org.xmind.ui.style.Styles; import org.xmind.ui.util.MindMapUtils; public class SpreadsheetBranchDecoration extends AbstractDecoration implements IBranchDecoration { private static final Rectangle CLIP_RECT = new Rectangle(); private static final int INSERTION_ALPHA = 0x60; static class Block { PrecisionRectangle bounds; IGraphicalPart part; int alpha = 0xff; public Block(PrecisionRectangle bounds, IGraphicalPart part) { this.bounds = bounds; this.part = part; } public void paint(Graphics graphics, Path path, int alpha, Rectangle clipRect) { if (!bounds.intersects(clipRect)) return; Color fillColor = getFillColor(); if (fillColor == null) return; graphics.clipRect(bounds.toDraw2DRectangle()); graphics.setBackgroundColor(fillColor); graphics.setAlpha(alpha * this.alpha / 0xff); graphics.fillPath(path); graphics.restoreState(); } private Color getFillColor() { if (part == null) // insertion return ColorConstants.gray; IStyleSelector ss = StyleUtils.getStyleSelector(part); String decorationId = StyleUtils.getString(part, ss, Styles.ShapeClass, null); return StyleUtils.getColor(part, ss, Styles.FillColor, decorationId, null); } } private static class Text { PrecisionRectangle bounds; String text; Font font; Point textLocation; public Text(PrecisionRectangle bounds, String text, Point textLocation) { this.bounds = bounds; this.text = text; this.textLocation = textLocation; } public void paint(Graphics graphics, Rectangle clipRect) { if (!bounds.intersects(clipRect)) return; graphics.clipRect(bounds.toDraw2DRectangle()); if (font != null) graphics.setFont(font); graphics.drawText(text, textLocation); graphics.restoreState(); } } private IBranchPart branch; private PrecisionRectangle bounds; private List<Block> blocks; private List<PrecisionLine> lines; private PrecisionRectangle insertedCellBounds; private List<Text> columnHeads; public SpreadsheetBranchDecoration(IBranchPart branch, String id) { super(id); this.branch = branch; } public void validate(IFigure figure) { super.validate(figure); IStyleSelector ss = StyleUtils.getStyleSelector(branch); String decorationId = StyleUtils.getString(branch, ss, Styles.ShapeClass, null); int lineWidth = StyleUtils.getInteger(branch, ss, Styles.LineWidth, decorationId, 1); double halfLineWidth1 = lineWidth / 2.0; double halfLineWidth2 = lineWidth - halfLineWidth1; bounds = new PrecisionRectangle(branch.getFigure().getBounds()) .shrink(lineWidth, lineWidth); double left = bounds.x; double right = bounds.right(); double top = bounds.y; double bottom = bounds.bottom(); double y; boolean ignoreFirstLine; Chart chart = getChart(); if (chart != null) { int titleHeight = chart.getTitleAreaHeight(); if (titleHeight > 0) { y = top + titleHeight; ignoreFirstLine = false; top = y + halfLineWidth1; } else { y = top - halfLineWidth1; ignoreFirstLine = true; } IInsertion ins = ((IInsertion) MindMapUtils.getCache(branch, IInsertion.CACHE_INSERTION)); int insHeight = ins == null ? 0 : ins.getSize().height + chart.getMajorSpacing(); int numRows = chart.getNumRows(); int numCols = numRows > 0 ? chart.getNumColumns() : 0; int numLines = Math.max(0, numCols) + Math.max(0, numRows); if (ins != null) numLines++; if (numLines > 0) { lines = new ArrayList<PrecisionLine>(numLines); double colHeadTop; double colHeadHeight; if (chart.hasColumns()) { if (!ignoreFirstLine) { addHorizontalLine(left, right, y + halfLineWidth1); } ignoreFirstLine = false; colHeadTop = y + lineWidth; colHeadHeight = chart.getColumnHeadHeight() + chart.getMajorSpacing(); y += lineWidth + colHeadHeight; } else { colHeadTop = 0; colHeadHeight = 0; } int minorSpacing = chart.getMinorSpacing(); ColumnHead insertionColHead = null; boolean insertionInRow = false; for (int i = 0; i < numRows; i++) { Row row = chart.getRow(i); if (insertionColHead == null) { insertionColHead = (ColumnHead) MindMapUtils.getCache( row.getHead(), Spreadsheet.KEY_INSERTION_COLUMN_HEAD); if (insertionColHead != null) { insertionInRow = true; insertedCellBounds = new PrecisionRectangle(); } } if (!ignoreFirstLine) { addHorizontalLine(left, right, y + halfLineWidth1); if (insertionInRow) { insertedCellBounds.y = y + halfLineWidth1; } } ignoreFirstLine = false; y += lineWidth; if (ins != null && i == ins.getIndex()) { Block block = addBlock(null, new PrecisionRectangle( left, y, right - left, insHeight)); block.alpha = INSERTION_ALPHA; y += insHeight; addHorizontalLine(left, right, y + halfLineWidth1); y += lineWidth; ins = null; } int rowHeight; if (i == numRows - 1 && ins == null) { rowHeight = (int) Math.ceil(bottom - y); } else { rowHeight = row.getHead().getFigure().getBounds().height + minorSpacing; } addBlock(row.getHead(), new PrecisionRectangle(left, y, right - left, rowHeight)); y += rowHeight; if (insertionInRow) { insertedCellBounds.height = y + halfLineWidth1 - insertedCellBounds.y; insertionInRow = false; } } if (ins != null && ins.getIndex() == numRows) { addHorizontalLine(left, right, y + halfLineWidth1); Block block = addBlock(null, new PrecisionRectangle(left, y, right - left, (int) Math.ceil(bottom - y))); block.alpha = INSERTION_ALPHA; } double x = left + halfLineWidth2 + chart.getRowHeadWidth() + minorSpacing; IInsertion colIns = (IInsertion) MindMapUtils.getCache(branch, Spreadsheet.CACHE_COLUMN_INSERTION); for (int i = 0; i < numCols; i++) { if (colIns != null && colIns.getIndex() == i) { int colInsWidth = colIns.getSize().width + chart.getMinorSpacing() + lineWidth; Block block = addBlock(null, new PrecisionRectangle(x + halfLineWidth1, top, colInsWidth, bottom - top)); block.alpha = INSERTION_ALPHA; addVerticalLine(x + halfLineWidth1, top, bottom); x += colInsWidth; } Column col = chart.getColumn(i); ColumnHead colHead = col.getHead(); boolean insertionInColumn = insertionColHead != null && insertionColHead.equals(colHead) && insertedCellBounds != null; if (insertionInColumn) { insertedCellBounds.x = x + halfLineWidth1; } addVerticalLine(x + halfLineWidth1, top, bottom); double columnWidth = col.getWidth();//getPrefCellWidth() + minorSpacing; PrecisionRectangle colHeadBounds = new PrecisionRectangle( x + lineWidth, colHeadTop, columnWidth, colHeadHeight); String text = colHead.toString(); Dimension size = colHead.getPrefSize(); Text colHeadText = addColumnHeadText(colHeadBounds, text, center(colHeadBounds, size.width, size.height)); colHeadText.font = colHead.getFont(); x += lineWidth + columnWidth; if (insertionInColumn) { if (i == numCols - 1) { insertedCellBounds.width = right - insertedCellBounds.x; } else { insertedCellBounds.width = x + halfLineWidth1 - insertedCellBounds.x; } } } if (colIns != null && colIns.getIndex() == numCols) { addVerticalLine(x + halfLineWidth1, top, bottom); int colInsWidth = colIns.getSize().width + chart.getMinorSpacing() + lineWidth; Block block = addBlock(null, new PrecisionRectangle(x + halfLineWidth1, top, colInsWidth, bottom - top)); block.alpha = INSERTION_ALPHA; } } } } private Point center(PrecisionRectangle bounds, int width, int height) { double x = bounds.x + (bounds.width - width) / 2; double y = bounds.y + (bounds.height - height) / 2; return new Point((int) x, (int) y); } List<Block> getBlocks() { return blocks; } private Block addBlock(IGraphicalPart part, PrecisionRectangle bounds) { if (blocks == null) blocks = new ArrayList<Block>(); Block block = new Block(bounds, part); blocks.add(block); return block; } private Text addColumnHeadText(PrecisionRectangle bounds, String text, Point textLocation) { if (columnHeads == null) columnHeads = new ArrayList<Text>(); Text columnHeadText = new Text(bounds, text, textLocation); columnHeads.add(columnHeadText); return columnHeadText; } private void addHorizontalLine(double x1, double x2, double y) { lines.add(new PrecisionLine(x1, y, x2, y)); } private void addVerticalLine(double x, double y1, double y2) { lines.add(new PrecisionLine(x, y1, x, y2)); } protected int getMinorSpacing() { return StyleUtils.getInteger(branch, StyleUtils.getStyleSelector(branch), Styles.MinorSpacing, 5); } protected int getMajorSpacing() { return StyleUtils.getMajorSpacing(branch, 5); } private Chart getChart() { IStructure sa = branch.getBranchPolicy().getStructure(branch); if (sa instanceof SpreadsheetStructure) return ((SpreadsheetStructure) sa).getChart(branch); return null; } private ITopicDecoration getTopicDecoration() { IFigure topicFigure = getTopicFigure(); if (topicFigure instanceof IDecoratedFigure) { IDecoration decoration = ((IDecoratedFigure) topicFigure) .getDecoration(); if (decoration instanceof ITopicDecoration) return ((ITopicDecoration) decoration); } return null; } private IFigure getTopicFigure() { ITopicPart topicPart = branch.getTopicPart(); return topicPart == null ? null : topicPart.getFigure(); } public void invalidate() { super.invalidate(); bounds = null; columnHeads = null; blocks = null; lines = null; insertedCellBounds = null; } private Color getTextColor() { ITopicPart topicPart = branch.getTopicPart(); if (topicPart != null) { ITitleTextPart title = topicPart.getTitle(); if (title != null) return title.getFigure().getForegroundColor(); } return null; } protected void performPaint(IFigure figure, Graphics graphics) { graphics.setAntialias(SWT.ON); ITopicDecoration topicDecoration = getTopicDecoration(); int fillAlpha; int corner; Color fillColor; if (topicDecoration != null) { fillAlpha = topicDecoration.getFillAlpha(); fillColor = topicDecoration.getFillColor(); corner = getCornerSize(topicDecoration); } else { fillColor = null; fillAlpha = 0xff; corner = 0; } if (bounds != null) { graphics.pushState(); int alpha = getAlpha() * fillAlpha / 0xff; Path path = new Path(Display.getCurrent()); addOutline(path, bounds, corner); try { if (fillColor != null) { graphics.setAlpha(alpha); graphics.setBackgroundColor(fillColor); graphics.fillPath(path); graphics.restoreState(); } if (blocks != null && !blocks.isEmpty()) { for (Block block : blocks) { block.paint(graphics, path, alpha, graphics.getClip(CLIP_RECT)); } } if (columnHeads != null && !columnHeads.isEmpty()) { Color textColor = getTextColor(); for (Text head : columnHeads) { graphics.setTextAntialias(SWT.ON); graphics.setForegroundColor(textColor); head.paint(graphics, graphics.getClip(CLIP_RECT)); } } } finally { path.dispose(); graphics.popState(); } } } private void addOutline(Path path, PrecisionRectangle bounds, int corner) { if (corner == 0) { path.addRectangle(bounds); } else { path.addRoundedRectangle(bounds, corner); } } private int getCornerSize(ITopicDecoration topicDecoration) { int corner; if (topicDecoration instanceof ICorneredDecoration) { corner = ((ICorneredDecoration) topicDecoration).getCornerSize(); } else { corner = 0; } return corner; } public void paintAboveChildren(IFigure figure, Graphics graphics) { if (!isVisible()) return; checkValidation(figure); ITopicDecoration topicDecoration = getTopicDecoration(); if (topicDecoration == null) return; Color lineColor = topicDecoration.getLineColor(); if (lineColor == null) return; int lineAlpha = topicDecoration.getLineAlpha(); int lineWidth = topicDecoration.getLineWidth(); int lineStyle = topicDecoration.getLineStyle(); // int corner = getCornerSize(topicDecoration); int corner = 2; graphics.setAntialias(SWT.ON); if (bounds != null || (lines != null && !lines.isEmpty()) || insertedCellBounds != null) { graphics.setAlpha(getAlpha() * lineAlpha / 0xff); graphics.setLineWidth(lineWidth); graphics.setLineStyle(lineStyle); graphics.setForegroundColor(lineColor); Path path = new Path(Display.getCurrent()); if (bounds != null) { addOutline(path, bounds, corner); } if (lines != null && !lines.isEmpty()) { for (PrecisionLine line : lines) { path.moveTo(line.getOrigin()); path.lineTo(line.getTerminus()); } } graphics.drawPath(path); path.dispose(); if (insertedCellBounds != null) { graphics.setAlpha(0x80); graphics.setLineWidth(lineWidth + 2); graphics.setForegroundColor( ColorUtils.getColor(MindMapUI.COLOR_WARNING)); path = new Path(Display.getCurrent()); path.addRectangle(insertedCellBounds); graphics.drawPath(path); path.dispose(); } } } }