/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g2d.diagram.handler.layout; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.diagram.handler.LayoutManager; import org.simantics.g2d.element.ElementUtils; import org.simantics.g2d.element.IElement; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; public class FlowLayout implements LayoutManager { /** Diagram hint, Row justification */ public static enum Align {Left, Center, Right, Fill} /** * The align specifies the alignment of elements * in a row. */ public static final Key ALIGN = new KeyOf(Align.class, "FlowLayout.ALIGN"); /** * The horizontal gap specifies the space between * elements and between an element and borders. */ public static final Key HGAP = new KeyOf(Double.class, "FlowLayout.HGAP"); /** * The vertical gap specifies the space between * elements and between an element and borders. */ public static final Key VGAP = new KeyOf(Double.class, "FlowLayout.VGAP"); @Override public void layout(IDiagram diagram, Rectangle2D bounds) { Align align = diagram.getHint(ALIGN); Double hgap = diagram.getHint(HGAP); Double vgap = diagram.getHint(VGAP); if (align==null) align = Align.Left; if (hgap==null) hgap = 0.; if (vgap==null) vgap = 0.; Map pos = computePositions(diagram, bounds, align, hgap, vgap); for (Entry e : pos.entrySet()) { ElementUtils.setPos(e.getKey(), e.getValue().getMinX(), e.getValue().getMinY()); } } @Override public Rectangle2D computeSize(IDiagram diagram, Double wHint, Double hHint) { Rectangle2D bounds = new Rectangle2D.Double(); Align align = diagram.getHint(ALIGN); Double hgap = diagram.getHint(HGAP); Double vgap = diagram.getHint(VGAP); if (align==null) align = Align.Left; if (hgap==null) hgap = 0.; if (vgap==null) vgap = 0.; if (wHint==null) wHint = Double.MAX_VALUE; if (hHint==null) hHint = Double.MAX_VALUE; bounds.setFrame(0, 0, wHint, hHint); Map pos = computePositions(diagram, bounds, align, hgap, vgap); double minX = Double.MAX_VALUE, minY = Double.MAX_VALUE, maxX = -Double.MAX_VALUE, maxY = -Double.MAX_VALUE; for (IElement e : pos.keySet()) { Rectangle2D b = pos.get(e); if (b.getMinX() < minX) minX = b.getMinX(); if (b.getMinY() < minY) minY = b.getMinY(); if (b.getMaxX() > maxX) maxX = b.getMaxX(); if (b.getMaxY() > maxY) maxY = b.getMaxY(); } return new Rectangle2D.Double(minX, minY, maxX-minX, maxY-minY); } private Map computePositions(IDiagram diagram, Rectangle2D bounds, Align align, double hgap, double vgap) { double width = bounds.getWidth(); // Must use diagram.getSnapshot since we are not guaranteed to be in the // diagram's canvas context thread. Collection elements = diagram.getSnapshot(); Map rects = ElementUtils.getElementBoundsOnDiagram(elements, null); // Rows List> rows = new ArrayList>(); // Divide elements to rows List row = new ArrayList(); rows.add(row); double rowWidth = 0.0; for (IElement e : elements) { Rectangle2D elementBounds = rects.get(e); if (row.isEmpty()) { row.add(e); rowWidth += elementBounds.getWidth(); continue; } // Add element if it fits to the row if (rowWidth + elementBounds.getWidth() + hgap*(row.size()) < width) { rowWidth += elementBounds.getWidth(); row.add(e); continue; } // Create a new row rows.add( row = new ArrayList() ); row.add(e); rowWidth = elementBounds.getWidth(); } // Flush rows double y = bounds.getMinY(); double hmargin = hgap; for (List row_ : rows) { double rowHeight = 0; // Calc row width double rowContentWidth = (row.size()-1) * hgap; for (IElement e : row) rowContentWidth += rects.get(e).getWidth(); double x = hgap + bounds.getMinX(); double _hgap = hgap; if (align == Align.Center) x = (width - hmargin - hmargin - rowContentWidth) / 2; if (align == Align.Left) x = hmargin; if (align == Align.Right) x = width-rowContentWidth-hmargin; if (align == Align.Fill) { x = hmargin; _hgap = (width - hmargin - hmargin - rowContentWidth) / (row.size()+1); //_hgap = 2; } for (IElement e : row_) { Rectangle2D rect = rects.get(e); rect.setFrame(x, y, rect.getWidth(), rect.getHeight()); x += rect.getWidth() + _hgap; rowHeight = Math.max(rowHeight, rect.getHeight()); } x = bounds.getMinX(); y += rowHeight + vgap; } return rects; } }