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