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
457
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
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
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
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
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
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
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
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
841
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
866
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
999
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 }