1 /*******************************************************************************
\r
2 * Copyright (c) 2016 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * Semantum Oy - initial API and implementation, courtesy of Rob Camick
\r
11 * https://tips4java.wordpress.com/2008/11/06/wrap-layout/
\r
12 *******************************************************************************/
\r
13 package org.simantics.utils.ui.awt;
\r
15 import java.awt.Component;
\r
16 import java.awt.Container;
\r
17 import java.awt.Dimension;
\r
18 import java.awt.FlowLayout;
\r
19 import java.awt.Insets;
\r
21 import javax.swing.JScrollPane;
\r
22 import javax.swing.SwingUtilities;
\r
25 * FlowLayout subclass that fully supports wrapping of components.
\r
27 public class WrapLayout extends FlowLayout
\r
32 private static final long serialVersionUID = -4312800883442354753L;
\r
35 * Constructs a new <code>WrapLayout</code> with a left
\r
36 * alignment and a default 5-unit horizontal and vertical gap.
\r
44 * Constructs a new <code>FlowLayout</code> with the specified
\r
45 * alignment and a default 5-unit horizontal and vertical gap.
\r
46 * The value of the alignment argument must be one of
\r
47 * <code>WrapLayout</code>, <code>WrapLayout</code>,
\r
48 * or <code>WrapLayout</code>.
\r
49 * @param align the alignment value
\r
51 public WrapLayout(int align)
\r
57 * Creates a new flow layout manager with the indicated alignment
\r
58 * and the indicated horizontal and vertical gaps.
\r
60 * The value of the alignment argument must be one of
\r
61 * <code>WrapLayout</code>, <code>WrapLayout</code>,
\r
62 * or <code>WrapLayout</code>.
\r
63 * @param align the alignment value
\r
64 * @param hgap the horizontal gap between components
\r
65 * @param vgap the vertical gap between components
\r
67 public WrapLayout(int align, int hgap, int vgap)
\r
69 super(align, hgap, vgap);
\r
73 * Returns the preferred dimensions for this layout given the
\r
74 * <i>visible</i> components in the specified target container.
\r
75 * @param target the component which needs to be laid out
\r
76 * @return the preferred dimensions to lay out the
\r
77 * subcomponents of the specified container
\r
80 public Dimension preferredLayoutSize(Container target)
\r
82 return layoutSize(target, true);
\r
86 * Returns the minimum dimensions needed to layout the <i>visible</i>
\r
87 * components contained in the specified target container.
\r
88 * @param target the component which needs to be laid out
\r
89 * @return the minimum dimensions to lay out the
\r
90 * subcomponents of the specified container
\r
93 public Dimension minimumLayoutSize(Container target)
\r
95 Dimension minimum = layoutSize(target, false);
\r
96 minimum.width -= (getHgap() + 1);
\r
101 * Returns the minimum or preferred dimension needed to layout the target
\r
104 * @param target target to get layout size for
\r
105 * @param preferred should preferred size be calculated
\r
106 * @return the dimension to layout the target container
\r
108 private Dimension layoutSize(Container target, boolean preferred)
\r
110 synchronized (target.getTreeLock())
\r
112 // Each row must fit with the width allocated to the containter.
\r
113 // When the container width = 0, the preferred width of the container
\r
114 // has not yet been calculated so lets ask for the maximum.
\r
116 int targetWidth = target.getSize().width;
\r
117 Container container = target;
\r
119 while (container.getSize().width == 0 && container.getParent() != null)
\r
121 container = container.getParent();
\r
124 targetWidth = container.getSize().width;
\r
126 if (targetWidth == 0)
\r
127 targetWidth = Integer.MAX_VALUE;
\r
129 int hgap = getHgap();
\r
130 int vgap = getVgap();
\r
131 Insets insets = target.getInsets();
\r
132 int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);
\r
133 int maxWidth = targetWidth - horizontalInsetsAndGap;
\r
135 // Fit components into the allowed width
\r
137 Dimension dim = new Dimension(0, 0);
\r
141 int nmembers = target.getComponentCount();
\r
143 for (int i = 0; i < nmembers; i++)
\r
145 Component m = target.getComponent(i);
\r
149 Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
\r
151 // Can't add the component to current row. Start a new row.
\r
153 if (rowWidth + d.width > maxWidth)
\r
155 addRow(dim, rowWidth, rowHeight);
\r
160 // Add a horizontal gap for all components after the first
\r
167 rowWidth += d.width;
\r
168 rowHeight = Math.max(rowHeight, d.height);
\r
172 addRow(dim, rowWidth, rowHeight);
\r
174 dim.width += horizontalInsetsAndGap;
\r
175 dim.height += insets.top + insets.bottom + vgap * 2;
\r
177 // When using a scroll pane or the DecoratedLookAndFeel we need to
\r
178 // make sure the preferred size is less than the size of the
\r
179 // target containter so shrinking the container size works
\r
180 // correctly. Removing the horizontal gap is an easy way to do this.
\r
182 Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
\r
184 if (scrollPane != null && target.isValid())
\r
186 dim.width -= (hgap + 1);
\r
194 * A new row has been completed. Use the dimensions of this row
\r
195 * to update the preferred size for the container.
\r
197 * @param dim update the width and height when appropriate
\r
198 * @param rowWidth the width of the row to add
\r
199 * @param rowHeight the height of the row to add
\r
201 private void addRow(Dimension dim, int rowWidth, int rowHeight)
\r
203 dim.width = Math.max(dim.width, rowWidth);
\r
205 if (dim.height > 0)
\r
207 dim.height += getVgap();
\r
210 dim.height += rowHeight;
\r