001 package util.swing;
002
003 import java.awt.Component;
004 import java.awt.Container;
005 import java.awt.Dimension;
006 import java.awt.Insets;
007 import java.awt.LayoutManager;
008
009 import javax.swing.BorderFactory;
010 import javax.swing.JComponent;
011 import javax.swing.JLabel;
012 import javax.swing.JPanel;
013
014 /**
015 * A Swing Panel realising a standard option panel layout. That means, option
016 * titles are displayed right in front of their corresponding components. All
017 * alignment is done automatically.
018 *
019 * @author Thomas Ryssel
020 * @since v3.3
021 */
022 public class JOptionPanel extends JPanel {
023
024 /**
025 * ID for Serialization.
026 */
027 private static final long serialVersionUID = -7494273537708600891L;
028
029 /**
030 * The title text (will be shown using TitledBorder).
031 */
032 private String m_sText = null;
033
034 /**
035 * Whether or not initialization is finished. This is mainly used to
036 * guard the LayoutManager once it is set.
037 */
038 private boolean m_fInitDone = false;
039
040 /**
041 * A LayoutManager for positioning components in the way wished for.
042 * This could also be done using several components and - for example -
043 * BoxLayout. But there are some disadvantages concerning exact
044 * positioning. Also here we can correctly handle "filling cases", e.g.
045 * components using all the space that is possibly left (both horizontally
046 * and vertically). Alignment (e.g. putting all stuff in the center) can
047 * be done quite easily that way, too.
048 *
049 * @author Thomas Ryssel
050 */
051 private class OptionLayout implements LayoutManager {
052
053 /**
054 * The horizontal gap (between titles and components).
055 */
056 private int m_nHgap;
057
058 /**
059 * The vertical gap.
060 */
061 private int n_nVgap = 0;
062
063 /**
064 * The horizontal alignment.
065 */
066 private int m_nHorizontalAlign;
067
068 /**
069 * The vertical alignment.
070 */
071 private int m_nVerticalAlign;
072
073 /**
074 * Create a new OptionLayout.
075 *
076 * @param hgap Horizontal gap.
077 * @param vgap Vertical gap.
078 * @param horiz Horizontal alignment.
079 * @param vert Vertical alignment.
080 */
081 public OptionLayout(int hgap, int vgap, int horiz, int vert) {
082 this.n_nVgap = vgap;
083 this.m_nHgap = hgap;
084 this.m_nHorizontalAlign = horiz;
085 this.m_nVerticalAlign = vert;
086 }
087
088 /**
089 * Add a special layout component. Not used in this implementation.
090 */
091 public void addLayoutComponent(String name, Component comp) {
092 }
093
094 /**
095 * Remove a special layout component. Not used in this implementation.
096 */
097 public void removeLayoutComponent(Component comp) {
098 }
099
100 /**
101 * Get the preferred Dimension of the layout.
102 */
103 public Dimension preferredLayoutSize(Container parent) {
104 synchronized (parent.getTreeLock()) {
105 Insets ins = parent.getInsets();
106 int n = parent.getComponentCount() / 2;
107 int wl = 0, wr = 0, w;
108 int h = 0;
109 int count = parent.getComponentCount();
110 for (int i = 0; i < count; i += 2) {
111 int lcw = parent.getComponent(i).getPreferredSize().width;
112 if (lcw > wl) {
113 wl = lcw;
114 }
115 }
116 for (int i = 0; i < count; i += 2) {
117 int rcw = parent.getComponent(i + 1).getPreferredSize().width;
118 if (rcw == Integer.MAX_VALUE && parent.getSize().width > 0) {
119 rcw = parent.getSize().width - wl - m_nHgap;
120 }
121 if (rcw > wr) {
122 wr = rcw;
123 }
124
125 if (parent.getComponent(i + 1).getPreferredSize().height == Integer.MAX_VALUE) {
126 h += parent.getSize().height;
127 } else {
128 h += parent.getComponent(i + 1).getPreferredSize().height;
129 }
130 }
131
132 h += (n - 1) * n_nVgap + ins.top + ins.bottom;
133 w = wl + wr + m_nHgap + ins.left + ins.right;
134
135 return new Dimension(w, h);
136 }
137 }
138
139 /**
140 * Get the minimum Dimension of the layout.
141 */
142 public Dimension minimumLayoutSize(Container parent) {
143 return preferredLayoutSize(parent);
144 }
145
146 /**
147 * Do the actual layout work.
148 */
149 public void layoutContainer(Container parent) {
150 synchronized (parent.getTreeLock()) {
151 Insets ins = parent.getInsets();
152 int topoffset = 0, leftoffset = 0;
153 Dimension pref = preferredLayoutSize(parent);
154
155
156 switch (m_nHorizontalAlign) {
157 case LEFT:
158 leftoffset = 0;
159 break;
160 case CENTER:
161 if (parent.getSize().width > pref.width) {
162 leftoffset = (parent.getSize().width - pref.width - ins.left - ins.right) / 2;
163 }
164 break;
165 case RIGHT:
166 if (parent.getSize().width > pref.width) {
167 leftoffset = parent.getSize().width - pref.width - ins.right - m_nHgap;
168 }
169 break;
170 }
171 switch (m_nVerticalAlign) {
172 case TOP:
173 topoffset = 0;
174 break;
175 case CENTER:
176 if (parent.getSize().height > pref.height) {
177 topoffset = (parent.getSize().height - pref.height - ins.top - ins.bottom) / 2;
178 }
179 break;
180 case BOTTOM:
181 if (parent.getSize().height > pref.height) {
182 topoffset = parent.getSize().height - pref.height - ins.bottom;
183 }
184 break;
185 }
186
187 int leftsize = 0;
188 int top = ins.top + topoffset;
189 int heightleft = parent.getSize().height - ins.top - ins.bottom;
190 for (int i = 0; i < parent.getComponentCount(); i += 2) {
191 Component comp = parent.getComponent(i);
192 if (comp.getPreferredSize().width > leftsize) {
193 leftsize = comp.getPreferredSize().width;
194 }
195 }
196 int rightsize = parent.getSize().width - leftsize - ins.left -
197 ins.right - m_nHgap;
198 for (int i = 0; i < parent.getComponentCount(); i += 2) {
199 Component compleft = parent.getComponent(i);
200 Component compright = parent.getComponent(i + 1);
201 int height = compright.getPreferredSize().height;
202 if (height == Integer.MAX_VALUE) {
203 height = heightleft;
204 heightleft = 0;
205 } else {
206 heightleft -= height + n_nVgap;
207 }
208 compleft.setBounds(ins.left, top, leftsize + leftoffset, compleft.getPreferredSize().height);
209
210 if (compright.getPreferredSize().width == Integer.MAX_VALUE) {
211 compright.setBounds(m_nHgap + ins.left + leftsize + leftoffset, top, rightsize, height);
212 } else {
213 compright.setBounds(m_nHgap + ins.left + leftsize + leftoffset, top, compright.getPreferredSize().width, height);
214 }
215 top += height + n_nVgap;
216 if (heightleft == 0) {
217 break;
218 }
219 }
220
221
222 }
223 }
224 }
225
226
227 /**
228 * Create a new JOptionPanel without title and horizontal and vertical alignment
229 * set to LEFT and TOP, respectively. Horizontal gap will be 16 (pixels) and
230 * vertical gap will be set to 4 (pixels).
231 */
232 public JOptionPanel() {
233 this(null);
234 }
235
236 /**
237 * Create a new JOptionPanel with horizontal and vertical alignment set to
238 * LEFT and TOP, respectively. Horizontal gap will be 16 (pixels) and vertical
239 * gap will be set to 4 (pixels).
240 *
241 * @param text The title text of the panel. Can be set to <code>null</code>
242 * to indicate that there is no title.
243 */
244 public JOptionPanel(String text) {
245 this(text, 16, 4);
246 }
247
248 /**
249 * Create a new JOptionPanel with horizontal and vertical alignment set to
250 * LEFT and TOP, respectively.
251 *
252 * @param text The title text of the panel. Can be set to <code>null</code>
253 * to indicate that there is no title.
254 * @param hgap Horizontal gap (between titles and components).
255 * @param vgap Vertical gap.
256 */
257 public JOptionPanel(String text, int hgap, int vgap) {
258 this(text, hgap, vgap, LEFT, TOP);
259 }
260
261 /**
262 * Create a new JOptionPanel.
263 *
264 * @param text The title text of the panel. Can be set to <code>null</code>
265 * to indicate that there is no title.
266 * @param hgap Horizontal gap (between titles and components).
267 * @param vgap Vertical gap.
268 * @param horizAlign Horizontal aligment. Can be either {@link #LEFT}, {@link #RIGHT}
269 * or {@link #CENTER}.
270 * @param vertAlign Vertical alignment. Can be either {@link #TOP}, {@link #BOTTOM}
271 * or {@link #CENTER}.
272 */
273 public JOptionPanel(String text, int hgap, int vgap, int horizAlign, int vertAlign) {
274 setText(text);
275 setLayout(new OptionLayout(hgap, vgap, horizAlign, vertAlign));
276 m_fInitDone = true;
277 }
278
279 /**
280 * Set the option panel's title text.
281 *
282 * @param text The title text of the panel. Can be set to <code>null</code>
283 * to indicate that there is no title.
284 */
285 public void setText(String text) {
286 this.m_sText = text;
287 if (text != null) {
288 setBorder(BorderFactory.createTitledBorder(this.m_sText));
289 } else {
290 setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
291 }
292 }
293
294 /**
295 * Get the option panel's title text.
296 *
297 * @return The title text of the panel.
298 */
299 public String getText() {
300 return m_sText;
301 }
302
303 /**
304 * Add the given component under the given title. The layout will be done
305 * according to the preferred dimensions of the components.
306 *
307 * <p>If the component's preferred width is set to <code>Integer.MAX_VALUE</code>,
308 * it will take up all possibly available horizontal space. If the component's
309 * preferred height is set to <code>Integer.MAX_VALUE</code>, it will take up
310 * all vertical space that is left. Note that there cannot be any other components
311 * underneath such a component.
312 *
313 * <p>Components are always shown in the order they have been added (from top
314 * to bottom).</p>
315 *
316 * @param title The option's title. If it is no empty string, a colon (":")
317 * will be added automatically.
318 * @param component The component representing the option in however way.
319 */
320 public void addOption(String title, JComponent component) {
321 JLabel label = new JLabel("", JLabel.RIGHT);
322 if (!title.equals("")) {
323 label.setText(" " + title + ":");
324 }
325 add(label);
326 add(component);
327 }
328
329 /**
330 * Add a "display only" option. the <code>text</code>'s <code>toString()</code>
331 * method will be used to construct a <code>JLabel</code> that will be displayed.
332 * That is merely a convenience method that allows you to simply display information.
333 *
334 * <p>For example, you could do something like: <code>addOption("File", "test.txt");</code>.
335 *
336 * @param title The option's title. If it is no empty string, a colon (":")
337 * will be added automatically.
338 * @param text The object representing the text to show.
339 */
340 public void addOption(String title, Object text) {
341 addOption(title, new JLabel(text.toString()));
342 }
343
344
345 /**
346 * Get the n-th option component (in the order they were added).
347 *
348 * @param n Number of the component to retrieve.
349 *
350 * @return The component requested.
351 */
352 public Component getOptionComponent(int n) {
353 return getComponent(n * 2 + 1);
354 }
355
356 /**
357 * Set layout manager. Overridden so that it cannot be used anymore
358 * after initialization.
359 */
360 public void setLayout(LayoutManager mgr) {
361 if (!m_fInitDone) {
362 super.setLayout(mgr);
363 return;
364 }
365 throw new UnsupportedOperationException("This does not allow to change the" +
366 " layout manager. Choose JPanel instead");
367 }
368
369 /**
370 * Constant representing center alignment.
371 */
372 public static final int CENTER = 0;
373
374 /**
375 * Constant representing left alignment.
376 */
377 public static final int LEFT = 1;
378
379 /**
380 * Constant representing right alignment.
381 */
382 public static final int RIGHT = 2;
383
384 /**
385 * Constant representing top alignment.
386 */
387 public static final int TOP = 3;
388
389 /**
390 * Constant representing bottom alignment.
391 */
392 public static final int BOTTOM = 4;
393 }