/**
* Copyright 2004-2016 Riccardo Solmi. All rights reserved.
* This file is part of the Whole Platform.
*
* The Whole Platform is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Whole Platform is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>.
*/
package org.whole.lang.ui.notations.styledtree.figures;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Toggle;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.whole.lang.ui.WholeImages;
import org.whole.lang.ui.figures.AnchorFactory;
import org.whole.lang.ui.figures.EntityFigure;
import org.whole.lang.ui.figures.EntityToggle;
import org.whole.lang.ui.figures.FigureConstants;
import org.whole.lang.ui.figures.IEntityFigure;
import org.whole.lang.ui.figures.INodeFigure;
import org.whole.lang.ui.figures.LabelFactory;
import org.whole.lang.ui.figures.NodeFigure;
import org.whole.lang.ui.figures.TableFigure;
import org.whole.lang.ui.figures.TableRowFigure;
import org.whole.lang.ui.layout.Alignment;
import org.whole.lang.ui.layout.ColumnLayout;
import org.whole.lang.ui.layout.MonoLayout;
import org.whole.lang.ui.layout.RowLayout;
import org.whole.lang.ui.layout.TableLayout;
import org.whole.lang.ui.layout.ViewportTracking;
import org.whole.lang.ui.notations.figures.DrawUtils;
import org.whole.lang.ui.notations.styledtree.styling.IEntityStyling;
import org.whole.lang.ui.notations.styledtree.styling.IFeatureStyling;
/**
* @author Riccardo Solmi
*/
public class SimpleTableNodeWithBranchesFigure extends NodeFigure {
protected IEntityFigure nodeFigure;
protected IEntityFigure titleFigure;
protected TableFigure tableFigure;
protected IEntityFigure contentsFigure;
protected Toggle[] featureToggles;
protected IFeatureStyling[] featuresStyling;
protected int typePaneShift;
public SimpleTableNodeWithBranchesFigure(boolean isAdaptive, IEntityStyling entityStyling) {
typePaneShift = isAdaptive ? 1 : 0;
featuresStyling = entityStyling.getFeaturesStyling();
initContentPanes(typePaneShift+featuresStyling.length);
tableFigure = new TableFigure(new TableLayout(3).withColumnAlignment(0, Alignment.TRAILING)
.withColumnSpacing(8).withMarginTop(2).withMarginBottom(2)) {
protected void paintFigure(Graphics g) {
super.paintFigure(g);
Rectangle b = getBounds();
if (entityStyling.getEmbeddedFeaturesSize() < featuresStyling.length)
b = b.getResized(-WholeImages.ROUND_EXPAND.getBounds().width/2, 0);
g.setClip(b);
g.setBackgroundColor(ColorConstants.lightGray);
drawColumnBackground(g, 0);
g.setForegroundColor(FigureConstants.blueColor);
drawHeadersRowSeparator(g);
g.setForegroundColor(ColorConstants.lightGray);
drawColumnSeparatorBefore(g, 1);
int oldAlpha = g.getAlpha();
g.setAlpha(80);
drawRowSeparators(g);
g.setAlpha(oldAlpha);
}
};
if (isAdaptive) {
titleFigure = createContentPane(0, ViewportTracking.HORIZONTAL);
titleFigure.getLayoutManager().withMargin(2, 4, 2, 8);
} else {
titleFigure = new EntityFigure(new MonoLayout().withMargin(2, 4, 2, 8).withAutoresizeWeight(1.0f))
.withViewportTracking(ViewportTracking.HORIZONTAL);
titleFigure.add(LabelFactory.createDeclaration(entityStyling.getTypeLabel()));
}
nodeFigure = new EntityFigure(new ColumnLayout()
.withMinorAlignment(Alignment.TRAILING)) {
protected void paintFigure(Graphics g) {
super.paintFigure(g);
Rectangle b = getBounds();
int gap = 0;
if (entityStyling.getEmbeddedFeaturesSize() < featuresStyling.length)
b = b.getResized(gap = -WholeImages.ROUND_EXPAND.getBounds().width/2, 0);
int oldAlpha = g.getAlpha();
g.setBackgroundColor(ColorConstants.lightGray);
Rectangle bf = b.getResized(-tableFigure.getBounds().width -gap+2, -titleFigure.getBounds().height);
bf.y += titleFigure.getBounds().height;
if (bf.width > 1) {
Rectangle oldClip = g.getClip(new Rectangle());
g.setAlpha(60);
g.setClip(bf);
g.fillRoundRectangle(bf, 8, 8);
g.setClip(oldClip);
g.setAlpha(oldAlpha);
g.setForegroundColor(FigureConstants.blueColor);
g.drawLine(bf.x, bf.y, bf.right()-1, bf.y);
}
g.setForegroundColor(FigureConstants.blueColor);
g.drawRoundRectangle(b.getResized(-1, -1), 8, 8);
// int oldAlpha = g.getAlpha();
g.setAlpha(60);
Rectangle b2 = b.getCopy();
b2.height = titleFigure.getBounds().height;
g.setBackgroundColor(FigureConstants.blueColor);
g.setClip(b2);
g.fillRoundRectangle(b2.getResized(0, 4), 8, 8);
g.setAlpha(oldAlpha);
}
};
nodeFigure.add(titleFigure);
nodeFigure.add(tableFigure);
contentsFigure = new EntityFigure(new ColumnLayout().withSpacing(8).withMarginLeft(2));
featureToggles = new Toggle[featuresStyling.length-entityStyling.getEmbeddedFeaturesSize()];
for (int i=0, ti=0; i<featuresStyling.length; i++) {
TableRowFigure row = new TableRowFigure();
row.getLayoutManager().withMarginLeft(4);
String name = featuresStyling[i].getName();
if (name.matches("\\d+"))
row.addContentLight(name);
else
row.addLabel(name);
if (featuresStyling[i].isEmbedded()) {
row.add(createContentPane(typePaneShift+i));
row.addEmptyLabel();
} else {
if (entityStyling.getEmbeddedFeaturesSize() > 0)
row.addEmptyLabel();
row.add(featureToggles[ti++] = createFoldingToggle(new EntityToggle(WholeImages.ROUND_EXPAND, WholeImages.ROUND_COLLAPSE), typePaneShift+i));
contentsFigure.add(createContentPane(typePaneShift+i));
}
tableFigure.add(row);
}
EntityFigure trackingFigure = new EntityFigure(new MonoLayout().withAutoresizeWeight(1f)).withViewportTracking(ViewportTracking.VERTICAL);
trackingFigure.add(nodeFigure);
add(trackingFigure);
add(contentsFigure);
setLayoutManager(new RowLayout().withMargin(2, 4, 2, 0).withSpacing(
contentsFigure.getChildren().size()*4));
}
//TODO rename internal source anchors
@Override
protected ConnectionAnchor[] createSourceAnchors() {
ConnectionAnchor[] connectionAnchors = new ConnectionAnchor[featureToggles.length];
for(int i=0; i<featureToggles.length; i++)
connectionAnchors[i] = AnchorFactory.createRightMiddleAnchor(featureToggles[i]);
return connectionAnchors;
}
@Override
protected ConnectionAnchor[] createTargetAnchors() {
return new ConnectionAnchor[] {
AnchorFactory.createLeftMiddleAnchor(nodeFigure)
};
}
@Override
public void paintClientArea(Graphics graphics) {
super.paintClientArea(graphics);
paintConnections(graphics);
graphics.restoreState();
}
protected void paintConnections(Graphics g) {
g.setForegroundColor(FigureConstants.relationsColor);
int egdeXOffset = DrawUtils.SPACING - DrawUtils.EDGE_SPACING;
ConnectionAnchor[] srcAnchors = getSourceAnchors();
int i, j, pi=0;
int prevYSourceLocation = Integer.MAX_VALUE;
for (i=0, pi=0; i<srcAnchors.length; i++, pi++) {
while (featuresStyling[pi].isEmbedded())
pi++;
IFigure contentPane = getContentPane(typePaneShift+pi);
if (contentPane.isVisible()) {
//TODO test if needed
if (contentPane.getChildren().isEmpty())
return;
Point sourceLocation = srcAnchors[i].getLocation(null);
int yMax = 0;
IFigure targetFigure = (IFigure) contentPane.getChildren().get(0);
List<Point> targetLocations = new ArrayList<>();
if (targetFigure instanceof INodeFigure) {
INodeFigure targetNode = (INodeFigure) targetFigure;
for (int ti=0; ti<targetNode.getTargetAnchorsSize(); ti++) {
Point t = targetNode.getTargetAnchor(ti).getLocation(sourceLocation);
translateToRelative(t);
targetLocations.add(t);
yMax = Math.max(yMax, t.y);
}
} else {
Point t = targetFigure.getBounds().getLeft();
targetFigure.translateToAbsolute(t);
translateToRelative(t);
targetLocations.add(t);
yMax = Math.max(yMax, t.y);
}
translateToRelative(sourceLocation);
if (yMax > sourceLocation.y)
break;
if (prevYSourceLocation >= yMax - 1)
egdeXOffset += DrawUtils.EDGE_SPACING;
prevYSourceLocation = sourceLocation.y;
DrawUtils.drawHorizontalTree(g, sourceLocation, egdeXOffset, targetLocations.toArray(new Point[targetLocations.size()]));
}
}
egdeXOffset = DrawUtils.SPACING - DrawUtils.EDGE_SPACING;
prevYSourceLocation = Integer.MIN_VALUE;
for (j=srcAnchors.length-1, pi=featuresStyling.length-1; j >= i; j--, pi--) {
while (featuresStyling[pi].isEmbedded())
pi--;
IFigure contentPane = getContentPane(typePaneShift+pi);
if (contentPane.isVisible()) {
//TODO test if needed
if (contentPane.getChildren().isEmpty())
return;
Point sourceLocation = srcAnchors[j].getLocation(null);
int yMax = 0;
IFigure targetFigure = (IFigure) contentPane.getChildren().get(0);
List<Point> targetLocations = new ArrayList<>();
if (targetFigure instanceof INodeFigure) {
INodeFigure targetNode = (INodeFigure) targetFigure;
for (int ti=0; ti<targetNode.getTargetAnchorsSize(); ti++) {
Point t = targetNode.getTargetAnchor(ti).getLocation(sourceLocation);
translateToRelative(t);
targetLocations.add(t);
yMax = Math.max(yMax, t.y);
}
} else {
Point t = targetFigure.getBounds().getLeft();
targetFigure.translateToAbsolute(t);
translateToRelative(t);
targetLocations.add(t);
yMax = Math.max(yMax, t.y);
}
translateToRelative(sourceLocation);
if (prevYSourceLocation <= yMax + 1)
egdeXOffset += DrawUtils.EDGE_SPACING;
prevYSourceLocation = sourceLocation.y;
DrawUtils.drawHorizontalTree(g, sourceLocation, egdeXOffset, targetLocations.toArray(new Point[targetLocations.size()]));
}
}
}
}