blob: 28427f7b3b0f96c2fb832ee99e368ad329a580e7 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27package javax.swing.plaf.basic;
28
29
30import sun.swing.DefaultLookup;
31import sun.swing.UIAction;
32import javax.swing.*;
33import javax.swing.border.Border;
34import javax.swing.event.*;
35import java.awt.*;
36import java.awt.event.*;
37import java.awt.peer.ComponentPeer;
38import java.awt.peer.LightweightPeer;
39import java.beans.*;
40import java.util.*;
41import javax.swing.plaf.ActionMapUIResource;
42import javax.swing.plaf.SplitPaneUI;
43import javax.swing.plaf.ComponentUI;
44import javax.swing.plaf.UIResource;
45import sun.swing.SwingUtilities2;
46
47
48/**
49 * A Basic L&F implementation of the SplitPaneUI.
50 *
51 * @author Scott Violet
52 * @author Steve Wilson
53 * @author Ralph Kar
54 */
55public class BasicSplitPaneUI extends SplitPaneUI
56{
57 /**
58 * The divider used for non-continuous layout is added to the split pane
59 * with this object.
60 */
61 protected static final String NON_CONTINUOUS_DIVIDER =
62 "nonContinuousDivider";
63
64
65 /**
66 * How far (relative) the divider does move when it is moved around by
67 * the cursor keys on the keyboard.
68 */
69 protected static int KEYBOARD_DIVIDER_MOVE_OFFSET = 3;
70
71
72 /**
73 * JSplitPane instance this instance is providing
74 * the look and feel for.
75 */
76 protected JSplitPane splitPane;
77
78
79 /**
80 * LayoutManager that is created and placed into the split pane.
81 */
82 protected BasicHorizontalLayoutManager layoutManager;
83
84
85 /**
86 * Instance of the divider for this JSplitPane.
87 */
88 protected BasicSplitPaneDivider divider;
89
90
91 /**
92 * Instance of the PropertyChangeListener for this JSplitPane.
93 */
94 protected PropertyChangeListener propertyChangeListener;
95
96
97 /**
98 * Instance of the FocusListener for this JSplitPane.
99 */
100 protected FocusListener focusListener;
101
102 private Handler handler;
103
104
105 /**
106 * Keys to use for forward focus traversal when the JComponent is
107 * managing focus.
108 */
109 private static Set managingFocusForwardTraversalKeys;
110
111 /**
112 * Keys to use for backward focus traversal when the JComponent is
113 * managing focus.
114 */
115 private static Set managingFocusBackwardTraversalKeys;
116
117
118 /**
119 * The size of the divider while the dragging session is valid.
120 */
121 protected int dividerSize;
122
123
124 /**
125 * Instance for the shadow of the divider when non continuous layout
126 * is being used.
127 */
128 protected Component nonContinuousLayoutDivider;
129
130
131 /**
132 * Set to true in startDragging if any of the children
133 * (not including the nonContinuousLayoutDivider) are heavy weights.
134 */
135 protected boolean draggingHW;
136
137
138 /**
139 * Location of the divider when the dragging session began.
140 */
141 protected int beginDragDividerLocation;
142
143
144 /**
145 * As of Java 2 platform v1.3 this previously undocumented field is no
146 * longer used.
147 * Key bindings are now defined by the LookAndFeel, please refer to
148 * the key bindings specification for further details.
149 *
150 * @deprecated As of Java 2 platform v1.3.
151 */
152 @Deprecated
153 protected KeyStroke upKey;
154 /**
155 * As of Java 2 platform v1.3 this previously undocumented field is no
156 * longer used.
157 * Key bindings are now defined by the LookAndFeel, please refer to
158 * the key bindings specification for further details.
159 *
160 * @deprecated As of Java 2 platform v1.3.
161 */
162 @Deprecated
163 protected KeyStroke downKey;
164 /**
165 * As of Java 2 platform v1.3 this previously undocumented field is no
166 * longer used.
167 * Key bindings are now defined by the LookAndFeel, please refer to
168 * the key bindings specification for further details.
169 *
170 * @deprecated As of Java 2 platform v1.3.
171 */
172 @Deprecated
173 protected KeyStroke leftKey;
174 /**
175 * As of Java 2 platform v1.3 this previously undocumented field is no
176 * longer used.
177 * Key bindings are now defined by the LookAndFeel, please refer to
178 * the key bindings specification for further details.
179 *
180 * @deprecated As of Java 2 platform v1.3.
181 */
182 @Deprecated
183 protected KeyStroke rightKey;
184 /**
185 * As of Java 2 platform v1.3 this previously undocumented field is no
186 * longer used.
187 * Key bindings are now defined by the LookAndFeel, please refer to
188 * the key bindings specification for further details.
189 *
190 * @deprecated As of Java 2 platform v1.3.
191 */
192 @Deprecated
193 protected KeyStroke homeKey;
194 /**
195 * As of Java 2 platform v1.3 this previously undocumented field is no
196 * longer used.
197 * Key bindings are now defined by the LookAndFeel, please refer to
198 * the key bindings specification for further details.
199 *
200 * @deprecated As of Java 2 platform v1.3.
201 */
202 @Deprecated
203 protected KeyStroke endKey;
204 /**
205 * As of Java 2 platform v1.3 this previously undocumented field is no
206 * longer used.
207 * Key bindings are now defined by the LookAndFeel, please refer to
208 * the key bindings specification for further details.
209 *
210 * @deprecated As of Java 2 platform v1.3.
211 */
212 @Deprecated
213 protected KeyStroke dividerResizeToggleKey;
214
215 /**
216 * As of Java 2 platform v1.3 this previously undocumented field is no
217 * longer used.
218 * Key bindings are now defined by the LookAndFeel, please refer to
219 * the key bindings specification for further details.
220 *
221 * @deprecated As of Java 2 platform v1.3.
222 */
223 @Deprecated
224 protected ActionListener keyboardUpLeftListener;
225 /**
226 * As of Java 2 platform v1.3 this previously undocumented field is no
227 * longer used.
228 * Key bindings are now defined by the LookAndFeel, please refer to
229 * the key bindings specification for further details.
230 *
231 * @deprecated As of Java 2 platform v1.3.
232 */
233 @Deprecated
234 protected ActionListener keyboardDownRightListener;
235 /**
236 * As of Java 2 platform v1.3 this previously undocumented field is no
237 * longer used.
238 * Key bindings are now defined by the LookAndFeel, please refer to
239 * the key bindings specification for further details.
240 *
241 * @deprecated As of Java 2 platform v1.3.
242 */
243 @Deprecated
244 protected ActionListener keyboardHomeListener;
245 /**
246 * As of Java 2 platform v1.3 this previously undocumented field is no
247 * longer used.
248 * Key bindings are now defined by the LookAndFeel, please refer to
249 * the key bindings specification for further details.
250 *
251 * @deprecated As of Java 2 platform v1.3.
252 */
253 @Deprecated
254 protected ActionListener keyboardEndListener;
255 /**
256 * As of Java 2 platform v1.3 this previously undocumented field is no
257 * longer used.
258 * Key bindings are now defined by the LookAndFeel, please refer to
259 * the key bindings specification for further details.
260 *
261 * @deprecated As of Java 2 platform v1.3.
262 */
263 @Deprecated
264 protected ActionListener keyboardResizeToggleListener;
265
266
267 // Private data of the instance
268 private int orientation;
269 private int lastDragLocation;
270 private boolean continuousLayout;
271 private boolean dividerKeyboardResize;
272 private boolean dividerLocationIsSet; // needed for tracking
273 // the first occurrence of
274 // setDividerLocation()
275 private Color dividerDraggingColor;
276 private boolean rememberPaneSizes;
277
278 // Indicates wether the one of splitpane sides is expanded
279 private boolean keepHidden = false;
280
281 /** Indicates that we have painted once. */
282 // This is used by the LayoutManager to determine when it should use
283 // the divider location provided by the JSplitPane. This is used as there
284 // is no way to determine when the layout process has completed.
285 boolean painted;
286 /** If true, setDividerLocation does nothing. */
287 boolean ignoreDividerLocationChange;
288
289
290 /**
291 * Creates a new BasicSplitPaneUI instance
292 */
293 public static ComponentUI createUI(JComponent x) {
294 return new BasicSplitPaneUI();
295 }
296
297 static void loadActionMap(LazyActionMap map) {
298 map.put(new Actions(Actions.NEGATIVE_INCREMENT));
299 map.put(new Actions(Actions.POSITIVE_INCREMENT));
300 map.put(new Actions(Actions.SELECT_MIN));
301 map.put(new Actions(Actions.SELECT_MAX));
302 map.put(new Actions(Actions.START_RESIZE));
303 map.put(new Actions(Actions.TOGGLE_FOCUS));
304 map.put(new Actions(Actions.FOCUS_OUT_FORWARD));
305 map.put(new Actions(Actions.FOCUS_OUT_BACKWARD));
306 }
307
308
309
310 /**
311 * Installs the UI.
312 */
313 public void installUI(JComponent c) {
314 splitPane = (JSplitPane) c;
315 dividerLocationIsSet = false;
316 dividerKeyboardResize = false;
317 keepHidden = false;
318 installDefaults();
319 installListeners();
320 installKeyboardActions();
321 setLastDragLocation(-1);
322 }
323
324
325 /**
326 * Installs the UI defaults.
327 */
328 protected void installDefaults(){
329 LookAndFeel.installBorder(splitPane, "SplitPane.border");
330 LookAndFeel.installColors(splitPane, "SplitPane.background",
331 "SplitPane.foreground");
332 LookAndFeel.installProperty(splitPane, "opaque", Boolean.TRUE);
333
334 if (divider == null) divider = createDefaultDivider();
335 divider.setBasicSplitPaneUI(this);
336
337 Border b = divider.getBorder();
338
339 if (b == null || !(b instanceof UIResource)) {
340 divider.setBorder(UIManager.getBorder("SplitPaneDivider.border"));
341 }
342
343 dividerDraggingColor = UIManager.getColor("SplitPaneDivider.draggingColor");
344
345 setOrientation(splitPane.getOrientation());
346
347 // This plus 2 here is to provide backwards consistancy. Previously,
348 // the old size did not include the 2 pixel border around the divider,
349 // it now does.
350 LookAndFeel.installProperty(splitPane, "dividerSize",
351 UIManager.get("SplitPane.dividerSize"));
352
353 divider.setDividerSize(splitPane.getDividerSize());
354 dividerSize = divider.getDividerSize();
355 splitPane.add(divider, JSplitPane.DIVIDER);
356
357 setContinuousLayout(splitPane.isContinuousLayout());
358
359 resetLayoutManager();
360
361 /* Install the nonContinuousLayoutDivider here to avoid having to
362 add/remove everything later. */
363 if(nonContinuousLayoutDivider == null) {
364 setNonContinuousLayoutDivider(
365 createDefaultNonContinuousLayoutDivider(),
366 true);
367 } else {
368 setNonContinuousLayoutDivider(nonContinuousLayoutDivider, true);
369 }
370
371 // focus forward traversal key
372 if (managingFocusForwardTraversalKeys==null) {
373 managingFocusForwardTraversalKeys = new HashSet();
374 managingFocusForwardTraversalKeys.add(
375 KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
376 }
377 splitPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
378 managingFocusForwardTraversalKeys);
379 // focus backward traversal key
380 if (managingFocusBackwardTraversalKeys==null) {
381 managingFocusBackwardTraversalKeys = new HashSet();
382 managingFocusBackwardTraversalKeys.add(
383 KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_MASK));
384 }
385 splitPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
386 managingFocusBackwardTraversalKeys);
387 }
388
389
390 /**
391 * Installs the event listeners for the UI.
392 */
393 protected void installListeners() {
394 if ((propertyChangeListener = createPropertyChangeListener()) !=
395 null) {
396 splitPane.addPropertyChangeListener(propertyChangeListener);
397 }
398
399 if ((focusListener = createFocusListener()) != null) {
400 splitPane.addFocusListener(focusListener);
401 }
402 }
403
404
405 /**
406 * Installs the keyboard actions for the UI.
407 */
408 protected void installKeyboardActions() {
409 InputMap km = getInputMap(JComponent.
410 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
411
412 SwingUtilities.replaceUIInputMap(splitPane, JComponent.
413 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
414 km);
415 LazyActionMap.installLazyActionMap(splitPane, BasicSplitPaneUI.class,
416 "SplitPane.actionMap");
417 }
418
419 InputMap getInputMap(int condition) {
420 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
421 return (InputMap)DefaultLookup.get(splitPane, this,
422 "SplitPane.ancestorInputMap");
423 }
424 return null;
425 }
426
427 /**
428 * Uninstalls the UI.
429 */
430 public void uninstallUI(JComponent c) {
431 uninstallKeyboardActions();
432 uninstallListeners();
433 uninstallDefaults();
434 dividerLocationIsSet = false;
435 dividerKeyboardResize = false;
436 splitPane = null;
437 }
438
439
440 /**
441 * Uninstalls the UI defaults.
442 */
443 protected void uninstallDefaults() {
444 if(splitPane.getLayout() == layoutManager) {
445 splitPane.setLayout(null);
446 }
447
448 if(nonContinuousLayoutDivider != null) {
449 splitPane.remove(nonContinuousLayoutDivider);
450 }
451
452 LookAndFeel.uninstallBorder(splitPane);
453
454 Border b = divider.getBorder();
455
456 if (b instanceof UIResource) {
457 divider.setBorder(null);
458 }
459
460 splitPane.remove(divider);
461 divider.setBasicSplitPaneUI(null);
462 layoutManager = null;
463 divider = null;
464 nonContinuousLayoutDivider = null;
465
466 setNonContinuousLayoutDivider(null);
467
468 // sets the focus forward and backward traversal keys to null
469 // to restore the defaults
470 splitPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null);
471 splitPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null);
472 }
473
474
475 /**
476 * Uninstalls the event listeners for the UI.
477 */
478 protected void uninstallListeners() {
479 if (propertyChangeListener != null) {
480 splitPane.removePropertyChangeListener(propertyChangeListener);
481 propertyChangeListener = null;
482 }
483 if (focusListener != null) {
484 splitPane.removeFocusListener(focusListener);
485 focusListener = null;
486 }
487
488 keyboardUpLeftListener = null;
489 keyboardDownRightListener = null;
490 keyboardHomeListener = null;
491 keyboardEndListener = null;
492 keyboardResizeToggleListener = null;
493 handler = null;
494 }
495
496
497 /**
498 * Uninstalls the keyboard actions for the UI.
499 */
500 protected void uninstallKeyboardActions() {
501 SwingUtilities.replaceUIActionMap(splitPane, null);
502 SwingUtilities.replaceUIInputMap(splitPane, JComponent.
503 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
504 null);
505 }
506
507
508 /**
509 * Creates a PropertyChangeListener for the JSplitPane UI.
510 */
511 protected PropertyChangeListener createPropertyChangeListener() {
512 return getHandler();
513 }
514
515 private Handler getHandler() {
516 if (handler == null) {
517 handler = new Handler();
518 }
519 return handler;
520 }
521
522
523 /**
524 * Creates a FocusListener for the JSplitPane UI.
525 */
526 protected FocusListener createFocusListener() {
527 return getHandler();
528 }
529
530
531 /**
532 * As of Java 2 platform v1.3 this method is no
533 * longer used. Subclassers previously using this method should
534 * instead create an Action wrapping the ActionListener, and register
535 * that Action by overriding <code>installKeyboardActions</code> and
536 * placing the Action in the SplitPane's ActionMap. Please refer to
537 * the key bindings specification for further details.
538 * <p>
539 * Creates a ActionListener for the JSplitPane UI that listens for
540 * specific key presses.
541 *
542 * @deprecated As of Java 2 platform v1.3.
543 */
544 @Deprecated
545 protected ActionListener createKeyboardUpLeftListener() {
546 return new KeyboardUpLeftHandler();
547 }
548
549
550 /**
551 * As of Java 2 platform v1.3 this method is no
552 * longer used. Subclassers previously using this method should
553 * instead create an Action wrapping the ActionListener, and register
554 * that Action by overriding <code>installKeyboardActions</code> and
555 * placing the Action in the SplitPane's ActionMap. Please refer to
556 * the key bindings specification for further details.
557 * <p>
558 * Creates a ActionListener for the JSplitPane UI that listens for
559 * specific key presses.
560 *
561 * @deprecated As of Java 2 platform v1.3.
562 */
563 @Deprecated
564 protected ActionListener createKeyboardDownRightListener() {
565 return new KeyboardDownRightHandler();
566 }
567
568
569 /**
570 * As of Java 2 platform v1.3 this method is no
571 * longer used. Subclassers previously using this method should
572 * instead create an Action wrapping the ActionListener, and register
573 * that Action by overriding <code>installKeyboardActions</code> and
574 * placing the Action in the SplitPane's ActionMap. Please refer to
575 * the key bindings specification for further details.
576 * <p>
577 * Creates a ActionListener for the JSplitPane UI that listens for
578 * specific key presses.
579 *
580 * @deprecated As of Java 2 platform v1.3.
581 */
582 @Deprecated
583 protected ActionListener createKeyboardHomeListener() {
584 return new KeyboardHomeHandler();
585 }
586
587
588 /**
589 * As of Java 2 platform v1.3 this method is no
590 * longer used. Subclassers previously using this method should
591 * instead create an Action wrapping the ActionListener, and register
592 * that Action by overriding <code>installKeyboardActions</code> and
593 * placing the Action in the SplitPane's ActionMap. Please refer to
594 * the key bindings specification for further details.
595 * <p>
596 * Creates a ActionListener for the JSplitPane UI that listens for
597 * specific key presses.
598 *
599 * @deprecated As of Java 2 platform v1.3.
600 */
601 @Deprecated
602 protected ActionListener createKeyboardEndListener() {
603 return new KeyboardEndHandler();
604 }
605
606
607 /**
608 * As of Java 2 platform v1.3 this method is no
609 * longer used. Subclassers previously using this method should
610 * instead create an Action wrapping the ActionListener, and register
611 * that Action by overriding <code>installKeyboardActions</code> and
612 * placing the Action in the SplitPane's ActionMap. Please refer to
613 * the key bindings specification for further details.
614 * <p>
615 * Creates a ActionListener for the JSplitPane UI that listens for
616 * specific key presses.
617 *
618 * @deprecated As of Java 2 platform v1.3.
619 */
620 @Deprecated
621 protected ActionListener createKeyboardResizeToggleListener() {
622 return new KeyboardResizeToggleHandler();
623 }
624
625
626 /**
627 * Returns the orientation for the JSplitPane.
628 */
629 public int getOrientation() {
630 return orientation;
631 }
632
633
634 /**
635 * Set the orientation for the JSplitPane.
636 */
637 public void setOrientation(int orientation) {
638 this.orientation = orientation;
639 }
640
641
642 /**
643 * Determines wether the JSplitPane is set to use a continuous layout.
644 */
645 public boolean isContinuousLayout() {
646 return continuousLayout;
647 }
648
649
650 /**
651 * Turn continuous layout on/off.
652 */
653 public void setContinuousLayout(boolean b) {
654 continuousLayout = b;
655 }
656
657
658 /**
659 * Returns the last drag location of the JSplitPane.
660 */
661 public int getLastDragLocation() {
662 return lastDragLocation;
663 }
664
665
666 /**
667 * Set the last drag location of the JSplitPane.
668 */
669 public void setLastDragLocation(int l) {
670 lastDragLocation = l;
671 }
672
673 /**
674 * @return increment via keyboard methods.
675 */
676 int getKeyboardMoveIncrement() {
677 return KEYBOARD_DIVIDER_MOVE_OFFSET;
678 }
679
680 /**
681 * Implementation of the PropertyChangeListener
682 * that the JSplitPane UI uses.
683 * <p>
684 * This class should be treated as a &quot;protected&quot; inner class.
685 * Instantiate it only within subclasses of BasicSplitPaneUI.
686 */
687 public class PropertyHandler implements PropertyChangeListener
688 {
689 // NOTE: This class exists only for backward compatability. All
690 // its functionality has been moved into Handler. If you need to add
691 // new functionality add it to the Handler, but make sure this
692 // class calls into the Handler.
693
694 /**
695 * Messaged from the <code>JSplitPane</code> the receiver is
696 * contained in. May potentially reset the layout manager and cause a
697 * <code>validate</code> to be sent.
698 */
699 public void propertyChange(PropertyChangeEvent e) {
700 getHandler().propertyChange(e);
701 }
702 }
703
704
705 /**
706 * Implementation of the FocusListener that the JSplitPane UI uses.
707 * <p>
708 * This class should be treated as a &quot;protected&quot; inner class.
709 * Instantiate it only within subclasses of BasicSplitPaneUI.
710 */
711 public class FocusHandler extends FocusAdapter
712 {
713 // NOTE: This class exists only for backward compatability. All
714 // its functionality has been moved into Handler. If you need to add
715 // new functionality add it to the Handler, but make sure this
716 // class calls into the Handler.
717 public void focusGained(FocusEvent ev) {
718 getHandler().focusGained(ev);
719 }
720
721 public void focusLost(FocusEvent ev) {
722 getHandler().focusLost(ev);
723 }
724 }
725
726
727 /**
728 * Implementation of an ActionListener that the JSplitPane UI uses for
729 * handling specific key presses.
730 * <p>
731 * This class should be treated as a &quot;protected&quot; inner class.
732 * Instantiate it only within subclasses of BasicSplitPaneUI.
733 */
734 public class KeyboardUpLeftHandler implements ActionListener
735 {
736 public void actionPerformed(ActionEvent ev) {
737 if (dividerKeyboardResize) {
738 splitPane.setDividerLocation(Math.max(0,getDividerLocation
739 (splitPane) - getKeyboardMoveIncrement()));
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 KeyboardDownRightHandler implements ActionListener
752 {
753 public void actionPerformed(ActionEvent ev) {
754 if (dividerKeyboardResize) {
755 splitPane.setDividerLocation(getDividerLocation(splitPane) +
756 getKeyboardMoveIncrement());
757 }
758 }
759 }
760
761
762 /**
763 * Implementation of an ActionListener that the JSplitPane UI uses for
764 * handling specific key presses.
765 * <p>
766 * This class should be treated as a &quot;protected&quot; inner class.
767 * Instantiate it only within subclasses of BasicSplitPaneUI.
768 */
769 public class KeyboardHomeHandler implements ActionListener
770 {
771 public void actionPerformed(ActionEvent ev) {
772 if (dividerKeyboardResize) {
773 splitPane.setDividerLocation(0);
774 }
775 }
776 }
777
778
779 /**
780 * Implementation of an ActionListener that the JSplitPane UI uses for
781 * handling specific key presses.
782 * <p>
783 * This class should be treated as a &quot;protected&quot; inner class.
784 * Instantiate it only within subclasses of BasicSplitPaneUI.
785 */
786 public class KeyboardEndHandler implements ActionListener
787 {
788 public void actionPerformed(ActionEvent ev) {
789 if (dividerKeyboardResize) {
790 Insets insets = splitPane.getInsets();
791 int bottomI = (insets != null) ? insets.bottom : 0;
792 int rightI = (insets != null) ? insets.right : 0;
793
794 if (orientation == JSplitPane.VERTICAL_SPLIT) {
795 splitPane.setDividerLocation(splitPane.getHeight() -
796 bottomI);
797 }
798 else {
799 splitPane.setDividerLocation(splitPane.getWidth() -
800 rightI);
801 }
802 }
803 }
804 }
805
806
807 /**
808 * Implementation of an ActionListener that the JSplitPane UI uses for
809 * handling specific key presses.
810 * <p>
811 * This class should be treated as a &quot;protected&quot; inner class.
812 * Instantiate it only within subclasses of BasicSplitPaneUI.
813 */
814 public class KeyboardResizeToggleHandler implements ActionListener
815 {
816 public void actionPerformed(ActionEvent ev) {
817 if (!dividerKeyboardResize) {
818 splitPane.requestFocus();
819 }
820 }
821 }
822
823 /**
824 * Returns the divider between the top Components.
825 */
826 public BasicSplitPaneDivider getDivider() {
827 return divider;
828 }
829
830
831 /**
832 * Returns the default non continuous layout divider, which is an
833 * instanceof Canvas that fills the background in dark gray.
834 */
835 protected Component createDefaultNonContinuousLayoutDivider() {
836 return new Canvas() {
837 public void paint(Graphics g) {
838 if(!isContinuousLayout() && getLastDragLocation() != -1) {
839 Dimension size = splitPane.getSize();
840
841 g.setColor(dividerDraggingColor);
842 if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
843 g.fillRect(0, 0, dividerSize - 1, size.height - 1);
844 } else {
845 g.fillRect(0, 0, size.width - 1, dividerSize - 1);
846 }
847 }
848 }
849 };
850 }
851
852
853 /**
854 * Sets the divider to use when the splitPane is configured to
855 * not continuously layout. This divider will only be used during a
856 * dragging session. It is recommended that the passed in component
857 * be a heavy weight.
858 */
859 protected void setNonContinuousLayoutDivider(Component newDivider) {
860 setNonContinuousLayoutDivider(newDivider, true);
861 }
862
863
864 /**
865 * Sets the divider to use.
866 */
867 protected void setNonContinuousLayoutDivider(Component newDivider,
868 boolean rememberSizes) {
869 rememberPaneSizes = rememberSizes;
870 if(nonContinuousLayoutDivider != null && splitPane != null) {
871 splitPane.remove(nonContinuousLayoutDivider);
872 }
873 nonContinuousLayoutDivider = newDivider;
874 }
875
876 private void addHeavyweightDivider() {
877 if(nonContinuousLayoutDivider != null && splitPane != null) {
878
879 /* Needs to remove all the components and re-add them! YECK! */
880 // This is all done so that the nonContinuousLayoutDivider will
881 // be drawn on top of the other components, without this, one
882 // of the heavyweights will draw over the divider!
883 Component leftC = splitPane.getLeftComponent();
884 Component rightC = splitPane.getRightComponent();
885 int lastLocation = splitPane.
886 getDividerLocation();
887
888 if(leftC != null)
889 splitPane.setLeftComponent(null);
890 if(rightC != null)
891 splitPane.setRightComponent(null);
892 splitPane.remove(divider);
893 splitPane.add(nonContinuousLayoutDivider, BasicSplitPaneUI.
894 NON_CONTINUOUS_DIVIDER,
895 splitPane.getComponentCount());
896 splitPane.setLeftComponent(leftC);
897 splitPane.setRightComponent(rightC);
898 splitPane.add(divider, JSplitPane.DIVIDER);
899 if(rememberPaneSizes) {
900 splitPane.setDividerLocation(lastLocation);
901 }
902 }
903
904 }
905
906
907 /**
908 * Returns the divider to use when the splitPane is configured to
909 * not continuously layout. This divider will only be used during a
910 * dragging session.
911 */
912 public Component getNonContinuousLayoutDivider() {
913 return nonContinuousLayoutDivider;
914 }
915
916
917 /**
918 * Returns the splitpane this instance is currently contained
919 * in.
920 */
921 public JSplitPane getSplitPane() {
922 return splitPane;
923 }
924
925
926 /**
927 * Creates the default divider.
928 */
929 public BasicSplitPaneDivider createDefaultDivider() {
930 return new BasicSplitPaneDivider(this);
931 }
932
933
934 /**
935 * Messaged to reset the preferred sizes.
936 */
937 public void resetToPreferredSizes(JSplitPane jc) {
938 if(splitPane != null) {
939 layoutManager.resetToPreferredSizes();
940 splitPane.revalidate();
941 splitPane.repaint();
942 }
943 }
944
945
946 /**
947 * Sets the location of the divider to location.
948 */
949 public void setDividerLocation(JSplitPane jc, int location) {
950 if (!ignoreDividerLocationChange) {
951 dividerLocationIsSet = true;
952 splitPane.revalidate();
953 splitPane.repaint();
954
955 if (keepHidden) {
956 Insets insets = splitPane.getInsets();
957 int orientation = splitPane.getOrientation();
958 if ((orientation == JSplitPane.VERTICAL_SPLIT &&
959 location != insets.top &&
960 location != splitPane.getHeight()-divider.getHeight()-insets.top) ||
961 (orientation == JSplitPane.HORIZONTAL_SPLIT &&
962 location != insets.left &&
963 location != splitPane.getWidth()-divider.getWidth()-insets.left)) {
964 setKeepHidden(false);
965 }
966 }
967 }
968 else {
969 ignoreDividerLocationChange = false;
970 }
971 }
972
973
974 /**
975 * Returns the location of the divider, which may differ from what
976 * the splitpane thinks the location of the divider is.
977 */
978 public int getDividerLocation(JSplitPane jc) {
979 if(orientation == JSplitPane.HORIZONTAL_SPLIT)
980 return divider.getLocation().x;
981 return divider.getLocation().y;
982 }
983
984
985 /**
986 * Gets the minimum location of the divider.
987 */
988 public int getMinimumDividerLocation(JSplitPane jc) {
989 int minLoc = 0;
990 Component leftC = splitPane.getLeftComponent();
991
992 if ((leftC != null) && (leftC.isVisible())) {
993 Insets insets = splitPane.getInsets();
994 Dimension minSize = leftC.getMinimumSize();
995 if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
996 minLoc = minSize.width;
997 } else {
998 minLoc = minSize.height;
999 }
1000 if(insets != null) {
1001 if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1002 minLoc += insets.left;
1003 } else {
1004 minLoc += insets.top;
1005 }
1006 }
1007 }
1008 return minLoc;
1009 }
1010
1011
1012 /**
1013 * Gets the maximum location of the divider.
1014 */
1015 public int getMaximumDividerLocation(JSplitPane jc) {
1016 Dimension splitPaneSize = splitPane.getSize();
1017 int maxLoc = 0;
1018 Component rightC = splitPane.getRightComponent();
1019
1020 if (rightC != null) {
1021 Insets insets = splitPane.getInsets();
1022 Dimension minSize = new Dimension(0, 0);
1023 if (rightC.isVisible()) {
1024 minSize = rightC.getMinimumSize();
1025 }
1026 if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1027 maxLoc = splitPaneSize.width - minSize.width;
1028 } else {
1029 maxLoc = splitPaneSize.height - minSize.height;
1030 }
1031 maxLoc -= dividerSize;
1032 if(insets != null) {
1033 if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1034 maxLoc -= insets.right;
1035 } else {
1036 maxLoc -= insets.top;
1037 }
1038 }
1039 }
1040 return Math.max(getMinimumDividerLocation(splitPane), maxLoc);
1041 }
1042
1043
1044 /**
1045 * Messaged after the JSplitPane the receiver is providing the look
1046 * and feel for paints its children.
1047 */
1048 public void finishedPaintingChildren(JSplitPane jc, Graphics g) {
1049 if(jc == splitPane && getLastDragLocation() != -1 &&
1050 !isContinuousLayout() && !draggingHW) {
1051 Dimension size = splitPane.getSize();
1052
1053 g.setColor(dividerDraggingColor);
1054 if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1055 g.fillRect(getLastDragLocation(), 0, dividerSize - 1,
1056 size.height - 1);
1057 } else {
1058 g.fillRect(0, lastDragLocation, size.width - 1,
1059 dividerSize - 1);
1060 }
1061 }
1062 }
1063
1064
1065 /**
1066 * Messaged to paint the look and feel.
1067 */
1068 public void paint(Graphics g, JComponent jc) {
1069 if (!painted && splitPane.getDividerLocation()<0) {
1070 ignoreDividerLocationChange = true;
1071 splitPane.setDividerLocation(getDividerLocation(splitPane));
1072 }
1073 painted = true;
1074 }
1075
1076
1077 /**
1078 * Returns the preferred size for the passed in component,
1079 * This is passed off to the current layoutmanager.
1080 */
1081 public Dimension getPreferredSize(JComponent jc) {
1082 if(splitPane != null)
1083 return layoutManager.preferredLayoutSize(splitPane);
1084 return new Dimension(0, 0);
1085 }
1086
1087
1088 /**
1089 * Returns the minimum size for the passed in component,
1090 * This is passed off to the current layoutmanager.
1091 */
1092 public Dimension getMinimumSize(JComponent jc) {
1093 if(splitPane != null)
1094 return layoutManager.minimumLayoutSize(splitPane);
1095 return new Dimension(0, 0);
1096 }
1097
1098
1099 /**
1100 * Returns the maximum size for the passed in component,
1101 * This is passed off to the current layoutmanager.
1102 */
1103 public Dimension getMaximumSize(JComponent jc) {
1104 if(splitPane != null)
1105 return layoutManager.maximumLayoutSize(splitPane);
1106 return new Dimension(0, 0);
1107 }
1108
1109
1110 /**
1111 * Returns the insets. The insets are returned from the border insets
1112 * of the current border.
1113 */
1114 public Insets getInsets(JComponent jc) {
1115 return null;
1116 }
1117
1118
1119 /**
1120 * Resets the layout manager based on orientation and messages it
1121 * with invalidateLayout to pull in appropriate Components.
1122 */
1123 protected void resetLayoutManager() {
1124 if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1125 layoutManager = new BasicHorizontalLayoutManager(0);
1126 } else {
1127 layoutManager = new BasicHorizontalLayoutManager(1);
1128 }
1129 splitPane.setLayout(layoutManager);
1130 layoutManager.updateComponents();
1131 splitPane.revalidate();
1132 splitPane.repaint();
1133 }
1134
1135 /**
1136 * Set the value to indicate if one of the splitpane sides is expanded.
1137 */
1138 void setKeepHidden(boolean keepHidden) {
1139 this.keepHidden = keepHidden;
1140 }
1141
1142 /**
1143 * The value returned indicates if one of the splitpane sides is expanded.
1144 * @return true if one of the splitpane sides is expanded, false otherwise.
1145 */
1146 private boolean getKeepHidden() {
1147 return keepHidden;
1148 }
1149
1150 /**
1151 * Should be messaged before the dragging session starts, resets
1152 * lastDragLocation and dividerSize.
1153 */
1154 protected void startDragging() {
1155 Component leftC = splitPane.getLeftComponent();
1156 Component rightC = splitPane.getRightComponent();
1157 ComponentPeer cPeer;
1158
1159 beginDragDividerLocation = getDividerLocation(splitPane);
1160 draggingHW = false;
1161 if(leftC != null && (cPeer = leftC.getPeer()) != null &&
1162 !(cPeer instanceof LightweightPeer)) {
1163 draggingHW = true;
1164 } else if(rightC != null && (cPeer = rightC.getPeer()) != null
1165 && !(cPeer instanceof LightweightPeer)) {
1166 draggingHW = true;
1167 }
1168 if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1169 setLastDragLocation(divider.getBounds().x);
1170 dividerSize = divider.getSize().width;
1171 if(!isContinuousLayout() && draggingHW) {
1172 nonContinuousLayoutDivider.setBounds
1173 (getLastDragLocation(), 0, dividerSize,
1174 splitPane.getHeight());
1175 addHeavyweightDivider();
1176 }
1177 } else {
1178 setLastDragLocation(divider.getBounds().y);
1179 dividerSize = divider.getSize().height;
1180 if(!isContinuousLayout() && draggingHW) {
1181 nonContinuousLayoutDivider.setBounds
1182 (0, getLastDragLocation(), splitPane.getWidth(),
1183 dividerSize);
1184 addHeavyweightDivider();
1185 }
1186 }
1187 }
1188
1189
1190 /**
1191 * Messaged during a dragging session to move the divider to the
1192 * passed in location. If continuousLayout is true the location is
1193 * reset and the splitPane validated.
1194 */
1195 protected void dragDividerTo(int location) {
1196 if(getLastDragLocation() != location) {
1197 if(isContinuousLayout()) {
1198 splitPane.setDividerLocation(location);
1199 setLastDragLocation(location);
1200 } else {
1201 int lastLoc = getLastDragLocation();
1202
1203 setLastDragLocation(location);
1204 if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1205 if(draggingHW) {
1206 nonContinuousLayoutDivider.setLocation(
1207 getLastDragLocation(), 0);
1208 } else {
1209 int splitHeight = splitPane.getHeight();
1210 splitPane.repaint(lastLoc, 0, dividerSize,
1211 splitHeight);
1212 splitPane.repaint(location, 0, dividerSize,
1213 splitHeight);
1214 }
1215 } else {
1216 if(draggingHW) {
1217 nonContinuousLayoutDivider.setLocation(0,
1218 getLastDragLocation());
1219 } else {
1220 int splitWidth = splitPane.getWidth();
1221
1222 splitPane.repaint(0, lastLoc, splitWidth,
1223 dividerSize);
1224 splitPane.repaint(0, location, splitWidth,
1225 dividerSize);
1226 }
1227 }
1228 }
1229 }
1230 }
1231
1232
1233 /**
1234 * Messaged to finish the dragging session. If not continuous display
1235 * the dividers location will be reset.
1236 */
1237 protected void finishDraggingTo(int location) {
1238 dragDividerTo(location);
1239 setLastDragLocation(-1);
1240 if(!isContinuousLayout()) {
1241 Component leftC = splitPane.getLeftComponent();
1242 Rectangle leftBounds = leftC.getBounds();
1243
1244 if (draggingHW) {
1245 if(orientation == JSplitPane.HORIZONTAL_SPLIT) {
1246 nonContinuousLayoutDivider.setLocation(-dividerSize, 0);
1247 }
1248 else {
1249 nonContinuousLayoutDivider.setLocation(0, -dividerSize);
1250 }
1251 splitPane.remove(nonContinuousLayoutDivider);
1252 }
1253 splitPane.setDividerLocation(location);
1254 }
1255 }
1256
1257
1258 /**
1259 * As of Java 2 platform v1.3 this method is no longer used. Instead
1260 * you should set the border on the divider.
1261 * <p>
1262 * Returns the width of one side of the divider border.
1263 *
1264 * @deprecated As of Java 2 platform v1.3, instead set the border on the
1265 * divider.
1266 */
1267 @Deprecated
1268 protected int getDividerBorderSize() {
1269 return 1;
1270 }
1271
1272
1273 /**
1274 * LayoutManager for JSplitPanes that have an orientation of
1275 * HORIZONTAL_SPLIT.
1276 */
1277 public class BasicHorizontalLayoutManager implements LayoutManager2
1278 {
1279 /* left, right, divider. (in this exact order) */
1280 protected int[] sizes;
1281 protected Component[] components;
1282 /** Size of the splitpane the last time laid out. */
1283 private int lastSplitPaneSize;
1284 /** True if resetToPreferredSizes has been invoked. */
1285 private boolean doReset;
1286 /** Axis, 0 for horizontal, or 1 for veritcal. */
1287 private int axis;
1288
1289
1290 BasicHorizontalLayoutManager() {
1291 this(0);
1292 }
1293
1294 BasicHorizontalLayoutManager(int axis) {
1295 this.axis = axis;
1296 components = new Component[3];
1297 components[0] = components[1] = components[2] = null;
1298 sizes = new int[3];
1299 }
1300
1301 //
1302 // LayoutManager
1303 //
1304
1305 /**
1306 * Does the actual layout.
1307 */
1308 public void layoutContainer(Container container) {
1309 Dimension containerSize = container.getSize();
1310
1311 // If the splitpane has a zero size then no op out of here.
1312 // If we execute this function now, we're going to cause ourselves
1313 // much grief.
1314 if (containerSize.height <= 0 || containerSize.width <= 0 ) {
1315 lastSplitPaneSize = 0;
1316 return;
1317 }
1318
1319 int spDividerLocation = splitPane.getDividerLocation();
1320 Insets insets = splitPane.getInsets();
1321 int availableSize = getAvailableSize(containerSize,
1322 insets);
1323 int newSize = getSizeForPrimaryAxis(containerSize);
1324 int beginLocation = getDividerLocation(splitPane);
1325 int dOffset = getSizeForPrimaryAxis(insets, true);
1326 Dimension dSize = (components[2] == null) ? null :
1327 components[2].getPreferredSize();
1328
1329 if ((doReset && !dividerLocationIsSet) || spDividerLocation < 0) {
1330 resetToPreferredSizes(availableSize);
1331 }
1332 else if (lastSplitPaneSize <= 0 ||
1333 availableSize == lastSplitPaneSize || !painted ||
1334 (dSize != null &&
1335 getSizeForPrimaryAxis(dSize) != sizes[2])) {
1336 if (dSize != null) {
1337 sizes[2] = getSizeForPrimaryAxis(dSize);
1338 }
1339 else {
1340 sizes[2] = 0;
1341 }
1342 setDividerLocation(spDividerLocation - dOffset, availableSize);
1343 dividerLocationIsSet = false;
1344 }
1345 else if (availableSize != lastSplitPaneSize) {
1346 distributeSpace(availableSize - lastSplitPaneSize,
1347 getKeepHidden());
1348 }
1349 doReset = false;
1350 dividerLocationIsSet = false;
1351 lastSplitPaneSize = availableSize;
1352
1353 // Reset the bounds of each component
1354 int nextLocation = getInitialLocation(insets);
1355 int counter = 0;
1356
1357 while (counter < 3) {
1358 if (components[counter] != null &&
1359 components[counter].isVisible()) {
1360 setComponentToSize(components[counter], sizes[counter],
1361 nextLocation, insets, containerSize);
1362 nextLocation += sizes[counter];
1363 }
1364 switch (counter) {
1365 case 0:
1366 counter = 2;
1367 break;
1368 case 2:
1369 counter = 1;
1370 break;
1371 case 1:
1372 counter = 3;
1373 break;
1374 }
1375 }
1376 if (painted) {
1377 // This is tricky, there is never a good time for us
1378 // to push the value to the splitpane, painted appears to
1379 // the best time to do it. What is really needed is
1380 // notification that layout has completed.
1381 int newLocation = getDividerLocation(splitPane);
1382
1383 if (newLocation != (spDividerLocation - dOffset)) {
1384 int lastLocation = splitPane.getLastDividerLocation();
1385
1386 ignoreDividerLocationChange = true;
1387 try {
1388 splitPane.setDividerLocation(newLocation);
1389 // This is not always needed, but is rather tricky
1390 // to determine when... The case this is needed for
1391 // is if the user sets the divider location to some
1392 // bogus value, say 0, and the actual value is 1, the
1393 // call to setDividerLocation(1) will preserve the
1394 // old value of 0, when we really want the divider
1395 // location value before the call. This is needed for
1396 // the one touch buttons.
1397 splitPane.setLastDividerLocation(lastLocation);
1398 } finally {
1399 ignoreDividerLocationChange = false;
1400 }
1401 }
1402 }
1403 }
1404
1405
1406 /**
1407 * Adds the component at place. Place must be one of
1408 * JSplitPane.LEFT, RIGHT, TOP, BOTTOM, or null (for the
1409 * divider).
1410 */
1411 public void addLayoutComponent(String place, Component component) {
1412 boolean isValid = true;
1413
1414 if(place != null) {
1415 if(place.equals(JSplitPane.DIVIDER)) {
1416 /* Divider. */
1417 components[2] = component;
1418 sizes[2] = getSizeForPrimaryAxis(component.
1419 getPreferredSize());
1420 } else if(place.equals(JSplitPane.LEFT) ||
1421 place.equals(JSplitPane.TOP)) {
1422 components[0] = component;
1423 sizes[0] = 0;
1424 } else if(place.equals(JSplitPane.RIGHT) ||
1425 place.equals(JSplitPane.BOTTOM)) {
1426 components[1] = component;
1427 sizes[1] = 0;
1428 } else if(!place.equals(
1429 BasicSplitPaneUI.NON_CONTINUOUS_DIVIDER))
1430 isValid = false;
1431 } else {
1432 isValid = false;
1433 }
1434 if(!isValid)
1435 throw new IllegalArgumentException("cannot add to layout: " +
1436 "unknown constraint: " +
1437 place);
1438 doReset = true;
1439 }
1440
1441
1442 /**
1443 * Returns the minimum size needed to contain the children.
1444 * The width is the sum of all the childrens min widths and
1445 * the height is the largest of the childrens minimum heights.
1446 */
1447 public Dimension minimumLayoutSize(Container container) {
1448 int minPrimary = 0;
1449 int minSecondary = 0;
1450 Insets insets = splitPane.getInsets();
1451
1452 for (int counter=0; counter<3; counter++) {
1453 if(components[counter] != null) {
1454 Dimension minSize = components[counter].getMinimumSize();
1455 int secSize = getSizeForSecondaryAxis(minSize);
1456
1457 minPrimary += getSizeForPrimaryAxis(minSize);
1458 if(secSize > minSecondary)
1459 minSecondary = secSize;
1460 }
1461 }
1462 if(insets != null) {
1463 minPrimary += getSizeForPrimaryAxis(insets, true) +
1464 getSizeForPrimaryAxis(insets, false);
1465 minSecondary += getSizeForSecondaryAxis(insets, true) +
1466 getSizeForSecondaryAxis(insets, false);
1467 }
1468 if (axis == 0) {
1469 return new Dimension(minPrimary, minSecondary);
1470 }
1471 return new Dimension(minSecondary, minPrimary);
1472 }
1473
1474
1475 /**
1476 * Returns the preferred size needed to contain the children.
1477 * The width is the sum of all the childrens preferred widths and
1478 * the height is the largest of the childrens preferred heights.
1479 */
1480 public Dimension preferredLayoutSize(Container container) {
1481 int prePrimary = 0;
1482 int preSecondary = 0;
1483 Insets insets = splitPane.getInsets();
1484
1485 for(int counter = 0; counter < 3; counter++) {
1486 if(components[counter] != null) {
1487 Dimension preSize = components[counter].
1488 getPreferredSize();
1489 int secSize = getSizeForSecondaryAxis(preSize);
1490
1491 prePrimary += getSizeForPrimaryAxis(preSize);
1492 if(secSize > preSecondary)
1493 preSecondary = secSize;
1494 }
1495 }
1496 if(insets != null) {
1497 prePrimary += getSizeForPrimaryAxis(insets, true) +
1498 getSizeForPrimaryAxis(insets, false);
1499 preSecondary += getSizeForSecondaryAxis(insets, true) +
1500 getSizeForSecondaryAxis(insets, false);
1501 }
1502 if (axis == 0) {
1503 return new Dimension(prePrimary, preSecondary);
1504 }
1505 return new Dimension(preSecondary, prePrimary);
1506 }
1507
1508
1509 /**
1510 * Removes the specified component from our knowledge.
1511 */
1512 public void removeLayoutComponent(Component component) {
1513 for(int counter = 0; counter < 3; counter++) {
1514 if(components[counter] == component) {
1515 components[counter] = null;
1516 sizes[counter] = 0;
1517 doReset = true;
1518 }
1519 }
1520 }
1521
1522
1523 //
1524 // LayoutManager2
1525 //
1526
1527
1528 /**
1529 * Adds the specified component to the layout, using the specified
1530 * constraint object.
1531 * @param comp the component to be added
1532 * @param constraints where/how the component is added to the layout.
1533 */
1534 public void addLayoutComponent(Component comp, Object constraints) {
1535 if ((constraints == null) || (constraints instanceof String)) {
1536 addLayoutComponent((String)constraints, comp);
1537 } else {
1538 throw new IllegalArgumentException("cannot add to layout: " +
1539 "constraint must be a " +
1540 "string (or null)");
1541 }
1542 }
1543
1544
1545 /**
1546 * Returns the alignment along the x axis. This specifies how
1547 * the component would like to be aligned relative to other
1548 * components. The value should be a number between 0 and 1
1549 * where 0 represents alignment along the origin, 1 is aligned
1550 * the furthest away from the origin, 0.5 is centered, etc.
1551 */
1552 public float getLayoutAlignmentX(Container target) {
1553 return 0.0f;
1554 }
1555
1556
1557 /**
1558 * Returns the alignment along the y axis. This specifies how
1559 * the component would like to be aligned relative to other
1560 * components. The value should be a number between 0 and 1
1561 * where 0 represents alignment along the origin, 1 is aligned
1562 * the furthest away from the origin, 0.5 is centered, etc.
1563 */
1564 public float getLayoutAlignmentY(Container target) {
1565 return 0.0f;
1566 }
1567
1568
1569 /**
1570 * Does nothing. If the developer really wants to change the
1571 * size of one of the views JSplitPane.resetToPreferredSizes should
1572 * be messaged.
1573 */
1574 public void invalidateLayout(Container c) {
1575 }
1576
1577
1578 /**
1579 * Returns the maximum layout size, which is Integer.MAX_VALUE
1580 * in both directions.
1581 */
1582 public Dimension maximumLayoutSize(Container target) {
1583 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1584 }
1585
1586
1587 //
1588 // New methods.
1589 //
1590
1591 /**
1592 * Marks the receiver so that the next time this instance is
1593 * laid out it'll ask for the preferred sizes.
1594 */
1595 public void resetToPreferredSizes() {
1596 doReset = true;
1597 }
1598
1599 /**
1600 * Resets the size of the Component at the passed in location.
1601 */
1602 protected void resetSizeAt(int index) {
1603 sizes[index] = 0;
1604 doReset = true;
1605 }
1606
1607
1608 /**
1609 * Sets the sizes to <code>newSizes</code>.
1610 */
1611 protected void setSizes(int[] newSizes) {
1612 System.arraycopy(newSizes, 0, sizes, 0, 3);
1613 }
1614
1615
1616 /**
1617 * Returns the sizes of the components.
1618 */
1619 protected int[] getSizes() {
1620 int[] retSizes = new int[3];
1621
1622 System.arraycopy(sizes, 0, retSizes, 0, 3);
1623 return retSizes;
1624 }
1625
1626
1627 /**
1628 * Returns the width of the passed in Components preferred size.
1629 */
1630 protected int getPreferredSizeOfComponent(Component c) {
1631 return getSizeForPrimaryAxis(c.getPreferredSize());
1632 }
1633
1634
1635 /**
1636 * Returns the width of the passed in Components minimum size.
1637 */
1638 int getMinimumSizeOfComponent(Component c) {
1639 return getSizeForPrimaryAxis(c.getMinimumSize());
1640 }
1641
1642
1643 /**
1644 * Returns the width of the passed in component.
1645 */
1646 protected int getSizeOfComponent(Component c) {
1647 return getSizeForPrimaryAxis(c.getSize());
1648 }
1649
1650
1651 /**
1652 * Returns the available width based on the container size and
1653 * Insets.
1654 */
1655 protected int getAvailableSize(Dimension containerSize,
1656 Insets insets) {
1657 if(insets == null)
1658 return getSizeForPrimaryAxis(containerSize);
1659 return (getSizeForPrimaryAxis(containerSize) -
1660 (getSizeForPrimaryAxis(insets, true) +
1661 getSizeForPrimaryAxis(insets, false)));
1662 }
1663
1664
1665 /**
1666 * Returns the left inset, unless the Insets are null in which case
1667 * 0 is returned.
1668 */
1669 protected int getInitialLocation(Insets insets) {
1670 if(insets != null)
1671 return getSizeForPrimaryAxis(insets, true);
1672 return 0;
1673 }
1674
1675
1676 /**
1677 * Sets the width of the component c to be size, placing its
1678 * x location at location, y to the insets.top and height
1679 * to the containersize.height less the top and bottom insets.
1680 */
1681 protected void setComponentToSize(Component c, int size,
1682 int location, Insets insets,
1683 Dimension containerSize) {
1684 if(insets != null) {
1685 if (axis == 0) {
1686 c.setBounds(location, insets.top, size,
1687 containerSize.height -
1688 (insets.top + insets.bottom));
1689 }
1690 else {
1691 c.setBounds(insets.left, location, containerSize.width -
1692 (insets.left + insets.right), size);
1693 }
1694 }
1695 else {
1696 if (axis == 0) {
1697 c.setBounds(location, 0, size, containerSize.height);
1698 }
1699 else {
1700 c.setBounds(0, location, containerSize.width, size);
1701 }
1702 }
1703 }
1704
1705 /**
1706 * If the axis == 0, the width is returned, otherwise the height.
1707 */
1708 int getSizeForPrimaryAxis(Dimension size) {
1709 if (axis == 0) {
1710 return size.width;
1711 }
1712 return size.height;
1713 }
1714
1715 /**
1716 * If the axis == 0, the width is returned, otherwise the height.
1717 */
1718 int getSizeForSecondaryAxis(Dimension size) {
1719 if (axis == 0) {
1720 return size.height;
1721 }
1722 return size.width;
1723 }
1724
1725 /**
1726 * Returns a particular value of the inset identified by the
1727 * axis and <code>isTop</code><p>
1728 * axis isTop
1729 * 0 true - left
1730 * 0 false - right
1731 * 1 true - top
1732 * 1 false - bottom
1733 */
1734 int getSizeForPrimaryAxis(Insets insets, boolean isTop) {
1735 if (axis == 0) {
1736 if (isTop) {
1737 return insets.left;
1738 }
1739 return insets.right;
1740 }
1741 if (isTop) {
1742 return insets.top;
1743 }
1744 return insets.bottom;
1745 }
1746
1747 /**
1748 * Returns a particular value of the inset identified by the
1749 * axis and <code>isTop</code><p>
1750 * axis isTop
1751 * 0 true - left
1752 * 0 false - right
1753 * 1 true - top
1754 * 1 false - bottom
1755 */
1756 int getSizeForSecondaryAxis(Insets insets, boolean isTop) {
1757 if (axis == 0) {
1758 if (isTop) {
1759 return insets.top;
1760 }
1761 return insets.bottom;
1762 }
1763 if (isTop) {
1764 return insets.left;
1765 }
1766 return insets.right;
1767 }
1768
1769 /**
1770 * Determines the components. This should be called whenever
1771 * a new instance of this is installed into an existing
1772 * SplitPane.
1773 */
1774 protected void updateComponents() {
1775 Component comp;
1776
1777 comp = splitPane.getLeftComponent();
1778 if(components[0] != comp) {
1779 components[0] = comp;
1780 if(comp == null) {
1781 sizes[0] = 0;
1782 } else {
1783 sizes[0] = -1;
1784 }
1785 }
1786
1787 comp = splitPane.getRightComponent();
1788 if(components[1] != comp) {
1789 components[1] = comp;
1790 if(comp == null) {
1791 sizes[1] = 0;
1792 } else {
1793 sizes[1] = -1;
1794 }
1795 }
1796
1797 /* Find the divider. */
1798 Component[] children = splitPane.getComponents();
1799 Component oldDivider = components[2];
1800
1801 components[2] = null;
1802 for(int counter = children.length - 1; counter >= 0; counter--) {
1803 if(children[counter] != components[0] &&
1804 children[counter] != components[1] &&
1805 children[counter] != nonContinuousLayoutDivider) {
1806 if(oldDivider != children[counter]) {
1807 components[2] = children[counter];
1808 } else {
1809 components[2] = oldDivider;
1810 }
1811 break;
1812 }
1813 }
1814 if(components[2] == null) {
1815 sizes[2] = 0;
1816 }
1817 else {
1818 sizes[2] = getSizeForPrimaryAxis(components[2].getPreferredSize());
1819 }
1820 }
1821
1822 /**
1823 * Resets the size of the first component to <code>leftSize</code>,
1824 * and the right component to the remainder of the space.
1825 */
1826 void setDividerLocation(int leftSize, int availableSize) {
1827 boolean lValid = (components[0] != null &&
1828 components[0].isVisible());
1829 boolean rValid = (components[1] != null &&
1830 components[1].isVisible());
1831 boolean dValid = (components[2] != null &&
1832 components[2].isVisible());
1833 int max = availableSize;
1834
1835 if (dValid) {
1836 max -= sizes[2];
1837 }
1838 leftSize = Math.max(0, Math.min(leftSize, max));
1839 if (lValid) {
1840 if (rValid) {
1841 sizes[0] = leftSize;
1842 sizes[1] = max - leftSize;
1843 }
1844 else {
1845 sizes[0] = max;
1846 sizes[1] = 0;
1847 }
1848 }
1849 else if (rValid) {
1850 sizes[1] = max;
1851 sizes[0] = 0;
1852 }
1853 }
1854
1855 /**
1856 * Returns an array of the minimum sizes of the components.
1857 */
1858 int[] getPreferredSizes() {
1859 int[] retValue = new int[3];
1860
1861 for (int counter = 0; counter < 3; counter++) {
1862 if (components[counter] != null &&
1863 components[counter].isVisible()) {
1864 retValue[counter] = getPreferredSizeOfComponent
1865 (components[counter]);
1866 }
1867 else {
1868 retValue[counter] = -1;
1869 }
1870 }
1871 return retValue;
1872 }
1873
1874 /**
1875 * Returns an array of the minimum sizes of the components.
1876 */
1877 int[] getMinimumSizes() {
1878 int[] retValue = new int[3];
1879
1880 for (int counter = 0; counter < 2; counter++) {
1881 if (components[counter] != null &&
1882 components[counter].isVisible()) {
1883 retValue[counter] = getMinimumSizeOfComponent
1884 (components[counter]);
1885 }
1886 else {
1887 retValue[counter] = -1;
1888 }
1889 }
1890 retValue[2] = (components[2] != null) ?
1891 getMinimumSizeOfComponent(components[2]) : -1;
1892 return retValue;
1893 }
1894
1895 /**
1896 * Resets the components to their preferred sizes.
1897 */
1898 void resetToPreferredSizes(int availableSize) {
1899 // Set the sizes to the preferred sizes (if fits), otherwise
1900 // set to min sizes and distribute any extra space.
1901 int[] testSizes = getPreferredSizes();
1902 int totalSize = 0;
1903
1904 for (int counter = 0; counter < 3; counter++) {
1905 if (testSizes[counter] != -1) {
1906 totalSize += testSizes[counter];
1907 }
1908 }
1909 if (totalSize > availableSize) {
1910 testSizes = getMinimumSizes();
1911
1912 totalSize = 0;
1913 for (int counter = 0; counter < 3; counter++) {
1914 if (testSizes[counter] != -1) {
1915 totalSize += testSizes[counter];
1916 }
1917 }
1918 }
1919 setSizes(testSizes);
1920 distributeSpace(availableSize - totalSize, false);
1921 }
1922
1923 /**
1924 * Distributes <code>space</code> between the two components
1925 * (divider won't get any extra space) based on the weighting. This
1926 * attempts to honor the min size of the components.
1927 *
1928 * @param keepHidden if true and one of the components is 0x0
1929 * it gets none of the extra space
1930 */
1931 void distributeSpace(int space, boolean keepHidden) {
1932 boolean lValid = (components[0] != null &&
1933 components[0].isVisible());
1934 boolean rValid = (components[1] != null &&
1935 components[1].isVisible());
1936
1937 if (keepHidden) {
1938 if (lValid && getSizeForPrimaryAxis(
1939 components[0].getSize()) == 0) {
1940 lValid = false;
1941 if (rValid && getSizeForPrimaryAxis(
1942 components[1].getSize()) == 0) {
1943 // Both aren't valid, force them both to be valid
1944 lValid = true;
1945 }
1946 }
1947 else if (rValid && getSizeForPrimaryAxis(
1948 components[1].getSize()) == 0) {
1949 rValid = false;
1950 }
1951 }
1952 if (lValid && rValid) {
1953 double weight = splitPane.getResizeWeight();
1954 int lExtra = (int)(weight * (double)space);
1955 int rExtra = (space - lExtra);
1956
1957 sizes[0] += lExtra;
1958 sizes[1] += rExtra;
1959
1960 int lMin = getMinimumSizeOfComponent(components[0]);
1961 int rMin = getMinimumSizeOfComponent(components[1]);
1962 boolean lMinValid = (sizes[0] >= lMin);
1963 boolean rMinValid = (sizes[1] >= rMin);
1964
1965 if (!lMinValid && !rMinValid) {
1966 if (sizes[0] < 0) {
1967 sizes[1] += sizes[0];
1968 sizes[0] = 0;
1969 }
1970 else if (sizes[1] < 0) {
1971 sizes[0] += sizes[1];
1972 sizes[1] = 0;
1973 }
1974 }
1975 else if (!lMinValid) {
1976 if (sizes[1] - (lMin - sizes[0]) < rMin) {
1977 // both below min, just make sure > 0
1978 if (sizes[0] < 0) {
1979 sizes[1] += sizes[0];
1980 sizes[0] = 0;
1981 }
1982 }
1983 else {
1984 sizes[1] -= (lMin - sizes[0]);
1985 sizes[0] = lMin;
1986 }
1987 }
1988 else if (!rMinValid) {
1989 if (sizes[0] - (rMin - sizes[1]) < lMin) {
1990 // both below min, just make sure > 0
1991 if (sizes[1] < 0) {
1992 sizes[0] += sizes[1];
1993 sizes[1] = 0;
1994 }
1995 }
1996 else {
1997 sizes[0] -= (rMin - sizes[1]);
1998 sizes[1] = rMin;
1999 }
2000 }
2001 if (sizes[0] < 0) {
2002 sizes[0] = 0;
2003 }
2004 if (sizes[1] < 0) {
2005 sizes[1] = 0;
2006 }
2007 }
2008 else if (lValid) {
2009 sizes[0] = Math.max(0, sizes[0] + space);
2010 }
2011 else if (rValid) {
2012 sizes[1] = Math.max(0, sizes[1] + space);
2013 }
2014 }
2015 }
2016
2017
2018 /**
2019 * LayoutManager used for JSplitPanes with an orientation of
2020 * VERTICAL_SPLIT.
2021 * <p>
2022 */
2023 public class BasicVerticalLayoutManager extends
2024 BasicHorizontalLayoutManager
2025 {
2026 public BasicVerticalLayoutManager() {
2027 super(1);
2028 }
2029 }
2030
2031
2032 private class Handler implements FocusListener, PropertyChangeListener {
2033 //
2034 // PropertyChangeListener
2035 //
2036 /**
2037 * Messaged from the <code>JSplitPane</code> the receiver is
2038 * contained in. May potentially reset the layout manager and cause a
2039 * <code>validate</code> to be sent.
2040 */
2041 public void propertyChange(PropertyChangeEvent e) {
2042 if(e.getSource() == splitPane) {
2043 String changeName = e.getPropertyName();
2044
2045 if(changeName == JSplitPane.ORIENTATION_PROPERTY) {
2046 orientation = splitPane.getOrientation();
2047 resetLayoutManager();
2048 } else if(changeName == JSplitPane.CONTINUOUS_LAYOUT_PROPERTY){
2049 setContinuousLayout(splitPane.isContinuousLayout());
2050 if(!isContinuousLayout()) {
2051 if(nonContinuousLayoutDivider == null) {
2052 setNonContinuousLayoutDivider(
2053 createDefaultNonContinuousLayoutDivider(),
2054 true);
2055 } else if(nonContinuousLayoutDivider.getParent() ==
2056 null) {
2057 setNonContinuousLayoutDivider(
2058 nonContinuousLayoutDivider,
2059 true);
2060 }
2061 }
2062 } else if(changeName == JSplitPane.DIVIDER_SIZE_PROPERTY){
2063 divider.setDividerSize(splitPane.getDividerSize());
2064 dividerSize = divider.getDividerSize();
2065 splitPane.revalidate();
2066 splitPane.repaint();
2067 }
2068 }
2069 }
2070
2071 //
2072 // FocusListener
2073 //
2074 public void focusGained(FocusEvent ev) {
2075 dividerKeyboardResize = true;
2076 splitPane.repaint();
2077 }
2078
2079 public void focusLost(FocusEvent ev) {
2080 dividerKeyboardResize = false;
2081 splitPane.repaint();
2082 }
2083 }
2084
2085
2086 private static class Actions extends UIAction {
2087 private static final String NEGATIVE_INCREMENT = "negativeIncrement";
2088 private static final String POSITIVE_INCREMENT = "positiveIncrement";
2089 private static final String SELECT_MIN = "selectMin";
2090 private static final String SELECT_MAX = "selectMax";
2091 private static final String START_RESIZE = "startResize";
2092 private static final String TOGGLE_FOCUS = "toggleFocus";
2093 private static final String FOCUS_OUT_FORWARD = "focusOutForward";
2094 private static final String FOCUS_OUT_BACKWARD = "focusOutBackward";
2095
2096 Actions(String key) {
2097 super(key);
2098 }
2099
2100 public void actionPerformed(ActionEvent ev) {
2101 JSplitPane splitPane = (JSplitPane)ev.getSource();
2102 BasicSplitPaneUI ui = (BasicSplitPaneUI)BasicLookAndFeel.
2103 getUIOfType(splitPane.getUI(), BasicSplitPaneUI.class);
2104
2105 if (ui == null) {
2106 return;
2107 }
2108 String key = getName();
2109 if (key == NEGATIVE_INCREMENT) {
2110 if (ui.dividerKeyboardResize) {
2111 splitPane.setDividerLocation(Math.max(
2112 0, ui.getDividerLocation
2113 (splitPane) - ui.getKeyboardMoveIncrement()));
2114 }
2115 }
2116 else if (key == POSITIVE_INCREMENT) {
2117 if (ui.dividerKeyboardResize) {
2118 splitPane.setDividerLocation(
2119 ui.getDividerLocation(splitPane) +
2120 ui.getKeyboardMoveIncrement());
2121 }
2122 }
2123 else if (key == SELECT_MIN) {
2124 if (ui.dividerKeyboardResize) {
2125 splitPane.setDividerLocation(0);
2126 }
2127 }
2128 else if (key == SELECT_MAX) {
2129 if (ui.dividerKeyboardResize) {
2130 Insets insets = splitPane.getInsets();
2131 int bottomI = (insets != null) ? insets.bottom : 0;
2132 int rightI = (insets != null) ? insets.right : 0;
2133
2134 if (ui.orientation == JSplitPane.VERTICAL_SPLIT) {
2135 splitPane.setDividerLocation(splitPane.getHeight() -
2136 bottomI);
2137 }
2138 else {
2139 splitPane.setDividerLocation(splitPane.getWidth() -
2140 rightI);
2141 }
2142 }
2143 }
2144 else if (key == START_RESIZE) {
2145 if (!ui.dividerKeyboardResize) {
2146 splitPane.requestFocus();
2147 } else {
2148 JSplitPane parentSplitPane =
2149 (JSplitPane)SwingUtilities.getAncestorOfClass(
2150 JSplitPane.class, splitPane);
2151 if (parentSplitPane!=null) {
2152 parentSplitPane.requestFocus();
2153 }
2154 }
2155 }
2156 else if (key == TOGGLE_FOCUS) {
2157 toggleFocus(splitPane);
2158 }
2159 else if (key == FOCUS_OUT_FORWARD) {
2160 moveFocus(splitPane, 1);
2161 }
2162 else if (key == FOCUS_OUT_BACKWARD) {
2163 moveFocus(splitPane, -1);
2164 }
2165 }
2166
2167 private void moveFocus(JSplitPane splitPane, int direction) {
2168 Container rootAncestor = splitPane.getFocusCycleRootAncestor();
2169 FocusTraversalPolicy policy = rootAncestor.getFocusTraversalPolicy();
2170 Component focusOn = (direction > 0) ?
2171 policy.getComponentAfter(rootAncestor, splitPane) :
2172 policy.getComponentBefore(rootAncestor, splitPane);
2173 HashSet focusFrom = new HashSet();
2174 if (splitPane.isAncestorOf(focusOn)) {
2175 do {
2176 focusFrom.add(focusOn);
2177 rootAncestor = focusOn.getFocusCycleRootAncestor();
2178 policy = rootAncestor.getFocusTraversalPolicy();
2179 focusOn = (direction > 0) ?
2180 policy.getComponentAfter(rootAncestor, focusOn) :
2181 policy.getComponentBefore(rootAncestor, focusOn);
2182 } while (splitPane.isAncestorOf(focusOn) &&
2183 !focusFrom.contains(focusOn));
2184 }
2185 if ( focusOn!=null && !splitPane.isAncestorOf(focusOn) ) {
2186 focusOn.requestFocus();
2187 }
2188 }
2189
2190 private void toggleFocus(JSplitPane splitPane) {
2191 Component left = splitPane.getLeftComponent();
2192 Component right = splitPane.getRightComponent();
2193
2194 KeyboardFocusManager manager =
2195 KeyboardFocusManager.getCurrentKeyboardFocusManager();
2196 Component focus = manager.getFocusOwner();
2197 Component focusOn = getNextSide(splitPane, focus);
2198 if (focusOn != null) {
2199 // don't change the focus if the new focused component belongs
2200 // to the same splitpane and the same side
2201 if ( focus!=null &&
2202 ( (SwingUtilities.isDescendingFrom(focus, left) &&
2203 SwingUtilities.isDescendingFrom(focusOn, left)) ||
2204 (SwingUtilities.isDescendingFrom(focus, right) &&
2205 SwingUtilities.isDescendingFrom(focusOn, right)) ) ) {
2206 return;
2207 }
2208 SwingUtilities2.compositeRequestFocus(focusOn);
2209 }
2210 }
2211
2212 private Component getNextSide(JSplitPane splitPane, Component focus) {
2213 Component left = splitPane.getLeftComponent();
2214 Component right = splitPane.getRightComponent();
2215 Component next = null;
2216 if (focus!=null && SwingUtilities.isDescendingFrom(focus, left) &&
2217 right!=null) {
2218 next = getFirstAvailableComponent(right);
2219 if (next != null) {
2220 return next;
2221 }
2222 }
2223 JSplitPane parentSplitPane = (JSplitPane)SwingUtilities.getAncestorOfClass(JSplitPane.class, splitPane);
2224 if (parentSplitPane!=null) {
2225 // focus next side of the parent split pane
2226 next = getNextSide(parentSplitPane, focus);
2227 } else {
2228 next = getFirstAvailableComponent(left);
2229 if (next == null) {
2230 next = getFirstAvailableComponent(right);
2231 }
2232 }
2233 return next;
2234 }
2235
2236 private Component getFirstAvailableComponent(Component c) {
2237 if (c!=null && c instanceof JSplitPane) {
2238 JSplitPane sp = (JSplitPane)c;
2239 Component left = getFirstAvailableComponent(sp.getLeftComponent());
2240 if (left != null) {
2241 c = left;
2242 } else {
2243 c = getFirstAvailableComponent(sp.getRightComponent());
2244 }
2245 }
2246 return c;
2247 }
2248 }
2249}