View Javadoc

1   /***
2    * JQuadSplitPane.java
3    *
4    * This file is part of the creme library.
5    * The creme library intends to ease the development effort of large
6    * applications by providing easy to use support classes.
7    *
8    * Copyright (C) 2003 Denis Bregeon
9    *
10   * This library is free software; you can redistribute it and/or
11   * modify it under the terms of the GNU Lesser General Public
12   * License as published by the Free Software Foundation; either
13   * version 2.1 of the License, or (at your option) any later version.
14   *
15   * This library is distributed in the hope that it will be useful,
16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18   * Lesser General Public License for more details.
19   *
20   * You should have received a copy of the GNU Lesser General Public
21   * License along with this library; if not, write to the Free Software
22   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23   *
24   * contact information: dbregeon@sourceforge.net
25   */
26  package org.jcreme.swing;
27  
28  import java.awt.Component;
29  import java.awt.Cursor;
30  import java.awt.Dimension;
31  import java.awt.Insets;
32  import java.awt.Point;
33  import java.awt.event.ActionEvent;
34  import java.awt.event.ActionListener;
35  import java.awt.event.AdjustmentEvent;
36  import java.awt.event.AdjustmentListener;
37  import java.awt.event.ContainerEvent;
38  import java.awt.event.ContainerListener;
39  import java.awt.event.MouseAdapter;
40  import java.awt.event.MouseEvent;
41  import java.awt.event.MouseListener;
42  import java.awt.event.MouseMotionAdapter;
43  import java.beans.PropertyChangeEvent;
44  import java.beans.PropertyChangeListener;
45  import java.security.InvalidParameterException;
46  
47  import javax.swing.JButton;
48  import javax.swing.JScrollBar;
49  import javax.swing.JScrollPane;
50  import javax.swing.JSplitPane;
51  import javax.swing.JViewport;
52  import javax.swing.ScrollPaneConstants;
53  import javax.swing.plaf.basic.BasicSplitPaneDivider;
54  import javax.swing.plaf.basic.BasicSplitPaneUI;
55  
56  /***
57   * @author $Author: dbregeon $
58   * @version $Revision: 1.1 $
59   */
60  public class JQuadSplitPane extends JSplitPane {
61  
62      /***
63       * Constant used to describe horizontal and vertical modes.
64       */
65      public static final int DIVIDERS = 0;
66  
67      /***
68       * Constant used to describe horizontal and vertical modes.
69       */
70      public static final int BUTTONS = 1;
71  
72      /***
73       * The horizontal mode : either QuadSplitPane.DIVIDER or
74       * QuadSplitPane.BUTTON.
75       */
76      private int horizontalMode = DIVIDERS;
77  
78      /***
79       * The vertical mode : either QuadSplitPane.DIVIDER or QuadSplitPane.BUTTON.
80       */
81      private int verticalMode = DIVIDERS;
82  
83      /***
84       * The left sub-splitpane's split divider.
85       */
86      private BasicSplitPaneDivider leftDivider = null;
87  
88      /***
89       * The right sub-splitpane's split divider.
90       */
91      private BasicSplitPaneDivider rightDivider = null;
92  
93      /***
94       * The main splitpane split divider.
95       */
96      private BasicSplitPaneDivider mainDivider = null;
97  
98      /***
99       * This button replaces the horizontal splitpane divider when it is
100      * minimized.
101      */
102     private JButton horizontalSplitButton = null;
103 
104     /***
105      * This button replaces the vertical splitpane divider when it is minimized.
106      */
107     private JButton verticalSplitButton = null;
108 
109     /***
110      * The splitpane used as the left component of the main splitpane.
111      */
112     private JSplitPane leftSubSplitPane = null;
113 
114     /***
115      * The splitpane used as the right component of the main splitpane.
116      */
117     private JSplitPane rightSubSplitPane = null;
118 
119     /***
120      * The top left scrollpane.
121      */
122     private JScrollPane topLeftScrollPane = null;
123 
124     /***
125      * The top right scrollpane.
126      */
127     private JScrollPane topRightScrollPane = null;
128 
129     /***
130      * The bottom left scrollpane.
131      */
132     private JScrollPane bottomLeftScrollPane = null;
133 
134     /***
135      * The bottom left scrollpane.
136      */
137     private JScrollPane bottomRightScrollPane = null;
138 
139     /***
140      * The displayed component.
141      */
142     private Component viewedComponent = null;
143 
144     /***
145      * Boolean showing whether a continuous-layout dragging is in progress.
146      */
147     private boolean isContinuousDragging = false;
148 
149     /***
150      *  
151      */
152     private final RepaintingMouseHandler columnHeaderRepainter = new RepaintingMouseHandler();
153 
154     /***
155      *  
156      */
157     private final RepaintingMouseHandler rowHeaderRepainter = new RepaintingMouseHandler();
158 
159     /***
160      *  
161      */
162     private final RepaintingMouseHandler viewRepainter = new RepaintingMouseHandler();
163 
164     /***
165      * This listener enables to keep track of the columnHeader and the rowHeader
166      * to add them into the relevant scrollpanes.
167      */
168     private final PropertyChangeListener topLeftScrollPaneListener = new PropertyChangeListener() {
169 
170         public void propertyChange(PropertyChangeEvent evt) {
171             if ("columnHeader".equals(evt.getPropertyName())) {
172                 if (evt.getOldValue() != null) {
173                     ((JViewport) evt.getOldValue())
174                             .removeContainerListener(getColumnHeaderListener());
175                 }
176                 if ((evt.getNewValue() != null)
177                         && (((JViewport) evt.getNewValue()).getView() != null)) {
178                     setTopRightHeader(((JViewport) evt.getNewValue()).getView());
179                 } else {
180                     getTopRightScrollPane().setColumnHeaderView(null);
181                 }
182                 if (evt.getNewValue() != null) {
183                     ((JViewport) evt.getNewValue())
184                             .addContainerListener(getColumnHeaderListener());
185                 }
186             }
187             if ("rowHeader".equals(evt.getPropertyName())) {
188                 if (evt.getOldValue() != null) {
189                     ((JViewport) evt.getOldValue())
190                             .removeContainerListener(getRowHeaderListener());
191                 }
192                 if ((evt.getNewValue() != null)
193                         && (((JViewport) evt.getNewValue()).getView() != null)) {
194                     setBottomLeftHeader(((JViewport) evt.getNewValue())
195                             .getView());
196                 } else {
197                     getBottomLeftScrollPane().setRowHeaderView(null);
198                 }
199                 if (evt.getNewValue() != null) {
200                     ((JViewport) evt.getNewValue())
201                             .addContainerListener(getRowHeaderListener());
202                 }
203             }
204         }
205     };
206 
207     /***
208      * Enables to ensure that the columnHeader is set into the
209      * topLeftScrollPane.
210      */
211     private final ContainerListener columnHeaderListener = new ContainerListener() {
212 
213         public void componentAdded(ContainerEvent e) {
214             e.getChild().addMouseMotionListener(getColumnHeaderRepainter());
215             e.getChild().addMouseListener(getColumnHeaderRepainter());
216             setTopRightHeader(e.getChild());
217         }
218 
219         public void componentRemoved(ContainerEvent e) {
220             e.getChild().removeMouseMotionListener(getColumnHeaderRepainter());
221             e.getChild().removeMouseListener(getColumnHeaderRepainter());
222             getTopRightScrollPane().setColumnHeaderView(null);
223         }
224     };
225 
226     /***
227      * Enables to ensure that the rowHeader is set into the
228      * bottomRightScrollPane.
229      */
230     private final ContainerListener rowHeaderListener = new ContainerListener() {
231 
232         public void componentAdded(ContainerEvent e) {
233             e.getChild().addMouseMotionListener(getRowHeaderRepainter());
234             e.getChild().addMouseListener(getRowHeaderRepainter());
235             setBottomLeftHeader(e.getChild());
236         }
237 
238         public void componentRemoved(ContainerEvent e) {
239             e.getChild().removeMouseMotionListener(getRowHeaderRepainter());
240             e.getChild().removeMouseListener(getRowHeaderRepainter());
241             getBottomLeftScrollPane().setRowHeaderView(null);
242         }
243     };
244 
245     /***
246      * Default constructor for the JQuadSplitPane
247      */
248     public JQuadSplitPane() {
249         super(JSplitPane.HORIZONTAL_SPLIT);
250     }
251 
252     /***
253      * Creates new JQuadSplitPane
254      * 
255      * @param viewedComponent
256      *            The component to be viewed.
257      * @param newContinuousLayout
258      *            Whether the splitpane dividers should use a continuous layout.
259      */
260     public JQuadSplitPane(Component viewedComponent, boolean newContinuousLayout) {
261         super(JSplitPane.HORIZONTAL_SPLIT, newContinuousLayout);
262         setViewedComponent(viewedComponent);
263     }
264 
265     /***
266      * Creates new JQuadSplitPane
267      * 
268      * @param viewedComponent
269      *            The component to be viewed.
270      */
271     public JQuadSplitPane(Component viewedComponent) {
272         super(JSplitPane.HORIZONTAL_SPLIT);
273         setViewedComponent(viewedComponent);
274     }
275 
276     protected final boolean isContinuousDragging() {
277         return this.isContinuousDragging;
278     }
279 
280     protected final void setContinuousDragging(boolean b) {
281         this.isContinuousDragging = b;
282     }
283 
284     /***
285      * 
286      * @param c
287      */
288     protected void setTopRightHeader(Component c) {
289         this.columnHeaderRepainter
290                 .removeRepaintedComponent(this.topRightScrollPane
291                         .getColumnHeader().getView());
292         if (c != null) {
293             ProxyComponent component = new ProxyComponent(c);
294             this.columnHeaderRepainter.addRepaintedComponent(component);
295             this.topRightScrollPane.setColumnHeaderView(component);
296         }
297     }
298 
299     /***
300      * 
301      * @param c
302      */
303     protected void setBottomLeftHeader(Component c) {
304         this.columnHeaderRepainter
305                 .removeRepaintedComponent(this.topRightScrollPane
306                         .getRowHeader().getView());
307         if (c != null) {
308             ProxyComponent component = new ProxyComponent(c);
309             this.rowHeaderRepainter.addRepaintedComponent(component);
310             this.bottomLeftScrollPane.setRowHeaderView(component);
311         }
312     }
313 
314     /***
315      * This method enables to create the UI and to display the given component
316      * in the JQuadSplitPane.
317      * 
318      * @param component
319      *            the component to be displayed.
320      */
321     public void setViewedComponent(Component component) {
322         if (component != null) {
323             buildUI(component, isContinuousLayout());
324             validateTree();
325         }
326     }
327 
328     protected final ContainerListener getColumnHeaderListener() {
329         return this.columnHeaderListener;
330     }
331 
332     protected final ContainerListener getRowHeaderListener() {
333         return this.rowHeaderListener;
334     }
335 
336     protected final BasicSplitPaneDivider getMainDivider() {
337         return this.mainDivider;
338     }
339 
340     protected final BasicSplitPaneDivider getLeftDivider() {
341         return this.leftDivider;
342     }
343 
344     protected final BasicSplitPaneDivider getRightDivider() {
345         return this.rightDivider;
346     }
347 
348     protected final JScrollPane getTopLeftScrollPane() {
349         return this.topLeftScrollPane;
350     }
351 
352     protected final JScrollPane getTopRightScrollPane() {
353         return this.topRightScrollPane;
354     }
355 
356     protected final JScrollPane getBottomLeftScrollPane() {
357         return this.bottomLeftScrollPane;
358     }
359 
360     protected final JScrollPane getBottomRightScrollPane() {
361         return this.bottomRightScrollPane;
362     }
363 
364     protected final JSplitPane getLeftSubSplitPane() {
365         return this.leftSubSplitPane;
366     }
367 
368     protected final JSplitPane getRightSubSplitPane() {
369         return this.rightSubSplitPane;
370     }
371 
372     protected final RepaintingMouseHandler getColumnHeaderRepainter() {
373         return this.columnHeaderRepainter;
374     }
375 
376     protected final RepaintingMouseHandler getRowHeaderRepainter() {
377         return this.rowHeaderRepainter;
378     }
379 
380     /***
381      * Builds the UI of this QuadSPlitPane. It first calls UI-building methods
382      * specific to the kind of component that is used, and then builds the
383      * common part of the UI.
384      * 
385      * @param vComponent
386      *            The component to be viewed.
387      * @param newContinuousLayout
388      *            Whether the splitpane dividers should use a continuous layout.
389      * @throws InvalidParameterException
390      *             if the kind of component given in the constructor is not
391      *             handled.
392      */
393     protected void buildUI(Component vComponent, boolean newContinuousLayout)
394             throws InvalidParameterException {
395         this.viewedComponent = vComponent;
396         if (this.viewedComponent != null) {
397             this.viewedComponent.addMouseMotionListener(this.viewRepainter);
398             this.viewedComponent.addMouseListener(this.viewRepainter);
399             this.topLeftScrollPane = new JScrollPane(this.viewedComponent);
400 
401             ProxyComponent component = new ProxyComponent(this.viewedComponent);
402             this.viewRepainter.addRepaintedComponent(component);
403             this.topRightScrollPane = new JScrollPane(component);
404             this.columnHeaderRepainter
405                     .addRepaintedComponent(this.topRightScrollPane);
406             this.rowHeaderRepainter
407                     .addRepaintedComponent(this.topRightScrollPane);
408             this.topLeftScrollPane
409                     .addPropertyChangeListener(this.topLeftScrollPaneListener);
410             if (this.topLeftScrollPane.getColumnHeader() != null) {
411                 this.topLeftScrollPane.getColumnHeader().addContainerListener(
412                         this.columnHeaderListener);
413                 setTopRightHeader(this.topLeftScrollPane.getColumnHeader()
414                         .getView());
415             }
416 
417             component = new ProxyComponent(this.viewedComponent);
418             this.viewRepainter.addRepaintedComponent(component);
419             this.bottomLeftScrollPane = new JScrollPane(component);
420             this.columnHeaderRepainter
421                     .addRepaintedComponent(this.bottomLeftScrollPane);
422             this.rowHeaderRepainter
423                     .addRepaintedComponent(this.bottomLeftScrollPane);
424             if (this.topLeftScrollPane.getRowHeader() != null) {
425                 this.bottomLeftScrollPane.setRowHeaderView(new ProxyComponent(
426                         this.topLeftScrollPane.getRowHeader().getView()));
427             }
428 
429             component = new ProxyComponent(this.viewedComponent);
430             this.viewRepainter.addRepaintedComponent(component);
431             this.bottomRightScrollPane = new JScrollPane(component);
432             this.columnHeaderRepainter
433                     .addRepaintedComponent(this.bottomRightScrollPane);
434             this.rowHeaderRepainter
435                     .addRepaintedComponent(this.bottomRightScrollPane);
436 
437             this.leftSubSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
438                     newContinuousLayout, this.topLeftScrollPane,
439                     this.bottomLeftScrollPane);
440             this.rightSubSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
441                     newContinuousLayout, this.topRightScrollPane,
442                     this.bottomRightScrollPane);
443             this.horizontalSplitButton = (JButton) createHorizontalSplitButton();
444             this.verticalSplitButton = (JButton) createVerticalSplitButton();
445             this.leftDivider = ((BasicSplitPaneUI) this.leftSubSplitPane
446                     .getUI()).getDivider();
447             this.rightDivider = ((BasicSplitPaneUI) this.rightSubSplitPane
448                     .getUI()).getDivider();
449             this.mainDivider = ((BasicSplitPaneUI) getUI()).getDivider();
450             setComponentsOptions();
451             bindScrollBars();
452             bindSplitPanes();
453             setupTresholdHandling();
454             setHorizontalDividerLocation(getHorizontalTreshold() + 5);
455             setDividerLocation(getVerticalTreshold() + 5);
456             // This listener enables to move all the dividers at the same time
457             // when dragging from the intersection.
458             this.mainDivider.addMouseMotionListener(new MouseMotionAdapter() {
459 
460                 protected boolean bound = false;
461 
462                 protected BindingMouseHandler left = new BindingMouseHandler(
463                         getLeftDivider()) {
464 
465                     protected MouseEvent changeMouseEventSource(MouseEvent e,
466                             Component source) {
467                         return new MouseEvent(source, e.getID(), e.getWhen(), e
468                                 .getModifiers(), 0, e.getY()
469                                 - getLeftDivider().getLocation().y, e
470                                 .getClickCount(), e.isPopupTrigger());
471                     }
472                 };
473 
474                 protected BindingMouseHandler right = new BindingMouseHandler(
475                         getRightDivider()) {
476 
477                     protected MouseEvent changeMouseEventSource(MouseEvent e,
478                             Component source) {
479                         return new MouseEvent(source, e.getID(), e.getWhen(), e
480                                 .getModifiers(), 0, e.getY()
481                                 - getRightDivider().getLocation().y, e
482                                 .getClickCount(), e.isPopupTrigger());
483                     }
484                 };
485 
486                 public void mouseMoved(MouseEvent e) {
487                     Point location = getLeftDivider().getLocation();
488                     int halfSize = getLeftDivider().getDividerSize() / 2;
489                     if ((getHorizontalMode() == DIVIDERS)
490                             && (getVerticalMode() == DIVIDERS)
491                             && (e.getX() >= 0) && (e.getX() <= (halfSize * 2))
492                             && (e.getY() >= (location.y - halfSize))
493                             && (e.getY() <= (location.y + halfSize))) {
494                         getMainDivider().setCursor(
495                                 Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
496                         if (!this.bound) {
497                             getMainDivider().addMouseMotionListener(this.left);
498                             getMainDivider().addMouseListener(this.left);
499                             getMainDivider().addMouseMotionListener(this.right);
500                             getMainDivider().addMouseListener(this.right);
501                         }
502                     } else {
503                         getMainDivider()
504                                 .setCursor(
505                                         Cursor
506                                                 .getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
507                         if (this.bound) {
508                             getMainDivider().removeMouseMotionListener(
509                                     this.left);
510                             getMainDivider().removeMouseListener(this.left);
511                             getMainDivider().removeMouseMotionListener(
512                                     this.right);
513                             getMainDivider().removeMouseListener(this.right);
514                         }
515                     }
516                 }
517             });
518         }
519     }
520 
521     /***
522      * Returns the viewed component of this QuadSplitPane.
523      * 
524      * @return The viewed component of this QuadSplitPane.
525      */
526     protected Component getViewedComponent() {
527         return this.viewedComponent;
528     }
529 
530     /***
531      * Sets the horizontal divider's location to the value given as parameter.
532      * 
533      * @param location
534      *            The new value of the location.
535      */
536     public void setHorizontalDividerLocation(int location) {
537         this.rightSubSplitPane.setDividerLocation(location);
538         this.leftSubSplitPane.setDividerLocation(location);
539     }
540 
541     /***
542      * 
543      * @return the current location of the horizontal divider.
544      */
545     public int getHorizontalDividerLocation() {
546         int result = -1;
547         if (this.leftSubSplitPane != null) {
548             result = this.leftSubSplitPane.getDividerLocation();
549         }
550         return result;
551     }
552 
553     /***
554      * Returns the treshold the horizontal splitpane divider should not go past.
555      * 
556      * @return The value of the actualized horizontal treshold.
557      */
558     public int getHorizontalTreshold() {
559         int result = 0;
560         if (((this.leftSubSplitPane != null) || (this.rightSubSplitPane != null))
561                 && (this.bottomLeftScrollPane != null)
562                 && (this.topLeftScrollPane != null)) {
563             JScrollBar scrollbar = (this.horizontalMode == DIVIDERS) ? this.bottomLeftScrollPane
564                     .getHorizontalScrollBar()
565                     : this.topLeftScrollPane.getHorizontalScrollBar();
566             Insets insets = (this.leftSubSplitPane != null) ? this.leftSubSplitPane
567                     .getInsets()
568                     : this.rightSubSplitPane.getInsets();
569             int dividerWidth = (this.leftSubSplitPane != null) ? this.leftSubSplitPane
570                     .getDividerSize()
571                     : this.rightSubSplitPane.getDividerSize();
572             if ((scrollbar != null) && (insets != null)) {
573                 result = this.leftSubSplitPane.getSize().height
574                         - scrollbar.getSize().height - dividerWidth
575                         - insets.bottom;
576             }
577         }
578         return result;
579     }
580 
581     /***
582      * Returns the treshold the vertical splitpane divider should not go past.
583      * 
584      * @return The value of the actualized vertical threshold.
585      */
586     public int getVerticalTreshold() {
587         int result = 0;
588         if ((this.topRightScrollPane != null)
589                 && (this.topLeftScrollPane != null)) {
590             JScrollBar scrollbar = (this.verticalMode == DIVIDERS) ? this.topRightScrollPane
591                     .getVerticalScrollBar()
592                     : this.topLeftScrollPane.getVerticalScrollBar();
593             Insets insets = getInsets();
594             if ((scrollbar != null) && (insets != null)) {
595                 result = getWidth() - scrollbar.getWidth() - getDividerSize()
596                         - insets.right;
597             }
598         }
599         return result;
600     }
601 
602     /***
603      * This convenience method sets various components options.
604      */
605     protected void setComponentsOptions() {
606         // Scrollpanes options.
607         this.bottomLeftScrollPane.setMinimumSize(new Dimension(0, 0));
608         this.bottomRightScrollPane.setMinimumSize(new Dimension(0, 0));
609         this.topRightScrollPane.setMinimumSize(new Dimension(0, 0));
610         this.topLeftScrollPane.setMinimumSize(new Dimension(0, 0));
611         // Inner Splitpanes options.
612         this.leftSubSplitPane.setOneTouchExpandable(true);
613         this.leftSubSplitPane.setMinimumSize(new Dimension(0, 0));
614         this.rightSubSplitPane.setMinimumSize(new Dimension(0, 0));
615         setMinimumSize(new java.awt.Dimension(0, 0));
616         setOneTouchExpandable(true);
617         super.setLeftComponent(this.leftSubSplitPane);
618         super.setRightComponent(this.rightSubSplitPane);
619     }
620 
621     /***
622      * This method sets up the treshold handling interface insures that the
623      * dividers be minimized when their location goes past a given treshold.
624      */
625     protected void setupTresholdHandling() {
626         this.leftSubSplitPane.addPropertyChangeListener(
627                 JSplitPane.DIVIDER_LOCATION_PROPERTY,
628                 new PropertyChangeListener() {
629 
630                     public void propertyChange(PropertyChangeEvent e) {
631                         int location = ((Integer) e.getNewValue()).intValue();
632                         setHorizontalMode(((location >= 0)
633                                 && (location <= getHorizontalTreshold()) ? DIVIDERS
634                                 : BUTTONS));
635                         switchUpdate();
636                     }
637                 });
638         addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY,
639                 new PropertyChangeListener() {
640 
641                     public void propertyChange(PropertyChangeEvent e) {
642                         int location = ((Integer) e.getNewValue()).intValue();
643                         setVerticalMode((((location >= 0) && (location <= getVerticalTreshold())) ? DIVIDERS
644                                 : BUTTONS));
645                         switchUpdate();
646                     }
647                 });
648     }
649 
650     /***
651      * Binds the four scrollpanes' scrollbars so that they move according to
652      * each other.
653      */
654     protected void bindScrollBars() {
655         // Binding left horizontal bars.
656         this.topLeftScrollPane.getHorizontalScrollBar().addAdjustmentListener(
657                 new AdjustmentListener() {
658 
659                     public void adjustmentValueChanged(AdjustmentEvent e) {
660                         int newValue = e.getValue();
661                         int boundBarValue = getBottomLeftScrollPane()
662                                 .getHorizontalScrollBar().getValue();
663                         if ((e.getID() == AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED)
664                                 && (e.getAdjustmentType() == AdjustmentEvent.TRACK)
665                                 && (boundBarValue != newValue)) {
666                             getBottomLeftScrollPane().getHorizontalScrollBar()
667                                     .setValue(newValue);
668                         }
669                     }
670                 });
671         this.bottomLeftScrollPane.getHorizontalScrollBar()
672                 .addAdjustmentListener(new AdjustmentListener() {
673 
674                     public void adjustmentValueChanged(AdjustmentEvent e) {
675                         int newValue = e.getValue();
676                         int boundBarValue = getTopLeftScrollPane()
677                                 .getHorizontalScrollBar().getValue();
678                         if ((e.getID() == AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED)
679                                 && (e.getAdjustmentType() == AdjustmentEvent.TRACK)
680                                 && (boundBarValue != newValue)) {
681                             getTopLeftScrollPane().getHorizontalScrollBar()
682                                     .setValue(newValue);
683                         }
684                     }
685                 });
686         // Binding right horizontal bars.
687         this.topRightScrollPane.getHorizontalScrollBar().addAdjustmentListener(
688                 new AdjustmentListener() {
689 
690                     public void adjustmentValueChanged(AdjustmentEvent e) {
691                         int newValue = e.getValue();
692                         int boundBarValue = getBottomRightScrollPane()
693                                 .getHorizontalScrollBar().getValue();
694                         if ((e.getID() == AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED)
695                                 && (e.getAdjustmentType() == AdjustmentEvent.TRACK)
696                                 && (boundBarValue != newValue)) {
697                             getBottomRightScrollPane().getHorizontalScrollBar()
698                                     .setValue(newValue);
699                         }
700                     }
701                 });
702         this.bottomRightScrollPane.getHorizontalScrollBar()
703                 .addAdjustmentListener(new AdjustmentListener() {
704 
705                     public void adjustmentValueChanged(AdjustmentEvent e) {
706                         int newValue = e.getValue();
707                         int boundBarValue = getTopRightScrollPane()
708                                 .getHorizontalScrollBar().getValue();
709                         if ((e.getID() == AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED)
710                                 && (e.getAdjustmentType() == AdjustmentEvent.TRACK)
711                                 && (boundBarValue != newValue)) {
712                             getTopRightScrollPane().getHorizontalScrollBar()
713                                     .setValue(newValue);
714                         }
715                     }
716                 });
717         // Binding top vertical bars.
718         this.topLeftScrollPane.getVerticalScrollBar().addAdjustmentListener(
719                 new AdjustmentListener() {
720 
721                     public void adjustmentValueChanged(AdjustmentEvent e) {
722                         int newValue = e.getValue();
723                         int boundBarValue = getTopRightScrollPane()
724                                 .getVerticalScrollBar().getValue();
725                         if ((e.getID() == AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED)
726                                 && (e.getAdjustmentType() == AdjustmentEvent.TRACK)
727                                 && (boundBarValue != newValue)) {
728                             getTopRightScrollPane().getVerticalScrollBar()
729                                     .setValue(newValue);
730                         }
731                     }
732                 });
733         this.topRightScrollPane.getVerticalScrollBar().addAdjustmentListener(
734                 new AdjustmentListener() {
735 
736                     public void adjustmentValueChanged(AdjustmentEvent e) {
737                         int newValue = e.getValue();
738                         int boundBarValue = getTopLeftScrollPane()
739                                 .getVerticalScrollBar().getValue();
740                         if ((e.getID() == AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED)
741                                 && (e.getAdjustmentType() == AdjustmentEvent.TRACK)
742                                 && (boundBarValue != newValue)) {
743                             getTopLeftScrollPane().getVerticalScrollBar()
744                                     .setValue(newValue);
745                         }
746                     }
747                 });
748         // Binding bottom vertical bars.
749         this.bottomLeftScrollPane.getVerticalScrollBar().addAdjustmentListener(
750                 new AdjustmentListener() {
751 
752                     public void adjustmentValueChanged(AdjustmentEvent e) {
753                         int newValue = e.getValue();
754                         int boundBarValue = getBottomRightScrollPane()
755                                 .getVerticalScrollBar().getValue();
756                         if ((e.getID() == AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED)
757                                 && (e.getAdjustmentType() == AdjustmentEvent.TRACK)
758                                 && (boundBarValue != newValue)) {
759                             getBottomRightScrollPane().getVerticalScrollBar()
760                                     .setValue(newValue);
761                         }
762                     }
763                 });
764         this.bottomRightScrollPane.getVerticalScrollBar()
765                 .addAdjustmentListener(new AdjustmentListener() {
766 
767                     public void adjustmentValueChanged(AdjustmentEvent e) {
768                         int newValue = e.getValue();
769                         int boundBarValue = getBottomLeftScrollPane()
770                                 .getVerticalScrollBar().getValue();
771                         if ((e.getID() == AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED)
772                                 && (e.getAdjustmentType() == AdjustmentEvent.TRACK)
773                                 && (boundBarValue != newValue)) {
774                             getBottomLeftScrollPane().getVerticalScrollBar()
775                                     .setValue(newValue);
776                         }
777                     }
778                 });
779     }
780 
781     /***
782      * Binds the two sub splitpanes using BindingMouseHandler to transmit mouse
783      * events.
784      */
785     protected void bindSplitPanes() {
786         // Binding left divider's one-touch buttons to the right divider.
787         if (getLeftDivider().getComponentCount() == 2) {
788             Component comp = getLeftDivider().getComponent(0);
789             if (comp instanceof JButton) {
790                 ((JButton) comp).addActionListener(new ActionListener() {
791 
792                     public void actionPerformed(ActionEvent e) {
793                         leftButtonActionPerformed();
794                     }
795                 });
796             }
797             comp = getLeftDivider().getComponent(1);
798             if (comp instanceof JButton) {
799                 ((JButton) comp).addActionListener(new ActionListener() {
800 
801                     public void actionPerformed(ActionEvent e) {
802                         rightButtonActionPerformed();
803                     }
804                 });
805             }
806         }
807         BindingMouseHandler bindingMouseHandler = new BindingMouseHandler(
808                 getRightDivider());
809         getLeftDivider().addMouseListener(bindingMouseHandler);
810         getLeftDivider().addMouseMotionListener(bindingMouseHandler);
811         bindingMouseHandler = new BindingMouseHandler(this.rightSubSplitPane);
812         this.leftSubSplitPane.addMouseListener(bindingMouseHandler);
813         this.leftSubSplitPane.addMouseMotionListener(bindingMouseHandler);
814         bindingMouseHandler = new BindingMouseHandler(this.leftSubSplitPane);
815         this.rightSubSplitPane.addMouseListener(bindingMouseHandler);
816         this.rightSubSplitPane.addMouseMotionListener(bindingMouseHandler);
817         bindingMouseHandler = new BindingMouseHandler(getLeftDivider());
818         getRightDivider().addMouseListener(bindingMouseHandler);
819         getRightDivider().addMouseMotionListener(bindingMouseHandler);
820     }
821 
822     /***
823      * This method is triggered when the left one-touch button is used. It makes
824      * the right part of the horizontal divider take that into account and move
825      * accordingly.
826      */
827     protected void leftButtonActionPerformed() {
828         Insets insets = getRightSubSplitPane().getInsets();
829         int lastLoc = getRightSubSplitPane().getLastDividerLocation();
830         int currentLoc = getRightSubSplitPane().getUI().getDividerLocation(
831                 getRightSubSplitPane());
832         int newLoc;
833         if (currentLoc >= (getRightSubSplitPane().getHeight() - insets.bottom - ((BasicSplitPaneUI) getRightSubSplitPane()
834                 .getUI()).getDivider().getDividerSize()))
835             newLoc = lastLoc;
836         else
837             newLoc = insets.top;
838         if (currentLoc != newLoc) {
839             getRightSubSplitPane().setDividerLocation(newLoc);
840             // We do this in case the dividers notion of the location
841             // differs from the real location.
842             getRightSubSplitPane().setLastDividerLocation(currentLoc);
843         }
844     }
845 
846     /***
847      * This method is triggered when the right one-touch button is used. It
848      * makes the right part of the horizontal divider take that into account and
849      * move accordingly.
850      */
851     protected void rightButtonActionPerformed() {
852         Insets insets = this.rightSubSplitPane.getInsets();
853         int lastLoc = this.rightSubSplitPane.getLastDividerLocation();
854         int currentLoc = this.rightSubSplitPane.getUI().getDividerLocation(
855                 this.rightSubSplitPane);
856         int newLoc;
857         if (currentLoc == insets.top)
858             newLoc = lastLoc;
859         else
860             newLoc = this.rightSubSplitPane.getHeight()
861                     - ((BasicSplitPaneUI) this.rightSubSplitPane.getUI())
862                             .getDivider().getHeight() - insets.top;
863         if (currentLoc != newLoc) {
864             this.rightSubSplitPane.setDividerLocation(newLoc);
865             // We do this in case the dividers notion of the location
866             // differs from the real location.
867             this.rightSubSplitPane.setLastDividerLocation(currentLoc);
868         }
869     }
870 
871     protected void switchUpdate() {
872         if (this.verticalMode == BUTTONS) {
873             setDividerSize(0);
874             this.topLeftScrollPane.setCorner(
875                     ScrollPaneConstants.UPPER_RIGHT_CORNER,
876                     this.verticalSplitButton);
877         } else {
878             setDividerSize(8);
879         }
880         if (this.horizontalMode == BUTTONS) {
881             this.leftSubSplitPane.setDividerSize(0);
882             this.rightSubSplitPane.setDividerSize(0);
883             (this.verticalMode == BUTTONS ? this.topLeftScrollPane
884                     : this.topRightScrollPane).setCorner(
885                     ScrollPaneConstants.LOWER_RIGHT_CORNER,
886                     this.horizontalSplitButton);
887         } else {
888             this.leftSubSplitPane.setDividerSize(8);
889             this.rightSubSplitPane.setDividerSize(8);
890         }
891         super.getRightComponent().setVisible(this.verticalMode == DIVIDERS);
892         this.leftSubSplitPane.getBottomComponent().setVisible(
893                 this.horizontalMode == DIVIDERS);
894         this.rightSubSplitPane.getBottomComponent().setVisible(
895                 this.horizontalMode == DIVIDERS);
896         this.rightSubSplitPane
897                 .setOneTouchExpandable((this.verticalMode == DIVIDERS)
898                         && isOneTouchExpandable());
899         this.leftSubSplitPane
900                 .setOneTouchExpandable((this.verticalMode != DIVIDERS)
901                         && isOneTouchExpandable());
902         setHorizontalScrollBarsVisibleValues(
903                 (this.horizontalMode != DIVIDERS),
904                 ((this.horizontalMode != DIVIDERS) && (this.verticalMode == DIVIDERS)),
905                 (this.horizontalMode == DIVIDERS),
906                 ((this.horizontalMode == DIVIDERS) && (this.verticalMode == DIVIDERS)));
907         setVerticalScrollBarsVisibleValues(
908                 (this.verticalMode != DIVIDERS),
909                 (this.verticalMode == DIVIDERS),
910                 ((this.verticalMode != DIVIDERS) && (this.horizontalMode == DIVIDERS)),
911                 ((this.verticalMode == DIVIDERS) && (this.horizontalMode == DIVIDERS)));
912         validateTree();
913     }
914 
915     /***
916      * Creates the horizontal split button that is used in place of the
917      * horizontal divider when it is "minimized".
918      * 
919      * @return A JPanel the button to be used.
920      */
921     protected Component createHorizontalSplitButton() {
922         this.horizontalSplitButton
923                 .setUI(new javax.swing.plaf.metal.MetalButtonUI());
924         this.horizontalSplitButton.setCursor(Cursor
925                 .getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
926         this.horizontalSplitButton.addMouseListener(new MouseAdapter() {
927 
928             public void mouseReleased(MouseEvent e) {
929                 Point from1 = getLeftDivider().getLocation();
930                 if (isContinuousLayout()) {
931                     setContinuousDragging(false);
932                 }
933                 getLeftDivider().dispatchEvent(
934                         new MouseEvent(getLeftDivider(), e.getID(),
935                                 e.getWhen(), e.getModifiers(), e.getX()
936                                         - from1.x, e.getY() - from1.y, e
937                                         .getClickCount(), e.isPopupTrigger()));
938             }
939 
940             public void mousePressed(MouseEvent e) {
941                 if (isContinuousLayout()) {
942                     setContinuousDragging(true);
943                 }
944                 setHorizontalDividerLocation(getHorizontalTreshold() - 1);
945                 getLeftDivider().dispatchEvent(
946                         new MouseEvent(getLeftDivider(), e.getID(),
947                                 e.getWhen(), e.getModifiers(), e.getX(), 0, e
948                                         .getClickCount(), e.isPopupTrigger()));
949             }
950         });
951         this.horizontalSplitButton
952                 .addMouseMotionListener(new MouseMotionAdapter() {
953 
954                     public void mouseDragged(MouseEvent e) {
955                         Point from1 = getLeftDivider().getLocation();
956                         int offset1 = e.getY() - from1.y;
957                         getLeftDivider().dispatchEvent(
958                                 new MouseEvent(getLeftDivider(), e.getID(), e
959                                         .getWhen(), e.getModifiers(), e.getX()
960                                         - from1.x, offset1, e.getClickCount(),
961                                         e.isPopupTrigger()));
962                     }
963                 });
964         return this.horizontalSplitButton;
965     }
966 
967     /***
968      * Creates the vertical split button that is used in place of the vertical
969      * divider when it is "minimized".
970      * 
971      * @return A JPanel containing the button to be used.
972      */
973     protected Component createVerticalSplitButton() {
974         this.verticalSplitButton
975                 .setUI(new javax.swing.plaf.metal.MetalButtonUI());
976         this.verticalSplitButton.setCursor(Cursor
977                 .getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
978         this.verticalSplitButton.addMouseListener(new MouseAdapter() {
979 
980             public void mouseReleased(MouseEvent e) {
981                 Point from = getMainDivider().getLocation();
982                 if (isContinuousLayout()) {
983                     setContinuousDragging(false);
984                 }
985                 getMainDivider().dispatchEvent(
986                         new MouseEvent(getMainDivider(), e.getID(),
987                                 e.getWhen(), e.getModifiers(), e.getX()
988                                         - from.x, e.getY() - from.y, e
989                                         .getClickCount(), e.isPopupTrigger()));
990             }
991 
992             public void mousePressed(MouseEvent e) {
993                 if (isContinuousLayout()) {
994                     setContinuousDragging(true);
995                 }
996                 setDividerLocation(getVerticalTreshold() - 1);
997                 if (getHorizontalMode() == DIVIDERS) {
998                     // The right divider appears in a wrong position.
999                     // Set everything in the right position.
1000                     setHorizontalDividerLocation(getLeftSubSplitPane()
1001                             .getDividerLocation());
1002                 }
1003                 getMainDivider().dispatchEvent(
1004                         new MouseEvent(getMainDivider(), e.getID(),
1005                                 e.getWhen(), e.getModifiers(), 0, e.getY(), e
1006                                         .getClickCount(), e.isPopupTrigger()));
1007             }
1008         });
1009         this.verticalSplitButton
1010                 .addMouseMotionListener(new MouseMotionAdapter() {
1011 
1012                     public void mouseDragged(MouseEvent e) {
1013                         Point from = getMainDivider().getLocation();
1014                         int offset = e.getX() - from.x;
1015                         if (offset != 0) {
1016                             getMainDivider().dispatchEvent(
1017                                     new MouseEvent(getMainDivider(), e.getID(),
1018                                             e.getWhen(), e.getModifiers(),
1019                                             offset, e.getY() - from.y, e
1020                                                     .getClickCount(), e
1021                                                     .isPopupTrigger()));
1022                         }
1023                     }
1024                 });
1025         return this.verticalSplitButton;
1026     }
1027 
1028     /***
1029      * Sets the four scrollpanes' horizontal scrollbar policies according to the
1030      * boolean parameters, representing whether the corresponding scrollbar
1031      * should be displayed or not.
1032      * 
1033      * @param topLeft
1034      *            Whether the top-left horizontal scrollbar should be displayed.
1035      * @param topRight
1036      *            Whether the top-right horizontal scrollbar should be
1037      *            displayed.
1038      * @param bottomLeft
1039      *            Whether the bottom-left horizontal scrollbar should be
1040      *            displayed.
1041      * @param bottomRight
1042      *            Whether the bottom-right horizontal scrollbar should be
1043      *            displayed.
1044      */
1045     protected void setHorizontalScrollBarsVisibleValues(boolean topLeft,
1046             boolean topRight, boolean bottomLeft, boolean bottomRight) {
1047         this.topLeftScrollPane
1048                 .setHorizontalScrollBarPolicy(topLeft ? ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS
1049                         : ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
1050         this.topRightScrollPane
1051                 .setHorizontalScrollBarPolicy(topRight ? ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS
1052                         : ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
1053         this.bottomLeftScrollPane
1054                 .setHorizontalScrollBarPolicy(bottomLeft ? ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS
1055                         : ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
1056         this.bottomRightScrollPane
1057                 .setHorizontalScrollBarPolicy(bottomRight ? ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS
1058                         : ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
1059     }
1060 
1061     /***
1062      * Sets the four scrollpanes' vertical scrollbar policies according to the
1063      * boolean parameters, representing whether the corresponding scrollbar
1064      * should be displayed or not.
1065      * 
1066      * @param topLeft
1067      *            Whether the top-left vertical scrollbar should be displayed.
1068      * @param topRight
1069      *            Whether the top-right vertical scrollbar should be displayed.
1070      * @param bottomLeft
1071      *            Whether the bottom-left vertical scrollbar should be
1072      *            displayed.
1073      * @param bottomRight
1074      *            Whether the bottom-right vertical scrollbar should be
1075      *            displayed.
1076      */
1077     protected void setVerticalScrollBarsVisibleValues(boolean topLeft,
1078             boolean topRight, boolean bottomLeft, boolean bottomRight) {
1079         this.topLeftScrollPane
1080                 .setVerticalScrollBarPolicy(topLeft ? ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
1081                         : ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
1082         this.topRightScrollPane
1083                 .setVerticalScrollBarPolicy(topRight ? ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
1084                         : ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
1085         this.bottomLeftScrollPane
1086                 .setVerticalScrollBarPolicy(bottomLeft ? ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
1087                         : ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
1088         this.bottomRightScrollPane
1089                 .setVerticalScrollBarPolicy(bottomRight ? ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
1090                         : ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
1091     }
1092 
1093     /***
1094      * 
1095      * @return the current horizontal mode (either BUTTONS or DIVIDERS)
1096      */
1097     public int getHorizontalMode() {
1098         return this.horizontalMode;
1099     }
1100 
1101     protected final void setHorizontalMode(int mode) {
1102         this.horizontalMode = mode;
1103     }
1104 
1105     /***
1106      * 
1107      * @return the current vertical mode (either BUTTONS or DIVIDERS)
1108      */
1109     public int getVerticalMode() {
1110         return this.verticalMode;
1111     }
1112 
1113     protected final void setVerticalMode(int mode) {
1114         this.verticalMode = mode;
1115     }
1116 
1117     /***
1118      * @see Component#addMouseListener(java.awt.event.MouseListener)
1119      */
1120     public void addMouseListener(MouseListener l) {
1121         super.addMouseListener(l);
1122         if (this.viewedComponent != null) {
1123             this.viewedComponent.addMouseListener(l);
1124         }
1125     }
1126 }