KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > plaf > basic > BasicSplitPaneUI


1 /*
2  * @(#)BasicSplitPaneUI.java 1.80 04/05/18
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8
9 package javax.swing.plaf.basic;
10
11
12 import sun.swing.DefaultLookup;
13 import sun.swing.UIAction;
14 import javax.swing.*;
15 import javax.swing.border.Border JavaDoc;
16 import javax.swing.event.*;
17 import java.awt.*;
18 import java.awt.event.*;
19 import java.awt.peer.ComponentPeer;
20 import java.awt.peer.LightweightPeer;
21 import java.beans.*;
22 import java.util.*;
23 import javax.swing.plaf.ActionMapUIResource JavaDoc;
24 import javax.swing.plaf.SplitPaneUI JavaDoc;
25 import javax.swing.plaf.ComponentUI JavaDoc;
26 import javax.swing.plaf.UIResource JavaDoc;
27
28
29 /**
30  * A Basic L&F implementation of the SplitPaneUI.
31  *
32  * @version 1.80 05/18/04
33  * @author Scott Violet
34  * @author Steve Wilson
35  * @author Ralph Kar
36  */

37 public class BasicSplitPaneUI extends SplitPaneUI JavaDoc
38 {
39     /**
40      * The divider used for non-continuous layout is added to the split pane
41      * with this object.
42      */

43     protected static final String JavaDoc NON_CONTINUOUS_DIVIDER =
44         "nonContinuousDivider";
45
46
47     /**
48      * How far (relative) the divider does move when it is moved around by
49      * the cursor keys on the keyboard.
50      */

51     protected static int KEYBOARD_DIVIDER_MOVE_OFFSET = 3;
52
53
54     /**
55      * JSplitPane instance this instance is providing
56      * the look and feel for.
57      */

58     protected JSplitPane splitPane;
59
60
61     /**
62      * LayoutManager that is created and placed into the split pane.
63      */

64     protected BasicHorizontalLayoutManager layoutManager;
65
66
67     /**
68      * Instance of the divider for this JSplitPane.
69      */

70     protected BasicSplitPaneDivider JavaDoc divider;
71
72
73     /**
74      * Instance of the PropertyChangeListener for this JSplitPane.
75      */

76     protected PropertyChangeListener propertyChangeListener;
77
78
79     /**
80      * Instance of the FocusListener for this JSplitPane.
81      */

82     protected FocusListener focusListener;
83
84     private Handler handler;
85
86
87     /**
88      * Keys to use for forward focus traversal when the JComponent is
89      * managing focus.
90      */

91     private static Set managingFocusForwardTraversalKeys;
92
93     /**
94      * Keys to use for backward focus traversal when the JComponent is
95      * managing focus.
96      */

97     private static Set managingFocusBackwardTraversalKeys;
98
99
100     /**
101      * The size of the divider while the dragging session is valid.
102      */

103     protected int dividerSize;
104
105
106     /**
107      * Instance for the shadow of the divider when non continuous layout
108      * is being used.
109      */

110     protected Component nonContinuousLayoutDivider;
111
112
113     /**
114      * Set to true in startDragging if any of the children
115      * (not including the nonContinuousLayoutDivider) are heavy weights.
116      */

117     protected boolean draggingHW;
118
119
120     /**
121      * Location of the divider when the dragging session began.
122      */

123     protected int beginDragDividerLocation;
124
125
126     /**
127      * As of Java 2 platform v1.3 this previously undocumented field is no
128      * longer used.
129      * Key bindings are now defined by the LookAndFeel, please refer to
130      * the key bindings specification for further details.
131      *
132      * @deprecated As of Java 2 platform v1.3.
133      */

134     @Deprecated JavaDoc
135     protected KeyStroke upKey;
136     /**
137      * As of Java 2 platform v1.3 this previously undocumented field is no
138      * longer used.
139      * Key bindings are now defined by the LookAndFeel, please refer to
140      * the key bindings specification for further details.
141      *
142      * @deprecated As of Java 2 platform v1.3.
143      */

144     @Deprecated JavaDoc
145     protected KeyStroke downKey;
146     /**
147      * As of Java 2 platform v1.3 this previously undocumented field is no
148      * longer used.
149      * Key bindings are now defined by the LookAndFeel, please refer to
150      * the key bindings specification for further details.
151      *
152      * @deprecated As of Java 2 platform v1.3.
153      */

154     @Deprecated JavaDoc
155     protected KeyStroke leftKey;
156     /**
157      * As of Java 2 platform v1.3 this previously undocumented field is no
158      * longer used.
159      * Key bindings are now defined by the LookAndFeel, please refer to
160      * the key bindings specification for further details.
161      *
162      * @deprecated As of Java 2 platform v1.3.
163      */

164     @Deprecated JavaDoc
165     protected KeyStroke rightKey;
166     /**
167      * As of Java 2 platform v1.3 this previously undocumented field is no
168      * longer used.
169      * Key bindings are now defined by the LookAndFeel, please refer to
170      * the key bindings specification for further details.
171      *
172      * @deprecated As of Java 2 platform v1.3.
173      */

174     @Deprecated JavaDoc
175     protected KeyStroke homeKey;
176     /**
177      * As of Java 2 platform v1.3 this previously undocumented field is no
178      * longer used.
179      * Key bindings are now defined by the LookAndFeel, please refer to
180      * the key bindings specification for further details.
181      *
182      * @deprecated As of Java 2 platform v1.3.
183      */

184     @Deprecated JavaDoc
185     protected KeyStroke endKey;
186     /**
187      * As of Java 2 platform v1.3 this previously undocumented field is no
188      * longer used.
189      * Key bindings are now defined by the LookAndFeel, please refer to
190      * the key bindings specification for further details.
191      *
192      * @deprecated As of Java 2 platform v1.3.
193      */

194     @Deprecated JavaDoc
195     protected KeyStroke dividerResizeToggleKey;
196
197     /**
198      * As of Java 2 platform v1.3 this previously undocumented field is no
199      * longer used.
200      * Key bindings are now defined by the LookAndFeel, please refer to
201      * the key bindings specification for further details.
202      *
203      * @deprecated As of Java 2 platform v1.3.
204      */

205     @Deprecated JavaDoc
206     protected ActionListener keyboardUpLeftListener;
207     /**
208      * As of Java 2 platform v1.3 this previously undocumented field is no
209      * longer used.
210      * Key bindings are now defined by the LookAndFeel, please refer to
211      * the key bindings specification for further details.
212      *
213      * @deprecated As of Java 2 platform v1.3.
214      */

215     @Deprecated JavaDoc
216     protected ActionListener keyboardDownRightListener;
217     /**
218      * As of Java 2 platform v1.3 this previously undocumented field is no
219      * longer used.
220      * Key bindings are now defined by the LookAndFeel, please refer to
221      * the key bindings specification for further details.
222      *
223      * @deprecated As of Java 2 platform v1.3.
224      */

225     @Deprecated JavaDoc
226     protected ActionListener keyboardHomeListener;
227     /**
228      * As of Java 2 platform v1.3 this previously undocumented field is no
229      * longer used.
230      * Key bindings are now defined by the LookAndFeel, please refer to
231      * the key bindings specification for further details.
232      *
233      * @deprecated As of Java 2 platform v1.3.
234      */

235     @Deprecated JavaDoc
236     protected ActionListener keyboardEndListener;
237     /**
238      * As of Java 2 platform v1.3 this previously undocumented field is no
239      * longer used.
240      * Key bindings are now defined by the LookAndFeel, please refer to
241      * the key bindings specification for further details.
242      *
243      * @deprecated As of Java 2 platform v1.3.
244      */

245     @Deprecated JavaDoc
246     protected ActionListener keyboardResizeToggleListener;
247
248
249     // Private data of the instance
250
private int orientation;
251     private int lastDragLocation;
252     private boolean continuousLayout;
253     private boolean dividerKeyboardResize;
254     private boolean dividerLocationIsSet; // needed for tracking
255
// the first occurrence of
256
// setDividerLocation()
257
private Color dividerDraggingColor;
258     private boolean rememberPaneSizes;
259
260     // Indicates wether the one of splitpane sides is expanded
261
private boolean keepHidden = false;
262
263     /** Indicates that we have painted once. */
264     // This is used by the LayoutManager to determine when it should use
265
// the divider location provided by the JSplitPane. This is used as there
266
// is no way to determine when the layout process has completed.
267
boolean painted;
268     /** If true, setDividerLocation does nothing. */
269     boolean ignoreDividerLocationChange;
270
271
272     /**
273      * Creates a new BasicSplitPaneUI instance
274      */

275     public static ComponentUI JavaDoc createUI(JComponent x) {
276         return new BasicSplitPaneUI JavaDoc();
277     }
278
279     static void loadActionMap(LazyActionMap JavaDoc map) {
280         map.put(new Actions(Actions.NEGATIVE_INCREMENT));
281     map.put(new Actions(Actions.POSITIVE_INCREMENT));
282     map.put(new Actions(Actions.SELECT_MIN));
283     map.put(new Actions(Actions.SELECT_MAX));
284     map.put(new Actions(Actions.START_RESIZE));
285     map.put(new Actions(Actions.TOGGLE_FOCUS));
286     map.put(new Actions(Actions.FOCUS_OUT_FORWARD));
287     map.put(new Actions(Actions.FOCUS_OUT_BACKWARD));
288     }
289
290
291
292     /**
293      * Installs the UI.
294      */

295     public void installUI(JComponent c) {
296         splitPane = (JSplitPane) c;
297         dividerLocationIsSet = false;
298         dividerKeyboardResize = false;
299         keepHidden = false;
300         installDefaults();
301         installListeners();
302         installKeyboardActions();
303         setLastDragLocation(-1);
304     }
305
306
307     /**
308      * Installs the UI defaults.
309      */

310     protected void installDefaults(){
311         LookAndFeel.installBorder(splitPane, "SplitPane.border");
312         LookAndFeel.installColors(splitPane, "SplitPane.background",
313                                   "SplitPane.foreground");
314         LookAndFeel.installProperty(splitPane, "opaque", Boolean.TRUE);
315
316         if (divider == null) divider = createDefaultDivider();
317         divider.setBasicSplitPaneUI(this);
318
319     Border JavaDoc b = divider.getBorder();
320
321     if (b == null || !(b instanceof UIResource JavaDoc)) {
322         divider.setBorder(UIManager.getBorder("SplitPaneDivider.border"));
323     }
324
325     dividerDraggingColor = UIManager.getColor("SplitPaneDivider.draggingColor");
326
327         setOrientation(splitPane.getOrientation());
328
329     // This plus 2 here is to provide backwards consistancy. Previously,
330
// the old size did not include the 2 pixel border around the divider,
331
// it now does.
332
LookAndFeel.installProperty(splitPane, "dividerSize",
333                     UIManager.get("SplitPane.dividerSize"));
334
335         divider.setDividerSize(splitPane.getDividerSize());
336     dividerSize = divider.getDividerSize();
337         splitPane.add(divider, JSplitPane.DIVIDER);
338
339         setContinuousLayout(splitPane.isContinuousLayout());
340
341         resetLayoutManager();
342
343         /* Install the nonContinuousLayoutDivider here to avoid having to
344         add/remove everything later. */

345         if(nonContinuousLayoutDivider == null) {
346             setNonContinuousLayoutDivider(
347                                 createDefaultNonContinuousLayoutDivider(),
348                                 true);
349         } else {
350             setNonContinuousLayoutDivider(nonContinuousLayoutDivider, true);
351         }
352
353     // focus forward traversal key
354
if (managingFocusForwardTraversalKeys==null) {
355         managingFocusForwardTraversalKeys = new TreeSet();
356         managingFocusForwardTraversalKeys.add(
357         KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
358     }
359     splitPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
360                     managingFocusForwardTraversalKeys);
361     // focus backward traversal key
362
if (managingFocusBackwardTraversalKeys==null) {
363         managingFocusBackwardTraversalKeys = new TreeSet();
364         managingFocusBackwardTraversalKeys.add(
365         KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK));
366     }
367     splitPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
368                     managingFocusBackwardTraversalKeys);
369     }
370
371
372     /**
373      * Installs the event listeners for the UI.
374      */

375     protected void installListeners() {
376         if ((propertyChangeListener = createPropertyChangeListener()) !=
377             null) {
378             splitPane.addPropertyChangeListener(propertyChangeListener);
379         }
380
381         if ((focusListener = createFocusListener()) != null) {
382             splitPane.addFocusListener(focusListener);
383         }
384     }
385
386
387     /**
388      * Installs the keyboard actions for the UI.
389      */

390     protected void installKeyboardActions() {
391     InputMap km = getInputMap(JComponent.
392                   WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
393
394     SwingUtilities.replaceUIInputMap(splitPane, JComponent.
395                        WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
396                        km);
397         LazyActionMap.installLazyActionMap(splitPane, BasicSplitPaneUI JavaDoc.class,
398                                            "SplitPane.actionMap");
399     }
400
401     InputMap getInputMap(int condition) {
402     if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
403         return (InputMap)DefaultLookup.get(splitPane, this,
404                                                "SplitPane.ancestorInputMap");
405     }
406     return null;
407     }
408
409     /**
410      * Uninstalls the UI.
411      */

412     public void uninstallUI(JComponent c) {
413         uninstallKeyboardActions();
414         uninstallListeners();
415         uninstallDefaults();
416         dividerLocationIsSet = false;
417         dividerKeyboardResize = false;
418         splitPane = null;
419     }
420
421
422     /**
423      * Uninstalls the UI defaults.
424      */

425     protected void uninstallDefaults() {
426         if(splitPane.getLayout() == layoutManager) {
427             splitPane.setLayout(null);
428         }
429
430         if(nonContinuousLayoutDivider != null) {
431             splitPane.remove(nonContinuousLayoutDivider);
432         }
433
434         LookAndFeel.uninstallBorder(splitPane);
435
436     Border JavaDoc b = divider.getBorder();
437
438     if (b instanceof UIResource JavaDoc) {
439         divider.setBorder(null);
440     }
441
442         splitPane.remove(divider);
443         divider.setBasicSplitPaneUI(null);
444         layoutManager = null;
445         divider = null;
446         nonContinuousLayoutDivider = null;
447
448         setNonContinuousLayoutDivider(null);
449
450     // sets the focus forward and backward traversal keys to null
451
// to restore the defaults
452
splitPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);
453     splitPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);
454     }
455
456
457     /**
458      * Uninstalls the event listeners for the UI.
459      */

460     protected void uninstallListeners() {
461         if (propertyChangeListener != null) {
462             splitPane.removePropertyChangeListener(propertyChangeListener);
463             propertyChangeListener = null;
464         }
465         if (focusListener != null) {
466             splitPane.removeFocusListener(focusListener);
467             focusListener = null;
468         }
469
470         keyboardUpLeftListener = null;
471         keyboardDownRightListener = null;
472         keyboardHomeListener = null;
473         keyboardEndListener = null;
474         keyboardResizeToggleListener = null;
475         handler = null;
476     }
477
478
479     /**
480      * Uninstalls the keyboard actions for the UI.
481      */

482     protected void uninstallKeyboardActions() {
483     SwingUtilities.replaceUIActionMap(splitPane, null);
484     SwingUtilities.replaceUIInputMap(splitPane, JComponent.
485                       WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
486                       null);
487     }
488
489
490     /**
491      * Creates a PropertyChangeListener for the JSplitPane UI.
492      */

493     protected PropertyChangeListener createPropertyChangeListener() {
494         return getHandler();
495     }
496
497     private Handler getHandler() {
498         if (handler == null) {
499             handler = new Handler();
500         }
501         return handler;
502     }
503
504
505     /**
506      * Creates a FocusListener for the JSplitPane UI.
507      */

508     protected FocusListener createFocusListener() {
509         return getHandler();
510     }
511
512
513     /**
514      * As of Java 2 platform v1.3 this method is no
515      * longer used. Subclassers previously using this method should
516      * instead create an Action wrapping the ActionListener, and register
517      * that Action by overriding <code>installKeyboardActions</code> and
518      * placing the Action in the SplitPane's ActionMap. Please refer to
519      * the key bindings specification for further details.
520      * <p>
521      * Creates a ActionListener for the JSplitPane UI that listens for
522      * specific key presses.
523      *
524      * @deprecated As of Java 2 platform v1.3.
525      */

526     @Deprecated JavaDoc
527     protected ActionListener createKeyboardUpLeftListener() {
528         return new KeyboardUpLeftHandler();
529     }
530
531
532     /**
533      * As of Java 2 platform v1.3 this method is no
534      * longer used. Subclassers previously using this method should
535      * instead create an Action wrapping the ActionListener, and register
536      * that Action by overriding <code>installKeyboardActions</code> and
537      * placing the Action in the SplitPane's ActionMap. Please refer to
538      * the key bindings specification for further details.
539      * <p>
540      * Creates a ActionListener for the JSplitPane UI that listens for
541      * specific key presses.
542      *
543      * @deprecated As of Java 2 platform v1.3.
544      */

545     @Deprecated JavaDoc
546     protected ActionListener createKeyboardDownRightListener() {
547         return new KeyboardDownRightHandler();
548     }
549
550
551     /**
552      * As of Java 2 platform v1.3 this method is no
553      * longer used. Subclassers previously using this method should
554      * instead create an Action wrapping the ActionListener, and register
555      * that Action by overriding <code>installKeyboardActions</code> and
556      * placing the Action in the SplitPane's ActionMap. Please refer to
557      * the key bindings specification for further details.
558      * <p>
559      * Creates a ActionListener for the JSplitPane UI that listens for
560      * specific key presses.
561      *
562      * @deprecated As of Java 2 platform v1.3.
563      */

564     @Deprecated JavaDoc
565     protected ActionListener createKeyboardHomeListener() {
566         return new KeyboardHomeHandler();
567     }
568
569
570     /**
571      * As of Java 2 platform v1.3 this method is no
572      * longer used. Subclassers previously using this method should
573      * instead create an Action wrapping the ActionListener, and register
574      * that Action by overriding <code>installKeyboardActions</code> and
575      * placing the Action in the SplitPane's ActionMap. Please refer to
576      * the key bindings specification for further details.
577      * <p>
578      * Creates a ActionListener for the JSplitPane UI that listens for
579      * specific key presses.
580      *
581      * @deprecated As of Java 2 platform v1.3.
582      */

583     @Deprecated JavaDoc
584     protected ActionListener createKeyboardEndListener() {
585         return new KeyboardEndHandler();
586     }
587
588
589     /**
590      * As of Java 2 platform v1.3 this method is no
591      * longer used. Subclassers previously using this method should
592      * instead create an Action wrapping the ActionListener, and register
593      * that Action by overriding <code>installKeyboardActions</code> and
594      * placing the Action in the SplitPane's ActionMap. Please refer to
595      * the key bindings specification for further details.
596      * <p>
597      * Creates a ActionListener for the JSplitPane UI that listens for
598      * specific key presses.
599      *
600      * @deprecated As of Java 2 platform v1.3.
601      */

602     @Deprecated JavaDoc
603     protected ActionListener createKeyboardResizeToggleListener() {
604         return new KeyboardResizeToggleHandler();
605     }
606
607
608     /**
609      * Returns the orientation for the JSplitPane.
610      */

611     public int getOrientation() {
612         return orientation;
613     }
614
615
616     /**
617      * Set the orientation for the JSplitPane.
618      */

619     public void setOrientation(int orientation) {
620         this.orientation = orientation;
621     }
622
623
624     /**
625      * Determines wether the JSplitPane is set to use a continuous layout.
626      */

627     public boolean isContinuousLayout() {
628         return continuousLayout;
629     }
630
631
632     /**
633      * Turn continuous layout on/off.
634      */

635     public void setContinuousLayout(boolean b) {
636         continuousLayout = b;
637     }
638
639
640     /**
641      * Returns the last drag location of the JSplitPane.
642      */

643     public int getLastDragLocation() {
644         return lastDragLocation;
645     }
646
647
648     /**
649      * Set the last drag location of the JSplitPane.
650      */

651     public void setLastDragLocation(int l) {
652         lastDragLocation = l;
653     }
654
655     /**
656      * @return increment via keyboard methods.
657      */

658     int getKeyboardMoveIncrement() {
659     return KEYBOARD_DIVIDER_MOVE_OFFSET;
660     }
661
662     /**
663      * Implementation of the PropertyChangeListener
664      * that the JSplitPane UI uses.
665      * <p>
666      * This class should be treated as a &quot;protected&quot; inner class.
667      * Instantiate it only within subclasses of BasicSplitPaneUI.
668      */

669     public class PropertyHandler implements PropertyChangeListener
670     {
671         // NOTE: This class exists only for backward compatability. All
672
// its functionality has been moved into Handler. If you need to add
673
// new functionality add it to the Handler, but make sure this
674
// class calls into the Handler.
675

676         /**
677          * Messaged from the <code>JSplitPane</code> the receiver is
678          * contained in. May potentially reset the layout manager and cause a
679          * <code>validate</code> to be sent.
680          */

681         public void propertyChange(PropertyChangeEvent e) {
682             getHandler().propertyChange(e);
683         }
684     }
685
686
687     /**
688      * Implementation of the FocusListener that the JSplitPane UI uses.
689      * <p>
690      * This class should be treated as a &quot;protected&quot; inner class.
691      * Instantiate it only within subclasses of BasicSplitPaneUI.
692      */

693     public class FocusHandler extends FocusAdapter
694     {
695         // NOTE: This class exists only for backward compatability. All
696
// its functionality has been moved into Handler. If you need to add
697
// new functionality add it to the Handler, but make sure this
698
// class calls into the Handler.
699
public void focusGained(FocusEvent ev) {
700             getHandler().focusGained(ev);
701         }
702
703         public void focusLost(FocusEvent ev) {
704             getHandler().focusLost(ev);
705         }
706     }
707     
708
709     /**
710      * Implementation of an ActionListener that the JSplitPane UI uses for
711      * handling specific key presses.
712      * <p>
713      * This class should be treated as a &quot;protected&quot; inner class.
714      * Instantiate it only within subclasses of BasicSplitPaneUI.
715      */

716     public class KeyboardUpLeftHandler implements ActionListener
717     {
718         public void actionPerformed(ActionEvent ev) {
719             if (dividerKeyboardResize) {
720         splitPane.setDividerLocation(Math.max(0,getDividerLocation
721                   (splitPane) - getKeyboardMoveIncrement()));
722             }
723         }
724     }
725
726     /**
727      * Implementation of an ActionListener that the JSplitPane UI uses for
728      * handling specific key presses.
729      * <p>
730      * This class should be treated as a &quot;protected&quot; inner class.
731      * Instantiate it only within subclasses of BasicSplitPaneUI.
732      */

733     public class KeyboardDownRightHandler implements ActionListener
734     {
735         public void actionPerformed(ActionEvent ev) {
736             if (dividerKeyboardResize) {
737                 splitPane.setDividerLocation(getDividerLocation(splitPane) +
738                          getKeyboardMoveIncrement());
739             }
740         }
741     }
742
743
744     /**
745      * Implementation of an ActionListener that the JSplitPane UI uses for
746      * handling specific key presses.
747      * <p>
748      * This class should be treated as a &quot;protected&quot; inner class.
749      * Instantiate it only within subclasses of BasicSplitPaneUI.
750      */

751     public class KeyboardHomeHandler implements ActionListener
752     {
753         public void actionPerformed(ActionEvent ev) {
754             if (dividerKeyboardResize) {
755                 splitPane.setDividerLocation(0);
756             }
757         }
758     }
759     
760
761     /**
762      * Implementation of an ActionListener that the JSplitPane UI uses for
763      * handling specific key presses.
764      * <p>
765      * This class should be treated as a &quot;protected&quot; inner class.
766      * Instantiate it only within subclasses of BasicSplitPaneUI.
767      */

768     public class KeyboardEndHandler implements ActionListener
769     {
770         public void actionPerformed(ActionEvent ev) {
771             if (dividerKeyboardResize) {
772         Insets insets = splitPane.getInsets();
773         int bottomI = (insets != null) ? insets.bottom : 0;
774         int rightI = (insets != null) ? insets.right : 0;
775
776                 if (orientation == JSplitPane.VERTICAL_SPLIT) {
777                     splitPane.setDividerLocation(splitPane.getHeight() -
778                                        bottomI);
779                 }
780                 else {
781                     splitPane.setDividerLocation(splitPane.getWidth() -
782                          rightI);
783                 }
784             }
785         }
786     }
787
788
789     /**
790      * Implementation of an ActionListener that the JSplitPane UI uses for
791      * handling specific key presses.
792      * <p>
793      * This class should be treated as a &quot;protected&quot; inner class.
794      * Instantiate it only within subclasses of BasicSplitPaneUI.
795      */

796     public class KeyboardResizeToggleHandler implements ActionListener
797     {
798         public void actionPerformed(ActionEvent ev) {
799             if (!dividerKeyboardResize) {
800                 splitPane.requestFocus();
801             }
802         }
803     }
804
805     /**
806      * Returns the divider between the top Components.
807      */

808     public BasicSplitPaneDivider JavaDoc getDivider() {
809         return divider;
810     }
811
812
813     /**
814      * Returns the default non continuous layout divider, which is an
815      * instanceof Canvas that fills the background in dark gray.
816      */

817     protected Component createDefaultNonContinuousLayoutDivider() {
818         return new Canvas() {
819             public void paint(Graphics g) {
820                 if(!isContinuousLayout() && getLastDragLocation() != -1) {
821                     Dimension size = splitPane.getSize();
822
823                     g.setColor(dividerDraggingColor);
824                     if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
825                         g.fillRect(0, 0, dividerSize - 1, size.height - 1);
826                     } else {
827                         g.fillRect(0, 0, size.width - 1, dividerSize - 1);
828                     }
829                 }
830             }
831         };
832     }
833
834
835     /**
836      * Sets the divider to use when the splitPane is configured to
837      * not continuously layout. This divider will only be used during a
838      * dragging session. It is recommended that the passed in component
839      * be a heavy weight.
840      */

841     protected void setNonContinuousLayoutDivider(Component newDivider) {
842         setNonContinuousLayoutDivider(newDivider, true);
843     }
844
845
846     /**
847      * Sets the divider to use.
848      */

849     protected void setNonContinuousLayoutDivider(Component newDivider,
850         boolean rememberSizes) {
851         rememberPaneSizes = rememberSizes;
852         if(nonContinuousLayoutDivider != null && splitPane != null) {
853             splitPane.remove(nonContinuousLayoutDivider);
854         }
855         nonContinuousLayoutDivider = newDivider;
856     }
857
858     private void addHeavyweightDivider() {
859         if(nonContinuousLayoutDivider != null && splitPane != null) {
860
861             /* Needs to remove all the components and re-add them! YECK! */
862         // This is all done so that the nonContinuousLayoutDivider will
863
// be drawn on top of the other components, without this, one
864
// of the heavyweights will draw over the divider!
865
Component leftC = splitPane.getLeftComponent();
866             Component rightC = splitPane.getRightComponent();
867         int lastLocation = splitPane.
868                                       getDividerLocation();
869
870             if(leftC != null)
871                 splitPane.setLeftComponent(null);
872             if(rightC != null)
873                 splitPane.setRightComponent(null);
874             splitPane.remove(divider);
875             splitPane.add(nonContinuousLayoutDivider, BasicSplitPaneUI.
876                           NON_CONTINUOUS_DIVIDER,
877                           splitPane.getComponentCount());
878             splitPane.setLeftComponent(leftC);
879             splitPane.setRightComponent(rightC);
880             splitPane.add(divider, JSplitPane.DIVIDER);
881             if(rememberPaneSizes) {
882         splitPane.setDividerLocation(lastLocation);
883         }
884         }
885  
886     }
887
888
889     /**
890      * Returns the divider to use when the splitPane is configured to
891      * not continuously layout. This divider will only be used during a
892      * dragging session.
893      */

894     public Component getNonContinuousLayoutDivider() {
895         return nonContinuousLayoutDivider;
896     }
897
898
899     /**
900      * Returns the splitpane this instance is currently contained
901      * in.
902      */

903     public JSplitPane getSplitPane() {
904         return splitPane;
905     }
906
907
908     /**
909      * Creates the default divider.
910      */

911     public BasicSplitPaneDivider JavaDoc createDefaultDivider() {
912         return new BasicSplitPaneDivider JavaDoc(this);
913     }
914
915
916     /**
917      * Messaged to reset the preferred sizes.
918      */

919     public void resetToPreferredSizes(JSplitPane jc) {
920         if(splitPane != null) {
921             layoutManager.resetToPreferredSizes();
922             splitPane.revalidate();
923         splitPane.repaint();
924         }
925     }
926
927
928     /**
929      * Sets the location of the divider to location.
930      */

931     public void setDividerLocation(JSplitPane jc, int location) {
932     if (!ignoreDividerLocationChange) {
933         dividerLocationIsSet = true;
934         splitPane.revalidate();
935         splitPane.repaint();
936
937             if (keepHidden) {
938         Insets insets = splitPane.getInsets();
939         int orientation = splitPane.getOrientation();
940         if ((orientation == JSplitPane.VERTICAL_SPLIT &&
941              location != insets.top &&
942              location != splitPane.getHeight()-divider.getHeight()-insets.top) ||
943             (orientation == JSplitPane.HORIZONTAL_SPLIT &&
944              location != insets.left &&
945              location != splitPane.getWidth()-divider.getWidth()-insets.left)) {
946             setKeepHidden(false);
947         }
948         }
949     }
950     else {
951         ignoreDividerLocationChange = false;
952     }
953     }
954
955
956     /**
957      * Returns the location of the divider, which may differ from what
958      * the splitpane thinks the location of the divider is.
959      */

960     public int getDividerLocation(JSplitPane jc) {
961         if(orientation == JSplitPane.HORIZONTAL_SPLIT)
962             return divider.getLocation().x;
963         return divider.getLocation().y;
964     }
965
966
967     /**
968      * Gets the minimum location of the divider.
969      */

970     public int getMinimumDividerLocation(JSplitPane jc) {
971         int minLoc = 0;
972         Component leftC = splitPane.getLeftComponent();
973
974         if ((leftC != null) && (leftC.isVisible())) {
975             Insets insets = splitPane.getInsets();
976             Dimension minSize = leftC.getMinimumSize();
977             if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
978                 minLoc = minSize.width;
979             } else {
980                 minLoc = minSize.height;
981             }
982             if(insets != null) {
983                 if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
984                     minLoc += insets.left;
985                 } else {
986                     minLoc += insets.top;
987                 }
988             }
989         }
990         return minLoc;
991     }
992
993
994     /**
995      * Gets the maximum location of the divider.
996      */

997     public int getMaximumDividerLocation(JSplitPane jc) {
998         Dimension splitPaneSize = splitPane.getSize();
999         int maxLoc = 0;
1000        Component rightC = splitPane.getRightComponent();
1001
1002        if (rightC != null) {
1003            Insets insets = splitPane.getInsets();
1004            Dimension minSize = new Dimension(0, 0);
1005            if (rightC.isVisible()) {
1006                minSize = rightC.getMinimumSize();
1007            }
1008            if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1009                maxLoc = splitPaneSize.width - minSize.width;
1010            } else {
1011                maxLoc = splitPaneSize.height - minSize.height;
1012            }
1013            maxLoc -= dividerSize;
1014            if(insets != null) {
1015                if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1016                    maxLoc -= insets.right;
1017                } else {
1018                    maxLoc -= insets.top;
1019                }
1020            }
1021        }
1022        return Math.max(getMinimumDividerLocation(splitPane), maxLoc);
1023    }
1024
1025
1026    /**
1027     * Messaged after the JSplitPane the receiver is providing the look
1028     * and feel for paints its children.
1029     */

1030    public void finishedPaintingChildren(JSplitPane jc, Graphics g) {
1031        if(jc == splitPane && getLastDragLocation() != -1 &&
1032           !isContinuousLayout() && !draggingHW) {
1033            Dimension size = splitPane.getSize();
1034
1035            g.setColor(dividerDraggingColor);
1036            if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1037                g.fillRect(getLastDragLocation(), 0, dividerSize - 1,
1038                           size.height - 1);
1039            } else {
1040                g.fillRect(0, lastDragLocation, size.width - 1,
1041                           dividerSize - 1);
1042            }
1043        }
1044    }
1045
1046
1047    /**
1048     * Messaged to paint the look and feel.
1049     */

1050    public void paint(Graphics g, JComponent jc) {
1051    if (!painted && splitPane.getDividerLocation()<0) {
1052        ignoreDividerLocationChange = true;
1053        splitPane.setDividerLocation(getDividerLocation(splitPane));
1054    }
1055    painted = true;
1056    }
1057
1058
1059    /**
1060     * Returns the preferred size for the passed in component,
1061     * This is passed off to the current layoutmanager.
1062     */

1063    public Dimension getPreferredSize(JComponent jc) {
1064        if(splitPane != null)
1065            return layoutManager.preferredLayoutSize(splitPane);
1066        return new Dimension(0, 0);
1067    }
1068
1069
1070    /**
1071     * Returns the minimum size for the passed in component,
1072     * This is passed off to the current layoutmanager.
1073     */

1074    public Dimension getMinimumSize(JComponent jc) {
1075        if(splitPane != null)
1076            return layoutManager.minimumLayoutSize(splitPane);
1077        return new Dimension(0, 0);
1078    }
1079
1080
1081    /**
1082     * Returns the maximum size for the passed in component,
1083     * This is passed off to the current layoutmanager.
1084     */

1085    public Dimension getMaximumSize(JComponent jc) {
1086        if(splitPane != null)
1087            return layoutManager.maximumLayoutSize(splitPane);
1088        return new Dimension(0, 0);
1089    }
1090
1091
1092    /**
1093     * Returns the insets. The insets are returned from the border insets
1094     * of the current border.
1095     */

1096    public Insets getInsets(JComponent jc) {
1097        return null;
1098    }
1099
1100
1101    /**
1102     * Resets the layout manager based on orientation and messages it
1103     * with invalidateLayout to pull in appropriate Components.
1104     */

1105    protected void resetLayoutManager() {
1106        if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1107            layoutManager = new BasicHorizontalLayoutManager(0);
1108        } else {
1109            layoutManager = new BasicHorizontalLayoutManager(1);
1110        }
1111        splitPane.setLayout(layoutManager);
1112        layoutManager.updateComponents();
1113        splitPane.revalidate();
1114        splitPane.repaint();
1115    }
1116
1117    /**
1118     * Set the value to indicate if one of the splitpane sides is expanded.
1119     */

1120    void setKeepHidden(boolean keepHidden) {
1121    this.keepHidden = keepHidden;
1122    }
1123
1124    /**
1125     * The value returned indicates if one of the splitpane sides is expanded.
1126     * @return true if one of the splitpane sides is expanded, false otherwise.
1127     */

1128    private boolean getKeepHidden() {
1129    return keepHidden;
1130    }
1131
1132    /**
1133     * Should be messaged before the dragging session starts, resets
1134     * lastDragLocation and dividerSize.
1135     */

1136    protected void startDragging() {
1137        Component leftC = splitPane.getLeftComponent();
1138        Component rightC = splitPane.getRightComponent();
1139        ComponentPeer cPeer;
1140
1141        beginDragDividerLocation = getDividerLocation(splitPane);
1142        draggingHW = false;
1143        if(leftC != null && (cPeer = leftC.getPeer()) != null &&
1144           !(cPeer instanceof LightweightPeer)) {
1145            draggingHW = true;
1146        } else if(rightC != null && (cPeer = rightC.getPeer()) != null
1147                  && !(cPeer instanceof LightweightPeer)) {
1148            draggingHW = true;
1149        }
1150        if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1151            setLastDragLocation(divider.getBounds().x);
1152            dividerSize = divider.getSize().width;
1153            if(!isContinuousLayout() && draggingHW) {
1154                nonContinuousLayoutDivider.setBounds
1155                        (getLastDragLocation(), 0, dividerSize,
1156                         splitPane.getHeight());
1157              addHeavyweightDivider();
1158            }
1159        } else {
1160            setLastDragLocation(divider.getBounds().y);
1161            dividerSize = divider.getSize().height;
1162            if(!isContinuousLayout() && draggingHW) {
1163                nonContinuousLayoutDivider.setBounds
1164                        (0, getLastDragLocation(), splitPane.getWidth(),
1165                         dividerSize);
1166              addHeavyweightDivider();
1167            }
1168        }
1169    }
1170
1171
1172    /**
1173     * Messaged during a dragging session to move the divider to the
1174     * passed in location. If continuousLayout is true the location is
1175     * reset and the splitPane validated.
1176     */

1177    protected void dragDividerTo(int location) {
1178        if(getLastDragLocation() != location) {
1179            if(isContinuousLayout()) {
1180        splitPane.setDividerLocation(location);
1181                setLastDragLocation(location);
1182            } else {
1183                int lastLoc = getLastDragLocation();
1184
1185                setLastDragLocation(location);
1186                if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1187                    if(draggingHW) {
1188                        nonContinuousLayoutDivider.setLocation(
1189                            getLastDragLocation(), 0);
1190                    } else {
1191            int splitHeight = splitPane.getHeight();
1192                        splitPane.repaint(lastLoc, 0, dividerSize,
1193                                          splitHeight);
1194                        splitPane.repaint(location, 0, dividerSize,
1195                                          splitHeight);
1196                    }
1197                } else {
1198                    if(draggingHW) {
1199                        nonContinuousLayoutDivider.setLocation(0,
1200                            getLastDragLocation());
1201                    } else {
1202            int splitWidth = splitPane.getWidth();
1203
1204                        splitPane.repaint(0, lastLoc, splitWidth,
1205                                          dividerSize);
1206                        splitPane.repaint(0, location, splitWidth,
1207                                          dividerSize);
1208                    }
1209                }
1210            }
1211        }
1212    }
1213
1214
1215    /**
1216     * Messaged to finish the dragging session. If not continuous display
1217     * the dividers location will be reset.
1218     */

1219    protected void finishDraggingTo(int location) {
1220        dragDividerTo(location);
1221        setLastDragLocation(-1);
1222        if(!isContinuousLayout()) {
1223            Component leftC = splitPane.getLeftComponent();
1224            Rectangle leftBounds = leftC.getBounds();
1225
1226        if (draggingHW) {
1227        if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1228                    nonContinuousLayoutDivider.setLocation(-dividerSize, 0);
1229        }
1230        else {
1231                    nonContinuousLayoutDivider.setLocation(0, -dividerSize);
1232        }
1233        splitPane.remove(nonContinuousLayoutDivider);
1234        }
1235        splitPane.setDividerLocation(location);
1236        }
1237    }
1238
1239
1240    /**
1241     * As of Java 2 platform v1.3 this method is no longer used. Instead
1242     * you should set the border on the divider.
1243     * <p>
1244     * Returns the width of one side of the divider border.
1245     *
1246     * @deprecated As of Java 2 platform v1.3, instead set the border on the
1247     * divider.
1248     */

1249    @Deprecated JavaDoc
1250    protected int getDividerBorderSize() {
1251        return 1;
1252    }
1253
1254
1255    /**
1256     * LayoutManager for JSplitPanes that have an orientation of
1257     * HORIZONTAL_SPLIT.
1258     */

1259    public class BasicHorizontalLayoutManager implements LayoutManager2
1260    {
1261        /* left, right, divider. (in this exact order) */
1262        protected int[] sizes;
1263        protected Component[] components;
1264    /** Size of the splitpane the last time laid out. */
1265    private int lastSplitPaneSize;
1266    /** True if resetToPreferredSizes has been invoked. */
1267    private boolean doReset;
1268    /** Axis, 0 for horizontal, or 1 for veritcal. */
1269    private int axis;
1270
1271
1272    BasicHorizontalLayoutManager() {
1273        this(0);
1274    }
1275
1276        BasicHorizontalLayoutManager(int axis) {
1277        this.axis = axis;
1278            components = new Component[3];
1279            components[0] = components[1] = components[2] = null;
1280            sizes = new int[3];
1281        }
1282
1283    //
1284
// LayoutManager
1285
//
1286

1287    /**
1288         * Does the actual layout.
1289         */

1290        public void layoutContainer(Container container) {
1291            Dimension containerSize = container.getSize();
1292
1293            // If the splitpane has a zero size then no op out of here.
1294
// If we execute this function now, we're going to cause ourselves
1295
// much grief.
1296
if (containerSize.height <= 0 || containerSize.width <= 0 ) {
1297        lastSplitPaneSize = 0;
1298                return;
1299            }
1300
1301        int spDividerLocation = splitPane.getDividerLocation();
1302            Insets insets = splitPane.getInsets();
1303        int availableSize = getAvailableSize(containerSize,
1304                             insets);
1305        int newSize = getSizeForPrimaryAxis(containerSize);
1306        int beginLocation = getDividerLocation(splitPane);
1307        int dOffset = getSizeForPrimaryAxis(insets, true);
1308        Dimension dSize = (components[2] == null) ? null :
1309                         components[2].getPreferredSize();
1310
1311        if ((doReset && !dividerLocationIsSet) || spDividerLocation < 0) {
1312        resetToPreferredSizes(availableSize);
1313        }
1314        else if (lastSplitPaneSize <= 0 ||
1315             availableSize == lastSplitPaneSize || !painted ||
1316             (dSize != null &&
1317              getSizeForPrimaryAxis(dSize) != sizes[2])) {
1318        if (dSize != null) {
1319            sizes[2] = getSizeForPrimaryAxis(dSize);
1320        }
1321        else {
1322            sizes[2] = 0;
1323        }
1324        setDividerLocation(spDividerLocation - dOffset, availableSize);
1325        dividerLocationIsSet = false;
1326        }
1327        else if (availableSize != lastSplitPaneSize) {
1328        distributeSpace(availableSize - lastSplitPaneSize,
1329                                getKeepHidden());
1330        }
1331        doReset = false;
1332        dividerLocationIsSet = false;
1333        lastSplitPaneSize = availableSize;
1334
1335            // Reset the bounds of each component
1336
int nextLocation = getInitialLocation(insets);
1337        int counter = 0;
1338
1339            while (counter < 3) {
1340                if (components[counter] != null &&
1341            components[counter].isVisible()) {
1342                    setComponentToSize(components[counter], sizes[counter],
1343                                       nextLocation, insets, containerSize);
1344                    nextLocation += sizes[counter];
1345                }
1346                switch (counter) {
1347                case 0:
1348                    counter = 2;
1349                    break;
1350                case 2:
1351                    counter = 1;
1352                    break;
1353                case 1:
1354                    counter = 3;
1355                    break;
1356                }
1357            }
1358        if (painted) {
1359        // This is tricky, there is never a good time for us
1360
// to push the value to the splitpane, painted appears to
1361
// the best time to do it. What is really needed is
1362
// notification that layout has completed.
1363
int newLocation = getDividerLocation(splitPane);
1364
1365        if (newLocation != (spDividerLocation - dOffset)) {
1366            int lastLocation = splitPane.getLastDividerLocation();
1367
1368            ignoreDividerLocationChange = true;
1369            try {
1370            splitPane.setDividerLocation(newLocation);
1371            // This is not always needed, but is rather tricky
1372
// to determine when... The case this is needed for
1373
// is if the user sets the divider location to some
1374
// bogus value, say 0, and the actual value is 1, the
1375
// call to setDividerLocation(1) will preserve the
1376
// old value of 0, when we really want the divider
1377
// location value before the call. This is needed for
1378
// the one touch buttons.
1379
splitPane.setLastDividerLocation(lastLocation);
1380            } finally {
1381            ignoreDividerLocationChange = false;
1382            }
1383        }
1384        }
1385        }
1386
1387
1388        /**
1389         * Adds the component at place. Place must be one of
1390         * JSplitPane.LEFT, RIGHT, TOP, BOTTOM, or null (for the
1391         * divider).
1392         */

1393        public void addLayoutComponent(String JavaDoc place, Component component) {
1394            boolean isValid = true;
1395
1396            if(place != null) {
1397                if(place.equals(JSplitPane.DIVIDER)) {
1398                    /* Divider. */
1399                    components[2] = component;
1400                    sizes[2] = getSizeForPrimaryAxis(component.
1401                             getPreferredSize());
1402                } else if(place.equals(JSplitPane.LEFT) ||
1403                          place.equals(JSplitPane.TOP)) {
1404                    components[0] = component;
1405                    sizes[0] = 0;
1406                } else if(place.equals(JSplitPane.RIGHT) ||
1407                          place.equals(JSplitPane.BOTTOM)) {
1408                    components[1] = component;
1409                    sizes[1] = 0;
1410                } else if(!place.equals(
1411                                    BasicSplitPaneUI.NON_CONTINUOUS_DIVIDER))
1412                    isValid = false;
1413            } else {
1414                isValid = false;
1415            }
1416            if(!isValid)
1417                throw new IllegalArgumentException JavaDoc("cannot add to layout: " +
1418                    "unknown constraint: " +
1419                    place);
1420        doReset = true;
1421        }
1422
1423
1424        /**
1425         * Returns the minimum size needed to contain the children.
1426         * The width is the sum of all the childrens min widths and
1427         * the height is the largest of the childrens minimum heights.
1428         */

1429        public Dimension minimumLayoutSize(Container container) {
1430            int minPrimary = 0;
1431            int minSecondary = 0;
1432            Insets insets = splitPane.getInsets();
1433
1434            for (int counter=0; counter<3; counter++) {
1435                if(components[counter] != null) {
1436                    Dimension minSize = components[counter].getMinimumSize();
1437            int secSize = getSizeForSecondaryAxis(minSize);
1438
1439                    minPrimary += getSizeForPrimaryAxis(minSize);
1440                    if(secSize > minSecondary)
1441                        minSecondary = secSize;
1442                }
1443            }
1444            if(insets != null) {
1445                minPrimary += getSizeForPrimaryAxis(insets, true) +
1446                      getSizeForPrimaryAxis(insets, false);
1447        minSecondary += getSizeForSecondaryAxis(insets, true) +
1448                      getSizeForSecondaryAxis(insets, false);
1449            }
1450        if (axis == 0) {
1451        return new Dimension(minPrimary, minSecondary);
1452        }
1453        return new Dimension(minSecondary, minPrimary);
1454        }
1455
1456
1457        /**
1458         * Returns the preferred size needed to contain the children.
1459         * The width is the sum of all the childrens preferred widths and
1460         * the height is the largest of the childrens preferred heights.
1461         */

1462        public Dimension preferredLayoutSize(Container container) {
1463            int prePrimary = 0;
1464            int preSecondary = 0;
1465            Insets insets = splitPane.getInsets();
1466
1467            for(int counter = 0; counter < 3; counter++) {
1468                if(components[counter] != null) {
1469            Dimension preSize = components[counter].
1470                              getPreferredSize();
1471            int secSize = getSizeForSecondaryAxis(preSize);
1472
1473                    prePrimary += getSizeForPrimaryAxis(preSize);
1474                    if(secSize > preSecondary)
1475                        preSecondary = secSize;
1476                }
1477            }
1478            if(insets != null) {
1479                prePrimary += getSizeForPrimaryAxis(insets, true) +
1480                      getSizeForPrimaryAxis(insets, false);
1481        preSecondary += getSizeForSecondaryAxis(insets, true) +
1482                      getSizeForSecondaryAxis(insets, false);
1483            }
1484        if (axis == 0) {
1485        return new Dimension(prePrimary, preSecondary);
1486        }
1487        return new Dimension(preSecondary, prePrimary);
1488        }
1489
1490
1491        /**
1492         * Removes the specified component from our knowledge.
1493         */

1494        public void removeLayoutComponent(Component component) {
1495            for(int counter = 0; counter < 3; counter++) {
1496                if(components[counter] == component) {
1497                    components[counter] = null;
1498                    sizes[counter] = 0;
1499            doReset = true;
1500                }
1501            }
1502        }
1503
1504
1505        //
1506
// LayoutManager2
1507
//
1508

1509
1510        /**
1511         * Adds the specified component to the layout, using the specified
1512         * constraint object.
1513         * @param comp the component to be added
1514         * @param constraints where/how the component is added to the layout.
1515         */

1516        public void addLayoutComponent(Component comp, Object JavaDoc constraints) {
1517            if ((constraints == null) || (constraints instanceof String JavaDoc)) {
1518                addLayoutComponent((String JavaDoc)constraints, comp);
1519            } else {
1520                throw new IllegalArgumentException JavaDoc("cannot add to layout: " +
1521                                                   "constraint must be a " +
1522                                                   "string (or null)");
1523            }
1524        }
1525
1526
1527        /**
1528         * Returns the alignment along the x axis. This specifies how
1529         * the component would like to be aligned relative to other
1530         * components. The value should be a number between 0 and 1
1531         * where 0 represents alignment along the origin, 1 is aligned
1532         * the furthest away from the origin, 0.5 is centered, etc.
1533         */

1534        public float getLayoutAlignmentX(Container target) {
1535            return 0.0f;
1536        }
1537
1538
1539        /**
1540         * Returns the alignment along the y axis. This specifies how
1541         * the component would like to be aligned relative to other
1542         * components. The value should be a number between 0 and 1
1543         * where 0 represents alignment along the origin, 1 is aligned
1544         * the furthest away from the origin, 0.5 is centered, etc.
1545         */

1546        public float getLayoutAlignmentY(Container target) {
1547            return 0.0f;
1548        }
1549
1550
1551        /**
1552         * Does nothing. If the developer really wants to change the
1553         * size of one of the views JSplitPane.resetToPreferredSizes should
1554         * be messaged.
1555         */

1556        public void invalidateLayout(Container c) {
1557        }
1558
1559
1560        /**
1561         * Returns the maximum layout size, which is Integer.MAX_VALUE
1562         * in both directions.
1563         */

1564        public Dimension maximumLayoutSize(Container target) {
1565            return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1566        }
1567
1568
1569    //
1570
// New methods.
1571
//
1572

1573        /**
1574         * Marks the receiver so that the next time this instance is
1575         * laid out it'll ask for the preferred sizes.
1576         */

1577        public void resetToPreferredSizes() {
1578        doReset = true;
1579        }
1580
1581        /**
1582         * Resets the size of the Component at the passed in location.
1583         */

1584        protected void resetSizeAt(int index) {
1585            sizes[index] = 0;
1586        doReset = true;
1587        }
1588
1589
1590        /**
1591         * Sets the sizes to <code>newSizes</code>.
1592         */

1593        protected void setSizes(int[] newSizes) {
1594            System.arraycopy(newSizes, 0, sizes, 0, 3);
1595        }
1596
1597
1598        /**
1599         * Returns the sizes of the components.
1600         */

1601        protected int[] getSizes() {
1602            int[] retSizes = new int[3];
1603
1604            System.arraycopy(sizes, 0, retSizes, 0, 3);
1605            return retSizes;
1606        }
1607
1608
1609        /**
1610         * Returns the width of the passed in Components preferred size.
1611         */

1612        protected int getPreferredSizeOfComponent(Component c) {
1613        return getSizeForPrimaryAxis(c.getPreferredSize());
1614        }
1615
1616
1617        /**
1618         * Returns the width of the passed in Components minimum size.
1619         */

1620        int getMinimumSizeOfComponent(Component c) {
1621        return getSizeForPrimaryAxis(c.getMinimumSize());
1622        }
1623
1624
1625        /**
1626         * Returns the width of the passed in component.
1627         */

1628        protected int getSizeOfComponent(Component c) {
1629        return getSizeForPrimaryAxis(c.getSize());
1630        }
1631
1632
1633        /**
1634         * Returns the available width based on the container size and
1635         * Insets.
1636         */

1637        protected int getAvailableSize(Dimension containerSize,
1638                                       Insets insets) {
1639            if(insets == null)
1640                return getSizeForPrimaryAxis(containerSize);
1641            return (getSizeForPrimaryAxis(containerSize) -
1642            (getSizeForPrimaryAxis(insets, true) +
1643             getSizeForPrimaryAxis(insets, false)));
1644        }
1645
1646
1647        /**
1648         * Returns the left inset, unless the Insets are null in which case
1649         * 0 is returned.
1650         */

1651        protected int getInitialLocation(Insets insets) {
1652            if(insets != null)
1653                return getSizeForPrimaryAxis(insets, true);
1654            return 0;
1655        }
1656
1657
1658        /**
1659         * Sets the width of the component c to be size, placing its
1660         * x location at location, y to the insets.top and height
1661         * to the containersize.height less the top and bottom insets.
1662         */

1663        protected void setComponentToSize(Component c, int size,
1664                                          int location, Insets insets,
1665                                          Dimension containerSize) {
1666            if(insets != null) {
1667        if (axis == 0) {
1668            c.setBounds(location, insets.top, size,
1669                containerSize.height -
1670                (insets.top + insets.bottom));
1671        }
1672        else {
1673            c.setBounds(insets.left, location, containerSize.width -
1674                (insets.left + insets.right), size);
1675        }
1676            }
1677        else {
1678                if (axis == 0) {
1679            c.setBounds(location, 0, size, containerSize.height);
1680        }
1681        else {
1682            c.setBounds(0, location, containerSize.width, size);
1683        }
1684            }
1685        }
1686
1687    /**
1688     * If the axis == 0, the width is returned, otherwise the height.
1689     */

1690    int getSizeForPrimaryAxis(Dimension size) {
1691        if (axis == 0) {
1692        return size.width;
1693        }
1694        return size.height;
1695    }
1696
1697    /**
1698     * If the axis == 0, the width is returned, otherwise the height.
1699     */

1700    int getSizeForSecondaryAxis(Dimension size) {
1701        if (axis == 0) {
1702        return size.height;
1703        }
1704        return size.width;
1705    }
1706
1707    /**
1708     * Returns a particular value of the inset identified by the
1709     * axis and <code>isTop</code><p>
1710     * axis isTop
1711     * 0 true - left
1712     * 0 false - right
1713     * 1 true - top
1714     * 1 false - bottom
1715     */

1716    int getSizeForPrimaryAxis(Insets insets, boolean isTop) {
1717        if (axis == 0) {
1718        if (isTop) {
1719            return insets.left;
1720        }
1721        return insets.right;
1722        }
1723        if (isTop) {
1724        return insets.top;
1725        }
1726        return insets.bottom;
1727    }
1728
1729    /**
1730     * Returns a particular value of the inset identified by the
1731     * axis and <code>isTop</code><p>
1732     * axis isTop
1733     * 0 true - left
1734     * 0 false - right
1735     * 1 true - top
1736     * 1 false - bottom
1737     */

1738    int getSizeForSecondaryAxis(Insets insets, boolean isTop) {
1739        if (axis == 0) {
1740        if (isTop) {
1741            return insets.top;
1742        }
1743        return insets.bottom;
1744        }
1745        if (isTop) {
1746        return insets.left;
1747        }
1748        return insets.right;
1749    }
1750
1751        /**
1752         * Determines the components. This should be called whenever
1753         * a new instance of this is installed into an existing
1754         * SplitPane.
1755         */

1756        protected void updateComponents() {
1757            Component comp;
1758
1759            comp = splitPane.getLeftComponent();
1760            if(components[0] != comp) {
1761                components[0] = comp;
1762                if(comp == null) {
1763                    sizes[0] = 0;
1764                } else {
1765                    sizes[0] = -1;
1766                }
1767            }
1768
1769            comp = splitPane.getRightComponent();
1770            if(components[1] != comp) {
1771                components[1] = comp;
1772                if(comp == null) {
1773                    sizes[1] = 0;
1774                } else {
1775                    sizes[1] = -1;
1776                }
1777            }
1778
1779            /* Find the divider. */
1780            Component[] children = splitPane.getComponents();
1781            Component oldDivider = components[2];
1782
1783            components[2] = null;
1784            for(int counter = children.length - 1; counter >= 0; counter--) {
1785                if(children[counter] != components[0] &&
1786                   children[counter] != components[1] &&
1787                   children[counter] != nonContinuousLayoutDivider) {
1788                    if(oldDivider != children[counter]) {
1789                        components[2] = children[counter];
1790                    } else {
1791                        components[2] = oldDivider;
1792                    }
1793                    break;
1794                }
1795            }
1796            if(components[2] == null) {
1797        sizes[2] = 0;
1798        }
1799        else {
1800        sizes[2] = getSizeForPrimaryAxis(components[2].getPreferredSize());
1801        }
1802        }
1803
1804    /**
1805     * Resets the size of the first component to <code>leftSize</code>,
1806     * and the right component to the remainder of the space.
1807     */

1808    void setDividerLocation(int leftSize, int availableSize) {
1809        boolean lValid = (components[0] != null &&
1810                       components[0].isVisible());
1811        boolean rValid = (components[1] != null &&
1812                       components[1].isVisible());
1813        boolean dValid = (components[2] != null &&
1814                       components[2].isVisible());
1815        int max = availableSize;
1816
1817        if (dValid) {
1818        max -= sizes[2];
1819        }
1820        leftSize = Math.max(0, Math.min(leftSize, max));
1821        if (lValid) {
1822        if (rValid) {
1823            sizes[0] = leftSize;
1824            sizes[1] = max - leftSize;
1825        }
1826        else {
1827            sizes[0] = max;
1828            sizes[1] = 0;
1829        }
1830        }
1831        else if (rValid) {
1832        sizes[1] = max;
1833        sizes[0] = 0;
1834        }
1835    }
1836
1837    /**
1838     * Returns an array of the minimum sizes of the components.
1839     */

1840    int[] getPreferredSizes() {
1841        int[] retValue = new int[3];
1842
1843        for (int counter = 0; counter < 3; counter++) {
1844        if (components[counter] != null &&
1845            components[counter].isVisible()) {
1846            retValue[counter] = getPreferredSizeOfComponent
1847                            (components[counter]);
1848        }
1849        else {
1850            retValue[counter] = -1;
1851        }
1852        }
1853        return retValue;
1854    }
1855
1856    /**
1857     * Returns an array of the minimum sizes of the components.
1858     */

1859    int[] getMinimumSizes() {
1860        int[] retValue = new int[3];
1861
1862        for (int counter = 0; counter < 2; counter++) {
1863        if (components[counter] != null &&
1864            components[counter].isVisible()) {
1865            retValue[counter] = getMinimumSizeOfComponent
1866                            (components[counter]);
1867        }
1868        else {
1869            retValue[counter] = -1;
1870        }
1871        }
1872        retValue[2] = (components[2] != null) ?
1873        getMinimumSizeOfComponent(components[2]) : -1;
1874        return retValue;
1875    }
1876
1877    /**
1878     * Resets the components to their preferred sizes.
1879     */

1880    void resetToPreferredSizes(int availableSize) {
1881        // Set the sizes to the preferred sizes (if fits), otherwise
1882
// set to min sizes and distribute any extra space.
1883
int[] testSizes = getPreferredSizes();
1884        int totalSize = 0;
1885
1886        for (int counter = 0; counter < 3; counter++) {
1887        if (testSizes[counter] != -1) {
1888            totalSize += testSizes[counter];
1889        }
1890        }
1891        if (totalSize > availableSize) {
1892        testSizes = getMinimumSizes();
1893
1894        totalSize = 0;
1895        for (int counter = 0; counter < 3; counter++) {
1896            if (testSizes[counter] != -1) {
1897            totalSize += testSizes[counter];
1898            }
1899        }
1900        }
1901        setSizes(testSizes);
1902        distributeSpace(availableSize - totalSize, false);
1903    }
1904
1905    /**
1906     * Distributes <code>space</code> between the two components
1907     * (divider won't get any extra space) based on the weighting. This
1908     * attempts to honor the min size of the components.
1909         *
1910         * @param keepHidden if true and one of the components is 0x0
1911         * it gets none of the extra space
1912     */

1913    void distributeSpace(int space, boolean keepHidden) {
1914        boolean lValid = (components[0] != null &&
1915                       components[0].isVisible());
1916        boolean rValid = (components[1] != null &&
1917                       components[1].isVisible());
1918
1919            if (keepHidden) {
1920                if (lValid && getSizeForPrimaryAxis(
1921                                 components[0].getSize()) == 0) {
1922                    lValid = false;
1923                    if (rValid && getSizeForPrimaryAxis(
1924                                     components[1].getSize()) == 0) {
1925                        // Both aren't valid, force them both to be valid
1926
lValid = true;
1927                    }
1928                }
1929                else if (rValid && getSizeForPrimaryAxis(
1930                                   components[1].getSize()) == 0) {
1931                    rValid = false;
1932                }
1933            }
1934        if (lValid && rValid) {
1935        double weight = splitPane.getResizeWeight();
1936        int lExtra = (int)(weight * (double)space);
1937        int rExtra = (space - lExtra);
1938
1939        sizes[0] += lExtra;
1940        sizes[1] += rExtra;
1941
1942        int lMin = getMinimumSizeOfComponent(components[0]);
1943        int rMin = getMinimumSizeOfComponent(components[1]);
1944        boolean lMinValid = (sizes[0] >= lMin);
1945        boolean rMinValid = (sizes[1] >= rMin);
1946
1947        if (!lMinValid && !rMinValid) {
1948            if (sizes[0] < 0) {
1949            sizes[1] += sizes[0];
1950            sizes[0] = 0;
1951            }
1952            else if (sizes[1] < 0) {
1953            sizes[0] += sizes[1];
1954            sizes[1] = 0;
1955            }
1956        }
1957        else if (!lMinValid) {
1958            if (sizes[1] - (lMin - sizes[0]) < rMin) {
1959            // both below min, just make sure > 0
1960
if (sizes[0] < 0) {
1961                sizes[1] += sizes[0];
1962                sizes[0] = 0;
1963            }
1964            }
1965            else {
1966            sizes[1] -= (lMin - sizes[0]);
1967            sizes[0] = lMin;
1968            }
1969        }
1970        else if (!rMinValid) {
1971            if (sizes[0] - (rMin - sizes[1]) < lMin) {
1972            // both below min, just make sure > 0
1973
if (sizes[1] < 0) {
1974                sizes[0] += sizes[1];
1975                sizes[1] = 0;
1976            }
1977            }
1978            else {
1979            sizes[0] -= (rMin - sizes[1]);
1980            sizes[1] = rMin;
1981            }
1982        }
1983        if (sizes[0] < 0) {
1984            sizes[0] = 0;
1985        }
1986        if (sizes[1] < 0) {
1987            sizes[1] = 0;
1988        }
1989        }
1990        else if (lValid) {
1991        sizes[0] = Math.max(0, sizes[0] + space);
1992        }
1993        else if (rValid) {
1994        sizes[1] = Math.max(0, sizes[1] + space);
1995        }
1996    }
1997    }
1998
1999
2000    /**
2001     * LayoutManager used for JSplitPanes with an orientation of
2002     * VERTICAL_SPLIT.
2003     * <p>
2004     */

2005    public class BasicVerticalLayoutManager extends
2006            BasicHorizontalLayoutManager
2007    {
2008    public BasicVerticalLayoutManager() {
2009        super(1);
2010    }
2011    }
2012
2013
2014    private class Handler implements FocusListener, PropertyChangeListener {
2015        //
2016
// PropertyChangeListener
2017
//
2018
/**
2019         * Messaged from the <code>JSplitPane</code> the receiver is
2020         * contained in. May potentially reset the layout manager and cause a
2021         * <code>validate</code> to be sent.
2022         */

2023        public void propertyChange(PropertyChangeEvent e) {
2024            if(e.getSource() == splitPane) {
2025                String JavaDoc changeName = e.getPropertyName();
2026
2027                if(changeName == JSplitPane.ORIENTATION_PROPERTY) {
2028                    orientation = splitPane.getOrientation();
2029                    resetLayoutManager();
2030                } else if(changeName == JSplitPane.CONTINUOUS_LAYOUT_PROPERTY){
2031                    setContinuousLayout(splitPane.isContinuousLayout());
2032                    if(!isContinuousLayout()) {
2033                        if(nonContinuousLayoutDivider == null) {
2034                            setNonContinuousLayoutDivider(
2035                                createDefaultNonContinuousLayoutDivider(),
2036                                true);
2037                        } else if(nonContinuousLayoutDivider.getParent() ==
2038                                  null) {
2039                            setNonContinuousLayoutDivider(
2040                                nonContinuousLayoutDivider,
2041                                true);
2042                        }
2043                    }
2044                } else if(changeName == JSplitPane.DIVIDER_SIZE_PROPERTY){
2045                    divider.setDividerSize(splitPane.getDividerSize());
2046            dividerSize = divider.getDividerSize();
2047                    splitPane.revalidate();
2048            splitPane.repaint();
2049                }
2050            }
2051        }
2052
2053        //
2054
// FocusListener
2055
//
2056
public void focusGained(FocusEvent ev) {
2057            dividerKeyboardResize = true;
2058            splitPane.repaint();
2059        }
2060
2061        public void focusLost(FocusEvent ev) {
2062            dividerKeyboardResize = false;
2063            splitPane.repaint();
2064        }
2065    }
2066
2067
2068    private static class Actions extends UIAction {
2069        private static final String JavaDoc NEGATIVE_INCREMENT = "negativeIncrement";
2070        private static final String JavaDoc POSITIVE_INCREMENT = "positiveIncrement";
2071        private static final String JavaDoc SELECT_MIN = "selectMin";
2072        private static final String JavaDoc SELECT_MAX = "selectMax";
2073        private static final String JavaDoc START_RESIZE = "startResize";
2074        private static final String JavaDoc TOGGLE_FOCUS = "toggleFocus";
2075        private static final String JavaDoc FOCUS_OUT_FORWARD = "focusOutForward";
2076        private static final String JavaDoc FOCUS_OUT_BACKWARD = "focusOutBackward";
2077
2078        Actions(String JavaDoc key) {
2079            super(key);
2080        }
2081
2082        public void actionPerformed(ActionEvent ev) {
2083        JSplitPane splitPane = (JSplitPane)ev.getSource();
2084        BasicSplitPaneUI JavaDoc ui = (BasicSplitPaneUI JavaDoc)BasicLookAndFeel.
2085                      getUIOfType(splitPane.getUI(), BasicSplitPaneUI JavaDoc.class);
2086
2087            if (ui == null) {
2088                return;
2089            }
2090            String JavaDoc key = getName();
2091            if (key == NEGATIVE_INCREMENT) {
2092                if (ui.dividerKeyboardResize) {
2093                    splitPane.setDividerLocation(Math.max(
2094                              0, ui.getDividerLocation
2095                              (splitPane) - ui.getKeyboardMoveIncrement()));
2096                }
2097            }
2098            else if (key == POSITIVE_INCREMENT) {
2099                if (ui.dividerKeyboardResize) {
2100                    splitPane.setDividerLocation(
2101                        ui.getDividerLocation(splitPane) +
2102                        ui.getKeyboardMoveIncrement());
2103                }
2104            }
2105            else if (key == SELECT_MIN) {
2106                if (ui.dividerKeyboardResize) {
2107                    splitPane.setDividerLocation(0);
2108                }
2109            }
2110            else if (key == SELECT_MAX) {
2111                if (ui.dividerKeyboardResize) {
2112                    Insets insets = splitPane.getInsets();
2113                    int bottomI = (insets != null) ? insets.bottom : 0;
2114                    int rightI = (insets != null) ? insets.right : 0;
2115
2116                    if (ui.orientation == JSplitPane.VERTICAL_SPLIT) {
2117                        splitPane.setDividerLocation(splitPane.getHeight() -
2118                                                     bottomI);
2119                    }
2120                    else {
2121                        splitPane.setDividerLocation(splitPane.getWidth() -
2122                                                     rightI);
2123                    }
2124                }
2125            }
2126            else if (key == START_RESIZE) {
2127                if (!ui.dividerKeyboardResize) {
2128                    splitPane.requestFocus();
2129                } else {
2130                    JSplitPane parentSplitPane =
2131                        (JSplitPane)SwingUtilities.getAncestorOfClass(
2132                                         JSplitPane.class, splitPane);
2133                    if (parentSplitPane!=null) {
2134                        parentSplitPane.requestFocus();
2135                    }
2136                }
2137            }
2138            else if (key == TOGGLE_FOCUS) {
2139                toggleFocus(splitPane);
2140            }
2141            else if (key == FOCUS_OUT_FORWARD) {
2142                moveFocus(splitPane, 1);
2143            }
2144            else if (key == FOCUS_OUT_BACKWARD) {
2145                moveFocus(splitPane, -1);
2146            }
2147        }
2148
2149    private void moveFocus(JSplitPane splitPane, int direction) {
2150        Container rootAncestor = splitPane.getFocusCycleRootAncestor();
2151        FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy();
2152        Component focusOn = (direction > 0) ?
2153        policy.getComponentAfter(rootAncestor, splitPane) :
2154        policy.getComponentBefore(rootAncestor, splitPane);
2155        HashSet focusFrom = new HashSet();
2156        if (splitPane.isAncestorOf(focusOn)) {
2157        do {
2158            focusFrom.add(focusOn);
2159            rootAncestor = focusOn.getFocusCycleRootAncestor();
2160            policy = rootAncestor.getFocusTraversalPolicy();
2161            focusOn = (direction > 0) ?
2162            policy.getComponentAfter(rootAncestor, focusOn) :
2163            policy.getComponentBefore(rootAncestor, focusOn);
2164        } while (splitPane.isAncestorOf(focusOn) &&
2165             !focusFrom.contains(focusOn));
2166        }
2167        if ( focusOn!=null && !splitPane.isAncestorOf(focusOn) ) {
2168        focusOn.requestFocus();
2169        }
2170    }
2171
2172    private void toggleFocus(JSplitPane splitPane) {
2173        Component left = splitPane.getLeftComponent();
2174        Component right = splitPane.getRightComponent();
2175
2176        KeyboardFocusManager manager =
2177        KeyboardFocusManager.getCurrentKeyboardFocusManager();
2178        Component focus = manager.getFocusOwner();
2179        Component focusOn = getNextSide(splitPane, focus);
2180        if (focusOn != null) {
2181        // don't change the focus if the new focused component belongs
2182
// to the same splitpane and the same side
2183
if ( focus!=null &&
2184             ( (SwingUtilities.isDescendingFrom(focus, left) &&
2185            SwingUtilities.isDescendingFrom(focusOn, left)) ||
2186               (SwingUtilities.isDescendingFrom(focus, right) &&
2187            SwingUtilities.isDescendingFrom(focusOn, right)) ) ) {
2188            return;
2189        }
2190        BasicLookAndFeel.compositeRequestFocus(focusOn);
2191        }
2192    }
2193
2194    private Component getNextSide(JSplitPane splitPane, Component focus) {
2195        Component left = splitPane.getLeftComponent();
2196        Component right = splitPane.getRightComponent();
2197        Component next = null;
2198        if (focus!=null && SwingUtilities.isDescendingFrom(focus, left) &&
2199        right!=null) {
2200        next = getFirstAvailableComponent(right);
2201        if (next != null) {
2202            return next;
2203        }
2204        }
2205        JSplitPane parentSplitPane = (JSplitPane)SwingUtilities.getAncestorOfClass(JSplitPane.class, splitPane);
2206        if (parentSplitPane!=null) {
2207        // focus next side of the parent split pane
2208
next = getNextSide(parentSplitPane, focus);
2209        } else {
2210        next = getFirstAvailableComponent(left);
2211        if (next == null) {
2212            next = getFirstAvailableComponent(right);
2213        }
2214        }
2215        return next;
2216    }
2217
2218    private Component getFirstAvailableComponent(Component c) {
2219        if (c!=null && c instanceof JSplitPane) {
2220        JSplitPane sp = (JSplitPane)c;
2221        Component left = getFirstAvailableComponent(sp.getLeftComponent());
2222        if (left != null) {
2223            c = left;
2224        } else {
2225            c = getFirstAvailableComponent(sp.getRightComponent());
2226        }
2227        }
2228        return c;
2229    }
2230    }
2231}
2232
Popular Tags