| AltFixedSplitPane.java |
1 /**
2 * Distribution License:
3 * JSword is free software; you can redistribute it and/or modify it under
4 * the terms of the GNU Lesser General Public License, version 2.1 as published by
5 * the Free Software Foundation. This program is distributed in the hope
6 * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8 * See the GNU Lesser General Public License for more details.
9 *
10 * The License is available on the internet at:
11 * http://www.gnu.org/copyleft/lgpl.html
12 * or by writing to:
13 * Free Software Foundation, Inc.
14 * 59 Temple Place - Suite 330
15 * Boston, MA 02111-1307, USA
16 *
17 * Copyright: 2005
18 * The copyright to this program is held by it's authors.
19 *
20 * ID: $Id: FixedSplitPane.java 1464 2007-07-02 02:34:40Z dmsmith $
21 */
22 package org.crosswire.common.swing;
23
24 import java.awt.Component;
25 import java.awt.ComponentOrientation;
26 import java.awt.Dimension;
27
28 import javax.swing.JComponent;
29 import javax.swing.JSplitPane;
30 import javax.swing.border.Border;
31 import javax.swing.border.EmptyBorder;
32 import javax.swing.plaf.SplitPaneUI;
33 import javax.swing.plaf.basic.BasicSplitPaneUI;
34
35 /**
36 * This is a hack to fix the setDividerLocation problem and other layout
37 * problems.
38 * <p>
39 * See Bug Parade 4101306, 4485465 for a description of the WIDE divider
40 * problem.
41 * <p>
42 * Bug Reports on JSplitpane setDividerLocation<br>
43 * 4101306, 4125713, 4148530
44 *<p>
45 * From the javadoc for setDividerLocation(double):
46 * -------------------------------------------<br>
47 * <p>
48 * This method is implemented in terms of setDividerLocation(int). This method
49 * immediately changes the size of the receiver based on its current size. If
50 * the receiver is not correctly realized and on screen, this method will have
51 * no effect (new divider location will become (current size *
52 * proportionalLocation) which is 0).<br>
53 * -------------------------------------------<br>
54 * So, as you can see the JSplitPane MUST be visible invoking this method
55 * otherwise it will not have the desired effect.
56 * <p>
57 * Another, Bug Report 4786896 notes that if the preferred sizes of the two
58 * components plus the divider of the split pane adds up to more than the
59 * preferred size of the JSplitPane, then JSplitPane will use the minimum size
60 * of the components.
61 * <p>
62 * Since the preferred way of managing the sizes of containers is not with pixel
63 * counts, the solution here is to set the preferred size to zero.
64 * <p>
65 * See Bug Parade: 4265389 for a description of the ComponentOrientation
66 * problem.
67 *
68 * @see gnu.lgpl.License for license details.<br>
69 * The copyright to this program is held by it's authors.
70 * @author DM Smith [dmsmith555 at yahoo dot com]
71 */
72 public class AltFixedSplitPane extends JSplitPane {
73 /* BUG_PARADE(DMS): many bugs here */
74
75 /**
76 * Constructor for FixedSplitPane
77 */
78 public AltFixedSplitPane() {
79 }
80
81 /**
82 * Constructor for FixedSplitPane that has no divider shadow and starts out
83 * with an empty border.
84 */
85 public AltFixedSplitPane(boolean visibleDividerBorder) {
86 this();
87 this.visibleDividerBorder = visibleDividerBorder;
88 setBorder(EMPTY_BORDER);
89 }
90
91 /**
92 * Constructor for FixedSplitPane
93 */
94 public AltFixedSplitPane(int arg0) {
95 super(arg0);
96 }
97
98 /**
99 * Constructor for FixedSplitPane
100 */
101 public AltFixedSplitPane(int arg0, boolean arg1) {
102 super(arg0, arg1);
103 }
104
105 /**
106 * Constructor for FixedSplitPane
107 */
108 public AltFixedSplitPane(int arg0, Component arg1, Component arg2) {
109 super(arg0, arg1, arg2);
110 }
111
112 /**
113 * Constructor for FixedSplitPane
114 */
115 public AltFixedSplitPane(int arg0, boolean arg1, Component arg2, Component arg3) {
116 super(arg0, arg1, arg2, arg3);
117 }
118
119 /*
120 * (non-Javadoc)
121 *
122 * @see
123 * java.awt.Component#setComponentOrientation(java.awt.ComponentOrientation)
124 */
125 @Override
126 public void setComponentOrientation(ComponentOrientation o) {
127 // Turns out that this is currently ignored by JSplitPane
128 // So we are handling it here.
129 boolean isLtoR = getComponentOrientation().isLeftToRight();
130
131 // if the layout is stacked/vertical then we are done
132 if (getOrientation() == JSplitPane.VERTICAL_SPLIT) {
133 return;
134 }
135
136 if (o.isLeftToRight() != isLtoR) {
137 // Swap the left and right
138 Component left = getLeftComponent();
139 Component right = getRightComponent();
140 setRightComponent(null);
141 setLeftComponent(right);
142 setRightComponent(left);
143
144 // The resizing is now reversed
145 setResizeWeight(isLtoR ? 1 - getResizeWeight() : getResizeWeight());
146
147 // also swap the proportional divider location
148 if (hasProportionalLocation) {
149 setDividerLocation(isLtoR ? 1 - proportionalLocation : proportionalLocation);
150 }
151 }
152
153 super.setComponentOrientation(o);
154 }
155
156 /*
157 * (non-Javadoc)
158 *
159 * @see java.awt.Container#addImpl(java.awt.Component, java.lang.Object,
160 * int)
161 */
162 @Override
163 protected void addImpl(Component comp, Object constraints, int index) {
164 if (comp instanceof JComponent) {
165 ((JComponent) comp).setPreferredSize(DOT);
166 }
167
168 // flip the constraint if we are not left to right
169 Object realConstraints = constraints;
170 if (!getComponentOrientation().isLeftToRight()) {
171 realConstraints = constraints != null && constraints.equals(JSplitPane.RIGHT) ? JSplitPane.LEFT : JSplitPane.RIGHT;
172 }
173
174 super.addImpl(comp, realConstraints, index);
175 }
176
177 /*
178 * (non-Javadoc)
179 *
180 * @see javax.swing.JSplitPane#setBottomComponent(java.awt.Component)
181 */
182 @Override
183 public void setBottomComponent(Component comp) {
184 if (comp instanceof JComponent) {
185 ((JComponent) comp).setPreferredSize(DOT);
186 }
187 super.setBottomComponent(comp);
188 }
189
190 /*
191 * (non-Javadoc)
192 *
193 * @see javax.swing.JSplitPane#setLeftComponent(java.awt.Component)
194 */
195 @Override
196 public void setLeftComponent(Component comp) {
197 if (comp instanceof JComponent) {
198 ((JComponent) comp).setPreferredSize(DOT);
199 }
200
201 if (getComponentOrientation().isLeftToRight()) {
202 super.setLeftComponent(comp);
203 } else {
204 super.setRightComponent(comp);
205 }
206 }
207
208 /*
209 * (non-Javadoc)
210 *
211 * @see javax.swing.JSplitPane#setRightComponent(java.awt.Component)
212 */
213 @Override
214 public void setRightComponent(Component comp) {
215 if (comp instanceof JComponent) {
216 ((JComponent) comp).setPreferredSize(DOT);
217 }
218
219 if (getComponentOrientation().isLeftToRight()) {
220 super.setRightComponent(comp);
221 } else {
222 super.setLeftComponent(comp);
223 }
224 }
225
226 /*
227 * (non-Javadoc)
228 *
229 * @see javax.swing.JSplitPane#setTopComponent(java.awt.Component)
230 */
231 @Override
232 public void setTopComponent(Component comp) {
233 if (comp instanceof JComponent) {
234 ((JComponent) comp).setPreferredSize(DOT);
235 }
236 super.setTopComponent(comp);
237 }
238
239 /**
240 * Validates this container and all of its subcomponents. The first time
241 * this method is called, the initial divider position is set.
242 */
243 @Override
244 public void validate() {
245 if (firstValidate) {
246 firstValidate = false;
247 if (hasProportionalLocation) {
248 setDividerLocation(proportionalLocation);
249 }
250 }
251 super.validate();
252 }
253
254 /**
255 * Sets the divider location as a percentage of the JSplitPane's size.
256 */
257 @Override
258 public void setDividerLocation(double newProportionalLoc) {
259 double realProportionalLoc = newProportionalLoc;
260
261 // When the orientation is reversed the proportions are reversed.
262 if (getComponentOrientation().isLeftToRight()) {
263 realProportionalLoc = 1 - realProportionalLoc;
264 }
265
266 if (!firstValidate) {
267 hasProportionalLocation = true;
268 proportionalLocation = realProportionalLoc;
269 } else {
270 super.setDividerLocation(realProportionalLoc);
271 }
272 }
273
274 /**
275 * Sets the divider location as a percentage of the JSplitPane's size.
276 */
277 @Override
278 public void setResizeWeight(double newResizeWeight) {
279 double realResizeWeight = newResizeWeight;
280
281 // When the orientation is reversed the proportions are reversed.
282 if (getComponentOrientation().isLeftToRight()) {
283 realResizeWeight = 1 - realResizeWeight;
284 }
285
286 super.setResizeWeight(realResizeWeight);
287 }
288
289 /**
290 * Whether visible borders are hinted
291 *
292 * @return the divider border visiblity hint
293 */
294 public boolean isVisibleDividerBorder() {
295 return visibleDividerBorder;
296 }
297
298 /**
299 * Set a hint whether the border should be visible or not. Look and feels
300 * may ignore this.
301 *
302 * @param newVisibility
303 */
304 public void setVisibleDividerBorder(boolean newVisibility) {
305 boolean oldVisibility = isVisibleDividerBorder();
306 if (oldVisibility == newVisibility) {
307 return;
308 }
309 visibleDividerBorder = newVisibility;
310 firePropertyChange(PROPERTYNAME_VISIBLE_DIVIDER_BORDER, oldVisibility, newVisibility);
311 }
312
313 /*
314 * (non-Javadoc)
315 *
316 * @see javax.swing.JComponent#updateUI()
317 */
318 @Override
319 public void updateUI() {
320 super.updateUI();
321 if (!visibleDividerBorder) {
322 SplitPaneUI splitPaneUI = getUI();
323 if (splitPaneUI instanceof BasicSplitPaneUI) {
324 BasicSplitPaneUI basicUI = (BasicSplitPaneUI) splitPaneUI;
325 basicUI.getDivider().setBorder(EMPTY_BORDER);
326 }
327 }
328 }
329
330 private static final Dimension DOT = new Dimension(0, 0);
331 private boolean firstValidate = true;
332 private boolean hasProportionalLocation;
333 private double proportionalLocation;
334
335 /**
336 * Property for border visibility
337 */
338 public static final String PROPERTYNAME_VISIBLE_DIVIDER_BORDER = "visibleDividerBorder";
339
340 /**
341 * An Empty Border
342 */
343 private static final Border EMPTY_BORDER = new EmptyBorder(0, 0, 0, 0);
344
345 /**
346 * Hint for whether Divider border should be visible.
347 */
348 private boolean visibleDividerBorder;
349
350 /**
351 * Serialization ID
352 */
353 private static final long serialVersionUID = 3761687909593789241L;
354 }
355