History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/plaf/basic/BasicSplitPaneUI.java (1 of 18352 files). Show all

Unified · Split

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