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 2050 2010-12-09 15:31:45Z dmsmith $
21   */
22  package org.crosswire.common.swing;
23  
24  import java.awt.Component;
25  import java.awt.Dimension;
26  
27  import javax.swing.JComponent;
28  import javax.swing.JSplitPane;
29  import javax.swing.border.Border;
30  import javax.swing.border.EmptyBorder;
31  import javax.swing.plaf.SplitPaneUI;
32  import javax.swing.plaf.basic.BasicSplitPaneUI;
33  
34  /**
35   * This is a hack to fix the setDividerLocation problem and other layout
36   * problems.
37   * <p>
38   * See Bug Parade 4101306, 4485465 for a description of the WIDE divider
39   * problem.
40   * <p>
41   * Bug Reports on JSplitpane setDividerLocation<br>
42   * 4101306, 4125713, 4148530
43   *<p>
44   * From the javadoc for setDividerLocation(double):
45   * -------------------------------------------<br>
46   * <p>
47   * This method is implemented in terms of setDividerLocation(int). This method
48   * immediately changes the size of the receiver based on its current size. If
49   * the receiver is not correctly realized and on screen, this method will have
50   * no effect (new divider location will become (current size *
51   * proportionalLocation) which is 0).<br>
52   * -------------------------------------------<br>
53   * So, as you can see the JSplitPane MUST be visible invoking this method
54   * otherwise it will not have the desired effect.
55   * <p>
56   * Another, Bug Report 4786896 notes that if the preferred sizes of the two
57   * components plus the divider of the split pane adds up to more than the
58   * preferred size of the JSplitPane, then JSplitPane will use the minimum size
59   * of the components.
60   * <p>
61   * Since the preferred way of managing the sizes of containers is not with pixel
62   * counts, the solution here is to set the preferred size to zero.
63   * 
64   * @see gnu.lgpl.License for license details.<br>
65   *      The copyright to this program is held by it's authors.
66   * @author DM Smith [dmsmith555 at yahoo dot com]
67   */
68  public class FixedSplitPane extends JSplitPane {
69      /* BUG_PARADE(DMS): many bugs here */
70  
71      /**
72       * Constructor for FixedSplitPane
73       */
74      public FixedSplitPane() {
75      }
76  
77      /**
78       * Constructor for FixedSplitPane that has no divider shadow and starts out
79       * with an empty border.
80       */
81      public FixedSplitPane(boolean visibleDividerBorder) {
82          this();
83          this.visibleDividerBorder = visibleDividerBorder;
84          setBorder(EMPTY_BORDER);
85      }
86  
87      /**
88       * Constructor for FixedSplitPane
89       */
90      public FixedSplitPane(int arg0) {
91          super(arg0);
92      }
93  
94      /**
95       * Constructor for FixedSplitPane
96       */
97      public FixedSplitPane(int arg0, boolean arg1) {
98          super(arg0, arg1);
99      }
100 
101     /**
102      * Constructor for FixedSplitPane
103      */
104     public FixedSplitPane(int arg0, Component arg1, Component arg2) {
105         super(arg0, arg1, arg2);
106     }
107 
108     /**
109      * Constructor for FixedSplitPane
110      */
111     public FixedSplitPane(int arg0, boolean arg1, Component arg2, Component arg3) {
112         super(arg0, arg1, arg2, arg3);
113     }
114 
115     /*
116      * (non-Javadoc)
117      * 
118      * @see java.awt.Container#addImpl(java.awt.Component, java.lang.Object,
119      * int)
120      */
121     @Override
122     protected void addImpl(Component comp, Object constraints, int index) {
123         if (comp instanceof JComponent) {
124             ((JComponent) comp).setPreferredSize(DOT);
125         }
126         super.addImpl(comp, constraints, index);
127     }
128 
129     /*
130      * (non-Javadoc)
131      * 
132      * @see javax.swing.JSplitPane#setBottomComponent(java.awt.Component)
133      */
134     @Override
135     public void setBottomComponent(Component comp) {
136         if (comp instanceof JComponent) {
137             ((JComponent) comp).setPreferredSize(DOT);
138         }
139         super.setBottomComponent(comp);
140     }
141 
142     /*
143      * (non-Javadoc)
144      * 
145      * @see javax.swing.JSplitPane#setLeftComponent(java.awt.Component)
146      */
147     @Override
148     public void setLeftComponent(Component comp) {
149         if (comp instanceof JComponent) {
150             ((JComponent) comp).setPreferredSize(DOT);
151         }
152         super.setLeftComponent(comp);
153     }
154 
155     /*
156      * (non-Javadoc)
157      * 
158      * @see javax.swing.JSplitPane#setRightComponent(java.awt.Component)
159      */
160     @Override
161     public void setRightComponent(Component comp) {
162         if (comp instanceof JComponent) {
163             ((JComponent) comp).setPreferredSize(DOT);
164         }
165         super.setRightComponent(comp);
166     }
167 
168     /*
169      * (non-Javadoc)
170      * 
171      * @see javax.swing.JSplitPane#setTopComponent(java.awt.Component)
172      */
173     @Override
174     public void setTopComponent(Component comp) {
175         if (comp instanceof JComponent) {
176             ((JComponent) comp).setPreferredSize(DOT);
177         }
178         super.setTopComponent(comp);
179     }
180 
181     /**
182      * Validates this container and all of its subcomponents. The first time
183      * this method is called, the initial divider position is set.
184      */
185     @Override
186     public void validate() {
187         if (firstValidate) {
188             firstValidate = false;
189             if (hasProportionalLocation) {
190                 setDividerLocation(proportionalLocation);
191             }
192         }
193         super.validate();
194     }
195 
196     /**
197      * Sets the divider location as a percentage of the JSplitPane's size.
198      */
199     @Override
200     public void setDividerLocation(double newProportionalLoc) {
201         if (!firstValidate) {
202             hasProportionalLocation = true;
203             proportionalLocation = newProportionalLoc;
204         } else {
205             super.setDividerLocation(newProportionalLoc);
206         }
207     }
208 
209     /**
210      * Whether visible borders are hinted
211      * 
212      * @return the divider border visiblity hint
213      */
214     public boolean isVisibleDividerBorder() {
215         return visibleDividerBorder;
216     }
217 
218     /**
219      * Set a hint whether the border should be visible or not. Look and feels
220      * may ignore this.
221      * 
222      * @param newVisibility
223      */
224     public void setVisibleDividerBorder(boolean newVisibility) {
225         boolean oldVisibility = isVisibleDividerBorder();
226         if (oldVisibility == newVisibility) {
227             return;
228         }
229         visibleDividerBorder = newVisibility;
230         firePropertyChange(PROPERTYNAME_VISIBLE_DIVIDER_BORDER, oldVisibility, newVisibility);
231     }
232 
233     /*
234      * (non-Javadoc)
235      * 
236      * @see javax.swing.JComponent#updateUI()
237      */
238     @Override
239     public void updateUI() {
240         super.updateUI();
241         if (!visibleDividerBorder) {
242             SplitPaneUI splitPaneUI = getUI();
243             if (splitPaneUI instanceof BasicSplitPaneUI) {
244                 BasicSplitPaneUI basicUI = (BasicSplitPaneUI) splitPaneUI;
245                 basicUI.getDivider().setBorder(EMPTY_BORDER);
246             }
247         }
248     }
249 
250     private static final Dimension DOT = new Dimension(0, 0);
251     private boolean firstValidate = true;
252     private boolean hasProportionalLocation;
253     private double proportionalLocation;
254 
255     /**
256      * Property for border visibility
257      */
258     public static final String PROPERTYNAME_VISIBLE_DIVIDER_BORDER = "visibleDividerBorder";
259 
260     /**
261      * An Empty Border
262      */
263     private static final Border EMPTY_BORDER = new EmptyBorder(0, 0, 0, 0);
264 
265     /**
266      * Hint for whether Divider border should be visible.
267      */
268     private boolean visibleDividerBorder;
269 
270     /**
271      * Serialization ID
272      */
273     private static final long serialVersionUID = 3761687909593789241L;
274 }
275