1 /* BasicSplitPaneUI.java --
2    Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 
39 package javax.swing.plaf.basic;
40 
41 import java.awt.Canvas;
42 import java.awt.Color;
43 import java.awt.Component;
44 import java.awt.Container;
45 import java.awt.Dimension;
46 import java.awt.Graphics;
47 import java.awt.Insets;
48 import java.awt.LayoutManager2;
49 import java.awt.Point;
50 import java.awt.event.ActionEvent;
51 import java.awt.event.ActionListener;
52 import java.awt.event.FocusAdapter;
53 import java.awt.event.FocusEvent;
54 import java.awt.event.FocusListener;
55 import java.beans.PropertyChangeEvent;
56 import java.beans.PropertyChangeListener;
57 
58 import javax.swing.JComponent;
59 import javax.swing.JSplitPane;
60 import javax.swing.KeyStroke;
61 import javax.swing.LookAndFeel;
62 import javax.swing.UIManager;
63 import javax.swing.plaf.ComponentUI;
64 import javax.swing.plaf.SplitPaneUI;
65 
66 /**
67  * This is the Basic Look and Feel implementation of the SplitPaneUI  class.
68  */
69 public class BasicSplitPaneUI extends SplitPaneUI
70 {
71   /**
72    * This Layout Manager controls the position and size of the components when
73    * the JSplitPane's orientation is HORIZONTAL_SPLIT.
74    *
75    * @specnote Apparently this class was intended to be protected,
76    *           but was made public by a compiler bug and is now
77    *           public for compatibility.
78    */
79   public class BasicHorizontalLayoutManager implements LayoutManager2
80   {
81     // 3 components at a time.
82     // LEFT/TOP = 0
83     // RIGHT/BOTTOM = 1
84     // DIVIDER = 2
85 
86     /**
87      * This array contains the components in the JSplitPane. The  left/top
88      * component is at index 0, the right/bottom is at 1, and the divider is
89      * at 2.
90      */
91     protected Component[] components = new Component[3];
92 
93     // These are the _current_ widths of the associated component.
94 
95     /**
96      * This array contains the current width (for HORIZONTAL_SPLIT) or height
97      * (for VERTICAL_SPLIT) of the components. The indices are the same as
98      * for components.
99      */
100     protected int[] sizes = new int[3];
101 
102     /**
103      * This method adds the component given to the JSplitPane. The position of
104      * the component is given by the constraints object.
105      *
106      * @param comp The Component to add.
107      * @param constraints The constraints that bind the object.
108      */
addLayoutComponent(Component comp, Object constraints)109     public void addLayoutComponent(Component comp, Object constraints)
110     {
111       addLayoutComponent((String) constraints, comp);
112     }
113 
114     /**
115      * This method is called to add a Component to the JSplitPane. The
116      * placement string determines where the Component will be placed. The
117      * string should be one of LEFT, RIGHT, TOP, BOTTOM or null (signals that
118      * the component is the divider).
119      *
120      * @param place The placement of the Component.
121      * @param component The Component to add.
122      *
123      * @throws IllegalArgumentException DOCUMENT ME!
124      */
addLayoutComponent(String place, Component component)125     public void addLayoutComponent(String place, Component component)
126     {
127       int i = 0;
128       if (place == null)
129 	i = 2;
130       else if (place.equals(JSplitPane.TOP) || place.equals(JSplitPane.LEFT))
131 	i = 0;
132       else if (place.equals(JSplitPane.BOTTOM)
133                || place.equals(JSplitPane.RIGHT))
134 	i = 1;
135       else
136 	throw new IllegalArgumentException("Illegal placement in JSplitPane");
137       components[i] = component;
138       resetSizeAt(i);
139       splitPane.revalidate();
140       splitPane.repaint();
141     }
142 
143     /**
144      * This method returns the width of the JSplitPane minus the insets.
145      *
146      * @param containerSize The Dimensions of the JSplitPane.
147      * @param insets The Insets of the JSplitPane.
148      *
149      * @return The width of the JSplitPane minus the insets.
150      */
getAvailableSize(Dimension containerSize, Insets insets)151     protected int getAvailableSize(Dimension containerSize, Insets insets)
152     {
153       return containerSize.width - insets.left - insets.right;
154     }
155 
156     /**
157      * This method returns the given insets left value. If the  given inset is
158      * null, then 0 is returned.
159      *
160      * @param insets The Insets to use with the JSplitPane.
161      *
162      * @return The inset's left value.
163      */
getInitialLocation(Insets insets)164     protected int getInitialLocation(Insets insets)
165     {
166       if (insets != null)
167 	return insets.left;
168       return 0;
169     }
170 
171     /**
172      * This specifies how a component is aligned with respect to  other
173      * components in the x fdirection.
174      *
175      * @param target The container.
176      *
177      * @return The component's alignment.
178      */
getLayoutAlignmentX(Container target)179     public float getLayoutAlignmentX(Container target)
180     {
181       return target.getAlignmentX();
182     }
183 
184     /**
185      * This specifies how a component is aligned with respect to  other
186      * components in the y direction.
187      *
188      * @param target The container.
189      *
190      * @return The component's alignment.
191      */
getLayoutAlignmentY(Container target)192     public float getLayoutAlignmentY(Container target)
193     {
194       return target.getAlignmentY();
195     }
196 
197     /**
198      * This method returns the preferred width of the component.
199      *
200      * @param c The component to measure.
201      *
202      * @return The preferred width of the component.
203      */
getPreferredSizeOfComponent(Component c)204     protected int getPreferredSizeOfComponent(Component c)
205     {
206       Dimension dims = c.getPreferredSize();
207       if (dims != null)
208 	return dims.width;
209       return 0;
210     }
211 
212     /**
213      * This method returns the current width of the component.
214      *
215      * @param c The component to measure.
216      *
217      * @return The width of the component.
218      */
getSizeOfComponent(Component c)219     protected int getSizeOfComponent(Component c)
220     {
221       return c.getWidth();
222     }
223 
224     /**
225      * This method returns the sizes array.
226      *
227      * @return The sizes array.
228      */
getSizes()229     protected int[] getSizes()
230     {
231       return sizes;
232     }
233 
234     /**
235      * This method invalidates the layout. It does nothing.
236      *
237      * @param c The container to invalidate.
238      */
invalidateLayout(Container c)239     public void invalidateLayout(Container c)
240     {
241       // DO NOTHING
242     }
243 
244     /**
245      * This method lays out the components in the container.
246      *
247      * @param container The container to lay out.
248      */
layoutContainer(Container container)249     public void layoutContainer(Container container)
250     {
251       if (container instanceof JSplitPane)
252         {
253 	  JSplitPane split = (JSplitPane) container;
254 	  distributeExtraSpace();
255 	  Insets insets = split.getInsets();
256 	  int width = getInitialLocation(insets);
257 	  Dimension dims = split.getSize();
258 	  for (int i = 0; i < components.length; i += 2)
259 	    {
260 	      if (components[i] == null)
261 		continue;
262 	      setComponentToSize(components[i], sizes[i], width, insets, dims);
263 	      width += sizes[i];
264 	    }
265 	  if (components[1] != null)
266 	    {
267 	      setComponentToSize(components[1], sizes[1], width, insets, dims);
268 	      width += sizes[1];
269 	    }
270         }
271     }
272 
273     /**
274      * This method returns the maximum size for the container given the
275      * components. It returns a new Dimension object that has width and
276      * height equal to Integer.MAX_VALUE.
277      *
278      * @param target The container to measure.
279      *
280      * @return The maximum size.
281      */
maximumLayoutSize(Container target)282     public Dimension maximumLayoutSize(Container target)
283     {
284       return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
285     }
286 
287     /**
288      * This method returns the container's minimum size. The  minimum width is
289      * the sum of all the component's minimum widths. The minimum height is
290      * the maximum of  all the components' minimum heights.
291      *
292      * @param target The container to measure.
293      *
294      * @return The minimum size.
295      */
minimumLayoutSize(Container target)296     public Dimension minimumLayoutSize(Container target)
297     {
298       if (target instanceof JSplitPane)
299         {
300 	  JSplitPane split = (JSplitPane) target;
301 	  Insets insets = target.getInsets();
302 
303 	  int height = 0;
304 	  int width = 0;
305 	  for (int i = 0; i < components.length; i++)
306 	    {
307 	      if (components[i] == null)
308 		continue;
309 	      Dimension dims = components[i].getMinimumSize();
310 	      if (dims != null)
311 	        {
312 		  width += dims.width;
313 		  height = Math.max(height, dims.height);
314 	        }
315 	    }
316 	  return new Dimension(width, height);
317         }
318       return null;
319     }
320 
321     /**
322      * This method returns the container's preferred size. The preferred width
323      * is the sum of all the component's preferred widths. The preferred
324      * height is the maximum of all the components' preferred heights.
325      *
326      * @param target The container to measure.
327      *
328      * @return The preferred size.
329      */
preferredLayoutSize(Container target)330     public Dimension preferredLayoutSize(Container target)
331     {
332       if (target instanceof JSplitPane)
333         {
334 	  JSplitPane split = (JSplitPane) target;
335 	  Insets insets = target.getInsets();
336 
337 	  int height = 0;
338 	  int width = 0;
339 	  for (int i = 0; i < components.length; i++)
340 	    {
341 	      if (components[i] == null)
342 		continue;
343 	      Dimension dims = components[i].getPreferredSize();
344 	      if (dims != null)
345 	        {
346 		  width += dims.width;
347 		  if (! (components[i] instanceof BasicSplitPaneDivider))
348 		    height = Math.max(height, dims.height);
349 	        }
350 	    }
351 	  return new Dimension(width, height);
352         }
353       return null;
354     }
355 
356     /**
357      * This method removes the component from the layout.
358      *
359      * @param component The component to remove from the layout.
360      */
removeLayoutComponent(Component component)361     public void removeLayoutComponent(Component component)
362     {
363       for (int i = 0; i < components.length; i++)
364         {
365 	  if (component == components[i])
366 	    {
367 	      components[i] = null;
368 	      sizes[i] = 0;
369 	    }
370         }
371     }
372 
373     /**
374      * This method resets the size of Component to the preferred size.
375      *
376      * @param index The index of the component to reset.
377      */
resetSizeAt(int index)378     protected void resetSizeAt(int index)
379     {
380       if (components[index] != null)
381 	sizes[index] = getPreferredSizeOfComponent(components[index]);
382     }
383 
384     /**
385      * This method resets the sizes of all the components.
386      */
resetToPreferredSizes()387     public void resetToPreferredSizes()
388     {
389       for (int i = 0; i < components.length; i++)
390 	resetSizeAt(i);
391     }
392 
393     /**
394      * This methods sets the bounds of the given component. The width is the
395      * size. The height is the container size minus the  top and bottom
396      * inset. The x coordinate is the location given.  The y coordinate is
397      * the top inset.
398      *
399      * @param c The component to set.
400      * @param size The width of the component.
401      * @param location The x coordinate.
402      * @param insets The insets to use.
403      * @param containerSize The height of the container.
404      */
setComponentToSize(Component c, int size, int location, Insets insets, Dimension containerSize)405     protected void setComponentToSize(Component c, int size, int location,
406                                       Insets insets, Dimension containerSize)
407     {
408       int w = size;
409       int h = containerSize.height - insets.top - insets.bottom;
410       int x = location;
411       int y = insets.top;
412       c.setBounds(x, y, w, h);
413     }
414 
415     /**
416      * This method stores the given int array as the new sizes array.
417      *
418      * @param newSizes The array to use as sizes.
419      */
setSizes(int[] newSizes)420     protected void setSizes(int[] newSizes)
421     {
422       sizes = newSizes;
423     }
424 
425     /**
426      * This method determines the size of each  component. It should be called
427      * when a new Layout Manager is created for an existing JSplitPane.
428      */
updateComponents()429     protected void updateComponents()
430     {
431       Component left = splitPane.getLeftComponent();
432       Component right = splitPane.getRightComponent();
433 
434       if (left != null)
435         {
436 	  components[0] = left;
437 	  resetSizeAt(0);
438         }
439       if (right != null)
440         {
441 	  components[1] = right;
442 	  resetSizeAt(1);
443         }
444       components[2] = divider;
445       resetSizeAt(2);
446     }
447 
448     /**
449      * This method resizes the left and right components to fit inside the
450      * JSplitPane when there is extra space.
451      */
distributeExtraSpace()452     void distributeExtraSpace()
453     {
454       int availSize = getAvailableSize(splitPane.getSize(),
455                                        splitPane.getInsets());
456       int[] newSizes = new int[3];
457       double weight = splitPane.getResizeWeight();
458 
459       int oldLen = sizes[0] + sizes[1];
460 
461       // dividers don't change size.
462       availSize -= sizes[2] + oldLen;
463 
464       int rightAlloc = (int) (availSize * (1 - weight));
465       int leftAlloc = availSize - rightAlloc;
466 
467       sizes[0] += leftAlloc;
468       sizes[1] += rightAlloc;
469     }
470 
471     /**
472      * This method returns the minimum width of the  component at the given
473      * index.
474      *
475      * @param index The index to check.
476      *
477      * @return The minimum width.
478      */
minimumSizeOfComponent(int index)479     int minimumSizeOfComponent(int index)
480     {
481       Dimension dims = components[index].getMinimumSize();
482       if (dims != null)
483 	return dims.width;
484       else
485 	return 0;
486     }
487   } //end BasicHorizontalLayoutManager
488 
489   /**
490    * This class is the Layout Manager for the JSplitPane when the orientation
491    * is VERTICAL_SPLIT.
492    *
493    * @specnote Apparently this class was intended to be protected,
494    *           but was made public by a compiler bug and is now
495    *           public for compatibility.
496    */
497   public class BasicVerticalLayoutManager
498     extends BasicHorizontalLayoutManager
499   {
500     /**
501      * This method returns the height of the container minus the top and
502      * bottom inset.
503      *
504      * @param containerSize The size of the container.
505      * @param insets The insets of the container.
506      *
507      * @return The height minus top and bottom inset.
508      */
getAvailableSize(Dimension containerSize, Insets insets)509     protected int getAvailableSize(Dimension containerSize, Insets insets)
510     {
511       return containerSize.height - insets.top - insets.bottom;
512     }
513 
514     /**
515      * This method returns the top inset.
516      *
517      * @param insets The Insets to use.
518      *
519      * @return The top inset.
520      */
getInitialLocation(Insets insets)521     protected int getInitialLocation(Insets insets)
522     {
523       return insets.top;
524     }
525 
526     /**
527      * This method returns the preferred height of the component.
528      *
529      * @param c The component to measure.
530      *
531      * @return The preferred height of the component.
532      */
getPreferredSizeOfComponent(Component c)533     protected int getPreferredSizeOfComponent(Component c)
534     {
535       Dimension dims = c.getPreferredSize();
536       if (dims != null)
537 	return dims.height;
538       return 0;
539     }
540 
541     /**
542      * This method returns the current height of the component.
543      *
544      * @param c The component to measure.
545      *
546      * @return The current height of the component.
547      */
getSizeOfComponent(Component c)548     protected int getSizeOfComponent(Component c)
549     {
550       return c.getHeight();
551     }
552 
553     /**
554      * This method returns the minimum layout size. The minimum height is the
555      * sum of all the components' minimum heights. The minimum width is the
556      * maximum of all the  components' minimum widths.
557      *
558      * @param container The container to measure.
559      *
560      * @return The minimum size.
561      */
minimumLayoutSize(Container container)562     public Dimension minimumLayoutSize(Container container)
563     {
564       if (container instanceof JSplitPane)
565         {
566 	  JSplitPane split = (JSplitPane) container;
567 	  Insets insets = container.getInsets();
568 
569 	  int height = 0;
570 	  int width = 0;
571 	  for (int i = 0; i < components.length; i++)
572 	    {
573 	      if (components[i] == null)
574 		continue;
575 	      Dimension dims = components[i].getMinimumSize();
576 	      if (dims != null)
577 	        {
578 		  height += dims.height;
579 		  width = Math.max(width, dims.width);
580 	        }
581 	    }
582 	  return new Dimension(width, height);
583         }
584       return null;
585     }
586 
587     /**
588      * This method returns the preferred layout size. The preferred height is
589      * the sum of all the components'  preferred heights. The preferred width
590      * is the maximum of  all the components' preferred widths.
591      *
592      * @param container The container to measure.
593      *
594      * @return The preferred size.
595      */
preferredLayoutSize(Container container)596     public Dimension preferredLayoutSize(Container container)
597     {
598       if (container instanceof JSplitPane)
599         {
600 	  JSplitPane split = (JSplitPane) container;
601 	  Insets insets = container.getInsets();
602 
603 	  int height = 0;
604 	  int width = 0;
605 	  for (int i = 0; i < components.length; i++)
606 	    {
607 	      if (components[i] == null)
608 		continue;
609 	      Dimension dims = components[i].getPreferredSize();
610 	      if (dims != null)
611 	        {
612 		  height += dims.height;
613 		  width = Math.max(width, dims.width);
614 	        }
615 	    }
616 	  return new Dimension(width, height);
617         }
618       return null;
619     }
620 
621     /**
622      * This method sets the bounds of the given component. The y coordinate is
623      * the location given. The x coordinate is the left inset. The height is
624      * the size given. The width is the container size minus the left and
625      * right inset.
626      *
627      * @param c The component to set bounds for.
628      * @param size The height.
629      * @param location The y coordinate.
630      * @param insets The insets to use.
631      * @param containerSize The container's size.
632      */
setComponentToSize(Component c, int size, int location, Insets insets, Dimension containerSize)633     protected void setComponentToSize(Component c, int size, int location,
634                                       Insets insets, Dimension containerSize)
635     {
636       int y = location;
637       int x = insets.left;
638       int h = size;
639       int w = containerSize.width - insets.left - insets.right;
640       c.setBounds(x, y, w, h);
641     }
642 
643     /**
644      * This method returns the minimum height of the component at the given
645      * index.
646      *
647      * @param index The index of the component to check.
648      *
649      * @return The minimum height of the given component.
650      */
minimumSizeOfComponent(int index)651     int minimumSizeOfComponent(int index)
652     {
653       Dimension dims = components[index].getMinimumSize();
654       if (dims != null)
655 	return dims.height;
656       else
657 	return 0;
658     }
659   }
660 
661   /**
662    * This class handles FocusEvents from the JComponent.
663    *
664    * @specnote Apparently this class was intended to be protected,
665    *           but was made public by a compiler bug and is now
666    *           public for compatibility.
667    */
668   public class FocusHandler extends FocusAdapter
669   {
670     /**
671      * This method is called when the JSplitPane gains focus.
672      *
673      * @param ev The FocusEvent.
674      */
focusGained(FocusEvent ev)675     public void focusGained(FocusEvent ev)
676     {
677       // FIXME: implement.
678     }
679 
680     /**
681      * This method is called when the JSplitPane loses focus.
682      *
683      * @param ev The FocusEvent.
684      */
focusLost(FocusEvent ev)685     public void focusLost(FocusEvent ev)
686     {
687       // FIXME: implement.
688     }
689   }
690 
691   /**
692    * This is a deprecated class. It is supposed to be used for handling down
693    * and right key presses.
694    *
695    * @specnote Apparently this class was intended to be protected,
696    *           but was made public by a compiler bug and is now
697    *           public for compatibility.
698    */
699   public class KeyboardDownRightHandler implements ActionListener
700   {
701     /**
702      * This method is called when the down or right keys are pressed.
703      *
704      * @param ev The ActionEvent
705      */
actionPerformed(ActionEvent ev)706     public void actionPerformed(ActionEvent ev)
707     {
708       // FIXME: implement.
709     }
710   }
711 
712   /**
713    * This is a deprecated class. It is supposed to be used for handling end
714    * key presses.
715    *
716    * @specnote Apparently this class was intended to be protected,
717    *           but was made public by a compiler bug and is now
718    *           public for compatibility.
719    */
720   public class KeyboardEndHandler implements ActionListener
721   {
722     /**
723      * This method is called when the end key is pressed.
724      *
725      * @param ev The ActionEvent.
726      */
actionPerformed(ActionEvent ev)727     public void actionPerformed(ActionEvent ev)
728     {
729       // FIXME: implement.
730     }
731   }
732 
733   /**
734    * This is a deprecated class. It is supposed to be used for handling home
735    * key presses.
736    *
737    * @specnote Apparently this class was intended to be protected,
738    *           but was made public by a compiler bug and is now
739    *           public for compatibility.
740    */
741   public class KeyboardHomeHandler implements ActionListener
742   {
743     /**
744      * This method is called when the home key is pressed.
745      *
746      * @param ev The ActionEvent.
747      */
actionPerformed(ActionEvent ev)748     public void actionPerformed(ActionEvent ev)
749     {
750       // FIXME: implement.
751     }
752   }
753 
754   /**
755    * This is a deprecated class. It is supposed to be used for handling resize
756    * toggles.
757    *
758    * @specnote Apparently this class was intended to be protected,
759    *           but was made public by a compiler bug and is now
760    *           public for compatibility.
761    */
762   public class KeyboardResizeToggleHandler implements ActionListener
763   {
764     /**
765      * This method is called when a resize is toggled.
766      *
767      * @param ev The ActionEvent.
768      */
actionPerformed(ActionEvent ev)769     public void actionPerformed(ActionEvent ev)
770     {
771       // FIXME: implement.
772     }
773   }
774 
775   /**
776    * This is a deprecated class. It is supposed to be used for handler up and
777    * left key presses.
778    *
779    * @specnote Apparently this class was intended to be protected,
780    *           but was made public by a compiler bug and is now
781    *           public for compatibility.
782    */
783   public class KeyboardUpLeftHandler implements ActionListener
784   {
785     /**
786      * This method is called when the left or up keys are pressed.
787      *
788      * @param ev The ActionEvent.
789      */
actionPerformed(ActionEvent ev)790     public void actionPerformed(ActionEvent ev)
791     {
792       // FIXME: implement.
793     }
794   }
795 
796   /**
797    * This helper class handles PropertyChangeEvents from the JSplitPane. When
798    * a property changes, this will update the UI accordingly.
799    *
800    * @specnote Apparently this class was intended to be protected,
801    *           but was made public by a compiler bug and is now
802    *           public for compatibility.
803    */
804   public class PropertyHandler implements PropertyChangeListener
805   {
806     /**
807      * This method is called whenever one of the JSplitPane's properties
808      * change.
809      *
810      * @param e DOCUMENT ME!
811      */
propertyChange(PropertyChangeEvent e)812     public void propertyChange(PropertyChangeEvent e)
813     {
814       if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY))
815         {
816 	  int newSize = splitPane.getDividerSize();
817 	  int[] tmpSizes = layoutManager.getSizes();
818 	  dividerSize = tmpSizes[2];
819       int newSpace = newSize - tmpSizes[2];
820 	  tmpSizes[2] = newSize;
821 
822 	  tmpSizes[0] += newSpace / 2;
823 	  tmpSizes[1] += newSpace / 2;
824 
825 	  layoutManager.setSizes(tmpSizes);
826         }
827       else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY))
828         {
829 	  int max = layoutManager.getAvailableSize(splitPane.getSize(),
830 	                                           splitPane.getInsets());
831 	  int dividerLoc = getDividerLocation(splitPane);
832 	  double prop = ((double) dividerLoc) / max;
833 
834 	  resetLayoutManager();
835 	  if (prop <= 1 && prop >= 0)
836 	    splitPane.setDividerLocation(prop);
837         }
838       layoutManager.layoutContainer(splitPane);
839       splitPane.repaint();
840       // Don't have to deal with continuous_layout - only
841       // necessary in dragging modes (and it's checked
842       // every time you drag there)
843       // Don't have to deal with resize_weight (as there
844       // will be no extra space associated with this
845       // event - the changes to the weighting will
846       // be taken into account the next time the
847       // sizes change.)
848       // Don't have to deal with divider_location
849       // The method in JSplitPane calls our setDividerLocation
850       // so we'll know about those anyway.
851       // Don't have to deal with last_divider_location
852       // Although I'm not sure why, it doesn't seem to
853       // have any effect on Sun's JSplitPane.
854       // one_touch_expandable changes are dealt with
855       // by our divider.
856     }
857   }
858 
859   /** The location of the divider when dragging began. */
860   protected int beginDragDividerLocation;
861 
862   /** The size of the divider while dragging. */
863   protected int dividerSize;
864 
865   /** The location where the last drag location ended. */
866   transient int lastDragLocation = -1;
867 
868   /** The distance the divider is moved when moved by keyboard actions. */
869   // Sun defines this as 3
870   protected static int KEYBOARD_DIVIDER_MOVE_OFFSET = 3;
871 
872   /** The divider that divides this JSplitPane. */
873   protected BasicSplitPaneDivider divider;
874 
875   /** The listener that listens for PropertyChangeEvents from the JSplitPane. */
876   protected PropertyChangeListener propertyChangeListener;
877 
878   /** The JSplitPane's focus handler. */
879   protected FocusListener focusListener;
880 
881   /** @deprecated The handler for down and right key presses. */
882   protected ActionListener keyboardDownRightListener;
883 
884   /** @deprecated The handler for end key presses. */
885   protected ActionListener keyboardEndListener;
886 
887   /** @deprecated The handler for home key presses. */
888   protected ActionListener keyboardHomeListener;
889 
890   /** @deprecated The handler for toggling resizes. */
891   protected ActionListener keyboardResizeToggleListener;
892 
893   /** @deprecated The handler for up and left key presses. */
894   protected ActionListener keyboardUpLeftListener;
895 
896   /** The JSplitPane's current layout manager. */
897   protected BasicHorizontalLayoutManager layoutManager;
898 
899   /** @deprecated The divider resize toggle key. */
900   protected KeyStroke dividerResizeToggleKey;
901 
902   /** @deprecated The down key. */
903   protected KeyStroke downKey;
904 
905   /** @deprecated The end key. */
906   protected KeyStroke endKey;
907 
908   /** @deprecated The home key. */
909   protected KeyStroke homeKey;
910 
911   /** @deprecated The left key. */
912   protected KeyStroke leftKey;
913 
914   /** @deprecated The right key. */
915   protected KeyStroke rightKey;
916 
917   /** @deprecated The up key. */
918   protected KeyStroke upKey;
919 
920   /** Set to true when dragging heavy weight components. */
921   protected boolean draggingHW;
922 
923   /**
924    * The constraints object used when adding the non-continuous divider to the
925    * JSplitPane.
926    */
927   protected static final String NON_CONTINUOUS_DIVIDER
928     = "nonContinuousDivider";
929 
930   /** The dark divider used when dragging in non-continuous layout mode. */
931   protected Component nonContinuousLayoutDivider;
932 
933   /** The JSplitPane that this UI draws. */
934   protected JSplitPane splitPane;
935 
936   /**
937    * Creates a new BasicSplitPaneUI object.
938    */
BasicSplitPaneUI()939   public BasicSplitPaneUI()
940   {
941     // Nothing to do here.
942   }
943 
944   /**
945    * This method creates a new BasicSplitPaneUI for the given JComponent.
946    *
947    * @param x The JComponent to create a UI for.
948    *
949    * @return A new BasicSplitPaneUI.
950    */
createUI(JComponent x)951   public static ComponentUI createUI(JComponent x)
952   {
953     return new BasicSplitPaneUI();
954   }
955 
956   /**
957    * This method installs the BasicSplitPaneUI for the given JComponent.
958    *
959    * @param c The JComponent to install the UI for.
960    */
installUI(JComponent c)961   public void installUI(JComponent c)
962   {
963     if (c instanceof JSplitPane)
964       {
965 	splitPane = (JSplitPane) c;
966 	installDefaults();
967 	installListeners();
968 	installKeyboardActions();
969       }
970   }
971 
972   /**
973    * This method uninstalls the BasicSplitPaneUI for the given JComponent.
974    *
975    * @param c The JComponent to uninstall the UI for.
976    */
uninstallUI(JComponent c)977   public void uninstallUI(JComponent c)
978   {
979     uninstallKeyboardActions();
980     uninstallListeners();
981     uninstallDefaults();
982 
983     splitPane = null;
984   }
985 
986   /**
987    * This method installs the defaults given by the Look and Feel.
988    */
installDefaults()989   protected void installDefaults()
990   {
991     LookAndFeel.installColors(splitPane, "SplitPane.background",
992                               "SplitPane.foreground");
993     LookAndFeel.installBorder(splitPane, "SplitPane.border");
994     divider = createDefaultDivider();
995     resetLayoutManager();
996     nonContinuousLayoutDivider = createDefaultNonContinuousLayoutDivider();
997     splitPane.add(divider, JSplitPane.DIVIDER);
998 
999     // There is no need to add the nonContinuousLayoutDivider
1000     splitPane.setDividerSize(UIManager.getInt("SplitPane.dividerSize"));
1001     splitPane.setOpaque(true);
1002   }
1003 
1004   /**
1005    * This method uninstalls the defaults and nulls any objects created during
1006    * install.
1007    */
uninstallDefaults()1008   protected void uninstallDefaults()
1009   {
1010     layoutManager = null;
1011     splitPane.remove(divider);
1012     divider = null;
1013     nonContinuousLayoutDivider = null;
1014 
1015     splitPane.setBackground(null);
1016     splitPane.setBorder(null);
1017   }
1018 
1019   /**
1020    * This method installs the listeners needed for this UI to function.
1021    */
installListeners()1022   protected void installListeners()
1023   {
1024     propertyChangeListener = createPropertyChangeListener();
1025     focusListener = createFocusListener();
1026 
1027     splitPane.addPropertyChangeListener(propertyChangeListener);
1028     splitPane.addFocusListener(focusListener);
1029   }
1030 
1031   /**
1032    * This method uninstalls all listeners registered for the UI.
1033    */
uninstallListeners()1034   protected void uninstallListeners()
1035   {
1036     splitPane.removePropertyChangeListener(propertyChangeListener);
1037     splitPane.removeFocusListener(focusListener);
1038 
1039     focusListener = null;
1040     propertyChangeListener = null;
1041   }
1042 
1043   /**
1044    * This method installs the keyboard actions for the JSplitPane.
1045    */
installKeyboardActions()1046   protected void installKeyboardActions()
1047   {
1048     // FIXME: implement.
1049   }
1050 
1051   /**
1052    * This method reverses the work done in installKeyboardActions.
1053    */
uninstallKeyboardActions()1054   protected void uninstallKeyboardActions()
1055   {
1056     // FIXME: implement.
1057   }
1058 
1059   /**
1060    * This method creates a new PropertyChangeListener.
1061    *
1062    * @return A new PropertyChangeListener.
1063    */
createPropertyChangeListener()1064   protected PropertyChangeListener createPropertyChangeListener()
1065   {
1066     return new PropertyHandler();
1067   }
1068 
1069   /**
1070    * This method creates a new FocusListener.
1071    *
1072    * @return A new FocusListener.
1073    */
createFocusListener()1074   protected FocusListener createFocusListener()
1075   {
1076     return new FocusHandler();
1077   }
1078 
1079   /**
1080    * This method creates a new ActionListener for up and left key presses.
1081    *
1082    * @return A new ActionListener for up and left keys.
1083    *
1084    * @deprecated 1.3
1085    */
createKeyboardUpLeftListener()1086   protected ActionListener createKeyboardUpLeftListener()
1087   {
1088     return new KeyboardUpLeftHandler();
1089   }
1090 
1091   /**
1092    * This method creates a new ActionListener for down and right key presses.
1093    *
1094    * @return A new ActionListener for down and right keys.
1095    *
1096    * @deprecated 1.3
1097    */
createKeyboardDownRightListener()1098   protected ActionListener createKeyboardDownRightListener()
1099   {
1100     return new KeyboardDownRightHandler();
1101   }
1102 
1103   /**
1104    * This method creates a new ActionListener for home key presses.
1105    *
1106    * @return A new ActionListener for home keys.
1107    *
1108    * @deprecated
1109    */
createKeyboardHomeListener()1110   protected ActionListener createKeyboardHomeListener()
1111   {
1112     return new KeyboardHomeHandler();
1113   }
1114 
1115   /**
1116    * This method creates a new ActionListener for end key presses.i
1117    *
1118    * @return A new ActionListener for end keys.
1119    *
1120    * @deprecated 1.3
1121    */
createKeyboardEndListener()1122   protected ActionListener createKeyboardEndListener()
1123   {
1124     return new KeyboardEndHandler();
1125   }
1126 
1127   /**
1128    * This method creates a new ActionListener for resize toggle key events.
1129    *
1130    * @return A new ActionListener for resize toggle keys.
1131    *
1132    * @deprecated 1.3
1133    */
createKeyboardResizeToggleListener()1134   protected ActionListener createKeyboardResizeToggleListener()
1135   {
1136     return new KeyboardResizeToggleHandler();
1137   }
1138 
1139   /**
1140    * This method returns the orientation of the JSplitPane.
1141    *
1142    * @return The orientation of the JSplitPane.
1143    */
getOrientation()1144   public int getOrientation()
1145   {
1146     return splitPane.getOrientation();
1147   }
1148 
1149   /**
1150    * This method sets the orientation of the JSplitPane.
1151    *
1152    * @param orientation The new orientation of the JSplitPane.
1153    */
setOrientation(int orientation)1154   public void setOrientation(int orientation)
1155   {
1156     splitPane.setOrientation(orientation);
1157   }
1158 
1159   /**
1160    * This method returns true if the JSplitPane is using continuous layout.
1161    *
1162    * @return True if the JSplitPane is using continuous layout.
1163    */
isContinuousLayout()1164   public boolean isContinuousLayout()
1165   {
1166     return splitPane.isContinuousLayout();
1167   }
1168 
1169   /**
1170    * This method sets the continuous layout property of the JSplitPane.
1171    *
1172    * @param b True if the JsplitPane is to use continuous layout.
1173    */
setContinuousLayout(boolean b)1174   public void setContinuousLayout(boolean b)
1175   {
1176     splitPane.setContinuousLayout(b);
1177   }
1178 
1179   /**
1180    * This method returns the last location the divider was dragged to.
1181    *
1182    * @return The last location the divider was dragged to.
1183    */
getLastDragLocation()1184   public int getLastDragLocation()
1185   {
1186     return lastDragLocation;
1187   }
1188 
1189   /**
1190    * This method sets the last location the divider was dragged to.
1191    *
1192    * @param l The last location the divider was dragged to.
1193    */
setLastDragLocation(int l)1194   public void setLastDragLocation(int l)
1195   {
1196     lastDragLocation = l;
1197   }
1198 
1199   /**
1200    * This method returns the BasicSplitPaneDivider that divides this
1201    * JSplitPane.
1202    *
1203    * @return The divider for the JSplitPane.
1204    */
getDivider()1205   public BasicSplitPaneDivider getDivider()
1206   {
1207     return divider;
1208   }
1209 
1210   /**
1211    * This method creates a nonContinuousLayoutDivider for use with the
1212    * JSplitPane in nonContinousLayout mode. The default divider is a gray
1213    * Canvas.
1214    *
1215    * @return The default nonContinousLayoutDivider.
1216    */
createDefaultNonContinuousLayoutDivider()1217   protected Component createDefaultNonContinuousLayoutDivider()
1218   {
1219     if (nonContinuousLayoutDivider == null)
1220       {
1221 	nonContinuousLayoutDivider = new Canvas();
1222 	nonContinuousLayoutDivider.setBackground(Color.DARK_GRAY);
1223       }
1224     return nonContinuousLayoutDivider;
1225   }
1226 
1227   /**
1228    * This method sets the component to use as the nonContinuousLayoutDivider.
1229    *
1230    * @param newDivider The component to use as the nonContinuousLayoutDivider.
1231    */
setNonContinuousLayoutDivider(Component newDivider)1232   protected void setNonContinuousLayoutDivider(Component newDivider)
1233   {
1234     setNonContinuousLayoutDivider(newDivider, true);
1235   }
1236 
1237   /**
1238    * This method sets the component to use as the nonContinuousLayoutDivider.
1239    *
1240    * @param newDivider The component to use as the nonContinuousLayoutDivider.
1241    * @param rememberSizes FIXME: document.
1242    */
setNonContinuousLayoutDivider(Component newDivider, boolean rememberSizes)1243   protected void setNonContinuousLayoutDivider(Component newDivider,
1244                                                boolean rememberSizes)
1245   {
1246     // FIXME: use rememberSizes for something
1247     nonContinuousLayoutDivider = newDivider;
1248   }
1249 
1250   /**
1251    * This method returns the nonContinuousLayoutDivider.
1252    *
1253    * @return The nonContinuousLayoutDivider.
1254    */
getNonContinuousLayoutDivider()1255   public Component getNonContinuousLayoutDivider()
1256   {
1257     return nonContinuousLayoutDivider;
1258   }
1259 
1260   /**
1261    * This method returns the JSplitPane that this BasicSplitPaneUI draws.
1262    *
1263    * @return The JSplitPane.
1264    */
getSplitPane()1265   public JSplitPane getSplitPane()
1266   {
1267     return splitPane;
1268   }
1269 
1270   /**
1271    * This method creates the divider used normally with the JSplitPane.
1272    *
1273    * @return The default divider.
1274    */
createDefaultDivider()1275   public BasicSplitPaneDivider createDefaultDivider()
1276   {
1277     if (divider == null)
1278       divider = new BasicSplitPaneDivider(this);
1279     return divider;
1280   }
1281 
1282   /**
1283    * This method is called when JSplitPane's resetToPreferredSizes is called.
1284    * It resets the sizes of all components in the JSplitPane.
1285    *
1286    * @param jc The JSplitPane to reset.
1287    */
resetToPreferredSizes(JSplitPane jc)1288   public void resetToPreferredSizes(JSplitPane jc)
1289   {
1290     layoutManager.resetToPreferredSizes();
1291   }
1292 
1293   /**
1294    * This method sets the location of the divider.
1295    *
1296    * @param jc The JSplitPane to set the divider location in.
1297    * @param location The new location of the divider.
1298    */
setDividerLocation(JSplitPane jc, int location)1299   public void setDividerLocation(JSplitPane jc, int location)
1300   {
1301     location = validLocation(location);
1302     Container p = jc.getParent();
1303     Dimension rightPrefSize = jc.getRightComponent().getPreferredSize();
1304     Dimension size = jc.getSize();
1305     // check if the size has been set for the splitpane
1306     if (size.width == 0 && size.height == 0)
1307       size = jc.getPreferredSize();
1308 
1309     if (getOrientation() == 0 && location > size.height)
1310       {
1311         location = size.height;
1312         while (p != null)
1313           {
1314             p.setSize(p.getWidth(), p.getHeight() + rightPrefSize.height);
1315             p = p.getParent();
1316           }
1317       }
1318     else if (location > size.width)
1319       {
1320         location = size.width;
1321         while (p != null)
1322           {
1323             p.setSize(p.getWidth() + rightPrefSize.width, p.getHeight());
1324             p = p.getParent();
1325           }
1326       }
1327 
1328     setLastDragLocation(getDividerLocation(splitPane));
1329     splitPane.setLastDividerLocation(getDividerLocation(splitPane));
1330     int[] tmpSizes = layoutManager.getSizes();
1331     tmpSizes[0] = location
1332                   - layoutManager.getInitialLocation(splitPane.getInsets());
1333     tmpSizes[1] = layoutManager.getAvailableSize(splitPane.getSize(),
1334                                                  splitPane.getInsets())
1335                   - tmpSizes[0];
1336     layoutManager.setSizes(tmpSizes);
1337     splitPane.revalidate();
1338     splitPane.repaint();
1339   }
1340 
1341   /**
1342    * This method returns the location of the divider.
1343    *
1344    * @param jc The JSplitPane to retrieve the location for.
1345    *
1346    * @return The location of the divider.
1347    */
getDividerLocation(JSplitPane jc)1348   public int getDividerLocation(JSplitPane jc)
1349   {
1350     return layoutManager.sizes[0]
1351            + layoutManager.getInitialLocation(splitPane.getInsets());
1352   }
1353 
1354   /**
1355    * This method returns the smallest value possible for the location of the
1356    * divider.
1357    *
1358    * @param jc The JSplitPane.
1359    *
1360    * @return The minimum divider location.
1361    */
getMinimumDividerLocation(JSplitPane jc)1362   public int getMinimumDividerLocation(JSplitPane jc)
1363   {
1364     int value = layoutManager.getInitialLocation(jc.getInsets())
1365                 - layoutManager.getAvailableSize(jc.getSize(), jc.getInsets())
1366                 + splitPane.getDividerSize();
1367     if (layoutManager.components[1] != null)
1368       value += layoutManager.minimumSizeOfComponent(1);
1369     return value;
1370   }
1371 
1372   /**
1373    * This method returns the largest value possible for the location of the
1374    * divider.
1375    *
1376    * @param jc The JSplitPane.
1377    *
1378    * @return The maximum divider location.
1379    */
getMaximumDividerLocation(JSplitPane jc)1380   public int getMaximumDividerLocation(JSplitPane jc)
1381   {
1382     int value = layoutManager.getInitialLocation(jc.getInsets())
1383                 + layoutManager.getAvailableSize(jc.getSize(), jc.getInsets())
1384                 - splitPane.getDividerSize();
1385     if (layoutManager.components[1] != null)
1386       value -= layoutManager.minimumSizeOfComponent(1);
1387     return value;
1388   }
1389 
1390   /**
1391    * This method is called after the children of the JSplitPane are painted.
1392    *
1393    * @param jc The JSplitPane.
1394    * @param g The Graphics object to paint with.
1395    */
finishedPaintingChildren(JSplitPane jc, Graphics g)1396   public void finishedPaintingChildren(JSplitPane jc, Graphics g)
1397   {
1398     if (! splitPane.isContinuousLayout() && nonContinuousLayoutDivider != null
1399         && nonContinuousLayoutDivider.isVisible())
1400       javax.swing.SwingUtilities.paintComponent(g, nonContinuousLayoutDivider,
1401                                                 null,
1402                                                 nonContinuousLayoutDivider
1403                                                 .getBounds());
1404   }
1405 
1406   /**
1407    * This method is called to paint the JSplitPane.
1408    *
1409    * @param g The Graphics object to paint with.
1410    * @param jc The JSplitPane to paint.
1411    */
paint(Graphics g, JComponent jc)1412   public void paint(Graphics g, JComponent jc)
1413   {
1414     // TODO: What should be done here?
1415   }
1416 
1417   /**
1418    * This method returns the preferred size of the JSplitPane.
1419    *
1420    * @param jc The JSplitPane.
1421    *
1422    * @return The preferred size of the JSplitPane.
1423    */
getPreferredSize(JComponent jc)1424   public Dimension getPreferredSize(JComponent jc)
1425   {
1426     return layoutManager.preferredLayoutSize((Container) jc);
1427   }
1428 
1429   /**
1430    * This method returns the minimum size of the JSplitPane.
1431    *
1432    * @param jc The JSplitPane.
1433    *
1434    * @return The minimum size of the JSplitPane.
1435    */
getMinimumSize(JComponent jc)1436   public Dimension getMinimumSize(JComponent jc)
1437   {
1438     return layoutManager.minimumLayoutSize((Container) jc);
1439   }
1440 
1441   /**
1442    * This method returns the maximum size of the JSplitPane.
1443    *
1444    * @param jc The JSplitPane.
1445    *
1446    * @return The maximum size of the JSplitPane.
1447    */
getMaximumSize(JComponent jc)1448   public Dimension getMaximumSize(JComponent jc)
1449   {
1450     return layoutManager.maximumLayoutSize((Container) jc);
1451   }
1452 
1453   /**
1454    * This method returns the border insets of the current border.
1455    *
1456    * @param jc The JSplitPane.
1457    *
1458    * @return The current border insets.
1459    */
getInsets(JComponent jc)1460   public Insets getInsets(JComponent jc)
1461   {
1462     return splitPane.getBorder().getBorderInsets(splitPane);
1463   }
1464 
1465   /**
1466    * This method resets the current layout manager. The type of layout manager
1467    * is dependent on the current orientation.
1468    */
resetLayoutManager()1469   protected void resetLayoutManager()
1470   {
1471     if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
1472       layoutManager = new BasicHorizontalLayoutManager();
1473     else
1474       layoutManager = new BasicVerticalLayoutManager();
1475     getSplitPane().setLayout(layoutManager);
1476     layoutManager.updateComponents();
1477 
1478     // invalidating by itself does not invalidate the layout.
1479     getSplitPane().revalidate();
1480   }
1481 
1482   /**
1483    * This method is called when dragging starts. It resets lastDragLocation
1484    * and dividerSize.
1485    */
startDragging()1486   protected void startDragging()
1487   {
1488     dividerSize = divider.getDividerSize();
1489     setLastDragLocation(-1);
1490 
1491     if (! splitPane.getLeftComponent().isLightweight()
1492         || ! splitPane.getRightComponent().isLightweight())
1493       draggingHW = true;
1494 
1495     if (splitPane.isContinuousLayout())
1496       nonContinuousLayoutDivider.setVisible(false);
1497     else
1498       {
1499 	nonContinuousLayoutDivider.setVisible(true);
1500 	nonContinuousLayoutDivider.setBounds(divider.getBounds());
1501       }
1502     splitPane.revalidate();
1503     splitPane.repaint();
1504   }
1505 
1506   /**
1507    * This method is called whenever the divider is dragged. If the JSplitPane
1508    * is in continuousLayout mode, the divider needs to be moved and the
1509    * JSplitPane needs to be laid out.
1510    *
1511    * @param location The new location of the divider.
1512    */
dragDividerTo(int location)1513   protected void dragDividerTo(int location)
1514   {
1515     location = validLocation(location);
1516     if (beginDragDividerLocation == -1)
1517       beginDragDividerLocation = location;
1518 
1519     if (splitPane.isContinuousLayout())
1520       splitPane.setDividerLocation(location);
1521     else
1522       {
1523 	Point p = nonContinuousLayoutDivider.getLocation();
1524 	if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
1525 	  p.x = location;
1526 	else
1527 	  p.y = location;
1528 	nonContinuousLayoutDivider.setLocation(p);
1529       }
1530     setLastDragLocation(location);
1531     splitPane.repaint();
1532   }
1533 
1534   /**
1535    * This method is called when the dragging is finished.
1536    *
1537    * @param location The location where the drag finished.
1538    */
finishDraggingTo(int location)1539   protected void finishDraggingTo(int location)
1540   {
1541     if (nonContinuousLayoutDivider != null)
1542       nonContinuousLayoutDivider.setVisible(false);
1543     draggingHW = false;
1544     location = validLocation(location);
1545     dragDividerTo(location);
1546     splitPane.setDividerLocation(location);
1547     splitPane.setLastDividerLocation(beginDragDividerLocation);
1548     beginDragDividerLocation = -1;
1549     splitPane.repaint();
1550   }
1551 
1552   /**
1553    * This method returns the width of one of the sides of the divider's border.
1554    *
1555    * @return The width of one side of the divider's border.
1556    *
1557    * @deprecated 1.3
1558    */
getDividerBorderSize()1559   protected int getDividerBorderSize()
1560   {
1561     if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
1562       return divider.getBorder().getBorderInsets(divider).left;
1563     else
1564       return divider.getBorder().getBorderInsets(divider).top;
1565   }
1566 
1567   /**
1568    * This is a helper method that returns a valid location for the divider
1569    * when dragging.
1570    *
1571    * @param location The location to check.
1572    *
1573    * @return A valid location.
1574    */
validLocation(int location)1575   private int validLocation(int location)
1576   {
1577     int min = getMinimumDividerLocation(splitPane);
1578     int max = getMaximumDividerLocation(splitPane);
1579     if (min > 0 && location < min)
1580       return min;
1581     if (max > 0 && location > max)
1582       return max;
1583     return location;
1584   }
1585 }
1586