]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.utils.ui/src/org/simantics/utils/ui/awt/WrapLayout.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / awt / WrapLayout.java
1 /*******************************************************************************
2  * Copyright (c) 2016 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     Semantum Oy - initial API and implementation, courtesy of Rob Camick
11  *                   https://tips4java.wordpress.com/2008/11/06/wrap-layout/
12  *******************************************************************************/
13 package org.simantics.utils.ui.awt;
14
15 import java.awt.Component;
16 import java.awt.Container;
17 import java.awt.Dimension;
18 import java.awt.FlowLayout;
19 import java.awt.Insets;
20
21 import javax.swing.JScrollPane;
22 import javax.swing.SwingUtilities;
23
24 /**
25  *  FlowLayout subclass that fully supports wrapping of components.
26  */
27 public class WrapLayout extends FlowLayout
28 {
29         /**
30          * 
31          */
32         private static final long serialVersionUID = -4312800883442354753L;
33
34         /**
35         * Constructs a new <code>WrapLayout</code> with a left
36         * alignment and a default 5-unit horizontal and vertical gap.
37         */
38         public WrapLayout()
39         {
40                 super();
41         }
42
43         /**
44         * Constructs a new <code>FlowLayout</code> with the specified
45         * alignment and a default 5-unit horizontal and vertical gap.
46         * The value of the alignment argument must be one of
47         * <code>WrapLayout</code>, <code>WrapLayout</code>,
48         * or <code>WrapLayout</code>.
49         * @param align the alignment value
50         */
51         public WrapLayout(int align)
52         {
53                 super(align);
54         }
55
56         /**
57         * Creates a new flow layout manager with the indicated alignment
58         * and the indicated horizontal and vertical gaps.
59         * <p>
60         * The value of the alignment argument must be one of
61         * <code>WrapLayout</code>, <code>WrapLayout</code>,
62         * or <code>WrapLayout</code>.
63         * @param align the alignment value
64         * @param hgap the horizontal gap between components
65         * @param vgap the vertical gap between components
66         */
67         public WrapLayout(int align, int hgap, int vgap)
68         {
69                 super(align, hgap, vgap);
70         }
71
72         /**
73         * Returns the preferred dimensions for this layout given the
74         * <i>visible</i> components in the specified target container.
75         * @param target the component which needs to be laid out
76         * @return the preferred dimensions to lay out the
77         * subcomponents of the specified container
78         */
79         @Override
80         public Dimension preferredLayoutSize(Container target)
81         {
82                 return layoutSize(target, true);
83         }
84
85         /**
86         * Returns the minimum dimensions needed to layout the <i>visible</i>
87         * components contained in the specified target container.
88         * @param target the component which needs to be laid out
89         * @return the minimum dimensions to lay out the
90         * subcomponents of the specified container
91         */
92         @Override
93         public Dimension minimumLayoutSize(Container target)
94         {
95                 Dimension minimum = layoutSize(target, false);
96                 minimum.width -= (getHgap() + 1);
97                 return minimum;
98         }
99
100         /**
101         * Returns the minimum or preferred dimension needed to layout the target
102         * container.
103         *
104         * @param target target to get layout size for
105         * @param preferred should preferred size be calculated
106         * @return the dimension to layout the target container
107         */
108         private Dimension layoutSize(Container target, boolean preferred)
109         {
110         synchronized (target.getTreeLock())
111         {
112                 //  Each row must fit with the width allocated to the containter.
113                 //  When the container width = 0, the preferred width of the container
114                 //  has not yet been calculated so lets ask for the maximum.
115
116                 int targetWidth = target.getSize().width;
117                 Container container = target;
118
119                 while (container.getSize().width == 0 && container.getParent() != null)
120                 {
121                         container = container.getParent();
122                 }
123
124                 targetWidth = container.getSize().width;
125
126                 if (targetWidth == 0)
127                         targetWidth = Integer.MAX_VALUE;
128
129                 int hgap = getHgap();
130                 int vgap = getVgap();
131                 Insets insets = target.getInsets();
132                 int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);
133                 int maxWidth = targetWidth - horizontalInsetsAndGap;
134
135                 //  Fit components into the allowed width
136
137                 Dimension dim = new Dimension(0, 0);
138                 int rowWidth = 0;
139                 int rowHeight = 0;
140
141                 int nmembers = target.getComponentCount();
142
143                 for (int i = 0; i < nmembers; i++)
144                 {
145                         Component m = target.getComponent(i);
146
147                         if (m.isVisible())
148                         {
149                                 Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
150
151                                 //  Can't add the component to current row. Start a new row.
152
153                                 if (rowWidth + d.width > maxWidth)
154                                 {
155                                         addRow(dim, rowWidth, rowHeight);
156                                         rowWidth = 0;
157                                         rowHeight = 0;
158                                 }
159
160                                 //  Add a horizontal gap for all components after the first
161
162                                 if (rowWidth != 0)
163                                 {
164                                         rowWidth += hgap;
165                                 }
166
167                                 rowWidth += d.width;
168                                 rowHeight = Math.max(rowHeight, d.height);
169                         }
170                 }
171
172                 addRow(dim, rowWidth, rowHeight);
173
174                 dim.width += horizontalInsetsAndGap;
175                 dim.height += insets.top + insets.bottom + vgap * 2;
176
177                 //      When using a scroll pane or the DecoratedLookAndFeel we need to
178                 //  make sure the preferred size is less than the size of the
179                 //  target containter so shrinking the container size works
180                 //  correctly. Removing the horizontal gap is an easy way to do this.
181
182                 Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
183
184                 if (scrollPane != null && target.isValid())
185                 {
186                         dim.width -= (hgap + 1);
187                 }
188
189                 return dim;
190         }
191         }
192
193         /*
194          *  A new row has been completed. Use the dimensions of this row
195          *  to update the preferred size for the container.
196          *
197          *  @param dim update the width and height when appropriate
198          *  @param rowWidth the width of the row to add
199          *  @param rowHeight the height of the row to add
200          */
201         private void addRow(Dimension dim, int rowWidth, int rowHeight)
202         {
203                 dim.width = Math.max(dim.width, rowWidth);
204
205                 if (dim.height > 0)
206                 {
207                         dim.height += getVgap();
208                 }
209
210                 dim.height += rowHeight;
211         }
212 }