History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

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

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

Unified · Split

java/pkg/javax/swing/plaf/basic/BasicScrollPaneUI.java+1178−0
@@ -0,0 +1,1178 @@
1/*
2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing.plaf.basic;
27
28import sun.swing.DefaultLookup;
29import sun.swing.UIAction;
30
31import javax.swing.*;
32import javax.swing.event.*;
33import javax.swing.border.*;
34import javax.swing.plaf.*;
35
36import java.beans.PropertyChangeListener;
37import java.beans.PropertyChangeEvent;
38
39import java.awt.Component;
40import java.awt.Rectangle;
41import java.awt.Dimension;
42import java.awt.Point;
43import java.awt.Insets;
44import java.awt.Graphics;
45import java.awt.event.*;
46
47/**
48 * A default L&F implementation of ScrollPaneUI.
49 *
50 * @author Hans Muller
51 */
52public class BasicScrollPaneUI
53 extends ScrollPaneUI implements ScrollPaneConstants
54{
55 protected JScrollPane scrollpane;
56 protected ChangeListener vsbChangeListener;
57 protected ChangeListener hsbChangeListener;
58 protected ChangeListener viewportChangeListener;
59 protected PropertyChangeListener spPropertyChangeListener;
60 private MouseWheelListener mouseScrollListener;
61 private int oldExtent = Integer.MIN_VALUE;
62
63 /**
64 * PropertyChangeListener installed on the vertical scrollbar.
65 */
66 private PropertyChangeListener vsbPropertyChangeListener;
67
68 /**
69 * PropertyChangeListener installed on the horizontal scrollbar.
70 */
71 private PropertyChangeListener hsbPropertyChangeListener;
72
73 private Handler handler;
74
75 /**
76 * State flag that shows whether setValue() was called from a user program
77 * before the value of "extent" was set in right-to-left component
78 * orientation.
79 */
80 private boolean setValueCalled = false;
81
82
83 public static ComponentUI createUI(JComponent x) {
84 return new BasicScrollPaneUI();
85 }
86
87 static void loadActionMap(LazyActionMap map) {
88 map.put(new Actions(Actions.SCROLL_UP));
89 map.put(new Actions(Actions.SCROLL_DOWN));
90 map.put(new Actions(Actions.SCROLL_HOME));
91 map.put(new Actions(Actions.SCROLL_END));
92 map.put(new Actions(Actions.UNIT_SCROLL_UP));
93 map.put(new Actions(Actions.UNIT_SCROLL_DOWN));
94 map.put(new Actions(Actions.SCROLL_LEFT));
95 map.put(new Actions(Actions.SCROLL_RIGHT));
96 map.put(new Actions(Actions.UNIT_SCROLL_RIGHT));
97 map.put(new Actions(Actions.UNIT_SCROLL_LEFT));
98 }
99
100
101
102 public void paint(Graphics g, JComponent c) {
103 Border vpBorder = scrollpane.getViewportBorder();
104 if (vpBorder != null) {
105 Rectangle r = scrollpane.getViewportBorderBounds();
106 vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height);
107 }
108 }
109
110
111 /**
112 * @return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE)
113 */
114 public Dimension getMaximumSize(JComponent c) {
115 return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
116 }
117
118
119 protected void installDefaults(JScrollPane scrollpane)
120 {
121 LookAndFeel.installBorder(scrollpane, "ScrollPane.border");
122 LookAndFeel.installColorsAndFont(scrollpane,
123 "ScrollPane.background",
124 "ScrollPane.foreground",
125 "ScrollPane.font");
126
127 Border vpBorder = scrollpane.getViewportBorder();
128 if ((vpBorder == null) ||( vpBorder instanceof UIResource)) {
129 vpBorder = UIManager.getBorder("ScrollPane.viewportBorder");
130 scrollpane.setViewportBorder(vpBorder);
131 }
132 LookAndFeel.installProperty(scrollpane, "opaque", Boolean.TRUE);
133 }
134
135
136 protected void installListeners(JScrollPane c)
137 {
138 vsbChangeListener = createVSBChangeListener();
139 vsbPropertyChangeListener = createVSBPropertyChangeListener();
140 hsbChangeListener = createHSBChangeListener();
141 hsbPropertyChangeListener = createHSBPropertyChangeListener();
142 viewportChangeListener = createViewportChangeListener();
143 spPropertyChangeListener = createPropertyChangeListener();
144
145 JViewport viewport = scrollpane.getViewport();
146 JScrollBar vsb = scrollpane.getVerticalScrollBar();
147 JScrollBar hsb = scrollpane.getHorizontalScrollBar();
148
149 if (viewport != null) {
150 viewport.addChangeListener(viewportChangeListener);
151 }
152 if (vsb != null) {
153 vsb.getModel().addChangeListener(vsbChangeListener);
154 vsb.addPropertyChangeListener(vsbPropertyChangeListener);
155 }
156 if (hsb != null) {
157 hsb.getModel().addChangeListener(hsbChangeListener);
158 hsb.addPropertyChangeListener(hsbPropertyChangeListener);
159 }
160
161 scrollpane.addPropertyChangeListener(spPropertyChangeListener);
162
163 mouseScrollListener = createMouseWheelListener();
164 scrollpane.addMouseWheelListener(mouseScrollListener);
165
166 }
167
168 protected void installKeyboardActions(JScrollPane c) {
169 InputMap inputMap = getInputMap(JComponent.
170 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
171
172 SwingUtilities.replaceUIInputMap(c, JComponent.
173 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, inputMap);
174
175 LazyActionMap.installLazyActionMap(c, BasicScrollPaneUI.class,
176 "ScrollPane.actionMap");
177 }
178
179 InputMap getInputMap(int condition) {
180 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
181 InputMap keyMap = (InputMap)DefaultLookup.get(scrollpane, this,
182 "ScrollPane.ancestorInputMap");
183 InputMap rtlKeyMap;
184
185 if (scrollpane.getComponentOrientation().isLeftToRight() ||
186 ((rtlKeyMap = (InputMap)DefaultLookup.get(scrollpane, this,
187 "ScrollPane.ancestorInputMap.RightToLeft")) == null)) {
188 return keyMap;
189 } else {
190 rtlKeyMap.setParent(keyMap);
191 return rtlKeyMap;
192 }
193 }
194 return null;
195 }
196
197 public void installUI(JComponent x) {
198 scrollpane = (JScrollPane)x;
199 installDefaults(scrollpane);
200 installListeners(scrollpane);
201 installKeyboardActions(scrollpane);
202 }
203
204
205 protected void uninstallDefaults(JScrollPane c) {
206 LookAndFeel.uninstallBorder(scrollpane);
207
208 if (scrollpane.getViewportBorder() instanceof UIResource) {
209 scrollpane.setViewportBorder(null);
210 }
211 }
212
213
214 protected void uninstallListeners(JComponent c) {
215 JViewport viewport = scrollpane.getViewport();
216 JScrollBar vsb = scrollpane.getVerticalScrollBar();
217 JScrollBar hsb = scrollpane.getHorizontalScrollBar();
218
219 if (viewport != null) {
220 viewport.removeChangeListener(viewportChangeListener);
221 }
222 if (vsb != null) {
223 vsb.getModel().removeChangeListener(vsbChangeListener);
224 vsb.removePropertyChangeListener(vsbPropertyChangeListener);
225 }
226 if (hsb != null) {
227 hsb.getModel().removeChangeListener(hsbChangeListener);
228 hsb.removePropertyChangeListener(hsbPropertyChangeListener);
229 }
230
231 scrollpane.removePropertyChangeListener(spPropertyChangeListener);
232
233 if (mouseScrollListener != null) {
234 scrollpane.removeMouseWheelListener(mouseScrollListener);
235 }
236
237 vsbChangeListener = null;
238 hsbChangeListener = null;
239 viewportChangeListener = null;
240 spPropertyChangeListener = null;
241 mouseScrollListener = null;
242 handler = null;
243 }
244
245
246 protected void uninstallKeyboardActions(JScrollPane c) {
247 SwingUtilities.replaceUIActionMap(c, null);
248 SwingUtilities.replaceUIInputMap(c, JComponent.
249 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
250 }
251
252
253 public void uninstallUI(JComponent c) {
254 uninstallDefaults(scrollpane);
255 uninstallListeners(scrollpane);
256 uninstallKeyboardActions(scrollpane);
257 scrollpane = null;
258 }
259
260 private Handler getHandler() {
261 if (handler == null) {
262 handler = new Handler();
263 }
264 return handler;
265 }
266
267 protected void syncScrollPaneWithViewport()
268 {
269 JViewport viewport = scrollpane.getViewport();
270 JScrollBar vsb = scrollpane.getVerticalScrollBar();
271 JScrollBar hsb = scrollpane.getHorizontalScrollBar();
272 JViewport rowHead = scrollpane.getRowHeader();
273 JViewport colHead = scrollpane.getColumnHeader();
274 boolean ltr = scrollpane.getComponentOrientation().isLeftToRight();
275
276 if (viewport != null) {
277 Dimension extentSize = viewport.getExtentSize();
278 Dimension viewSize = viewport.getViewSize();
279 Point viewPosition = viewport.getViewPosition();
280
281 if (vsb != null) {
282 int extent = extentSize.height;
283 int max = viewSize.height;
284 int value = Math.max(0, Math.min(viewPosition.y, max - extent));
285 vsb.setValues(value, extent, 0, max);
286 }
287
288 if (hsb != null) {
289 int extent = extentSize.width;
290 int max = viewSize.width;
291 int value;
292
293 if (ltr) {
294 value = Math.max(0, Math.min(viewPosition.x, max - extent));
295 } else {
296 int currentValue = hsb.getValue();
297
298 /* Use a particular formula to calculate "value"
299 * until effective x coordinate is calculated.
300 */
301 if (setValueCalled && ((max - currentValue) == viewPosition.x)) {
302 value = Math.max(0, Math.min(max - extent, currentValue));
303 /* After "extent" is set, turn setValueCalled flag off.
304 */
305 if (extent != 0) {
306 setValueCalled = false;
307 }
308 } else {
309 if (extent > max) {
310 viewPosition.x = max - extent;
311 viewport.setViewPosition(viewPosition);
312 value = 0;
313 } else {
314 /* The following line can't handle a small value of
315 * viewPosition.x like Integer.MIN_VALUE correctly
316 * because (max - extent - viewPositoiin.x) causes
317 * an overflow. As a result, value becomes zero.
318 * (e.g. setViewPosition(Integer.MAX_VALUE, ...)
319 * in a user program causes a overflow.
320 * Its expected value is (max - extent).)
321 * However, this seems a trivial bug and adding a
322 * fix makes this often-called method slow, so I'll
323 * leave it until someone claims.
324 */
325 value = Math.max(0, Math.min(max - extent, max - extent - viewPosition.x));
326 if (oldExtent > extent) {
327 value -= oldExtent - extent;
328 }
329 }
330 }
331 }
332 oldExtent = extent;
333 hsb.setValues(value, extent, 0, max);
334 }
335
336 if (rowHead != null) {
337 Point p = rowHead.getViewPosition();
338 p.y = viewport.getViewPosition().y;
339 p.x = 0;
340 rowHead.setViewPosition(p);
341 }
342
343 if (colHead != null) {
344 Point p = colHead.getViewPosition();
345 if (ltr) {
346 p.x = viewport.getViewPosition().x;
347 } else {
348 p.x = Math.max(0, viewport.getViewPosition().x);
349 }
350 p.y = 0;
351 colHead.setViewPosition(p);
352 }
353 }
354 }
355
356 /**
357 * Returns the baseline.
358 *
359 * @throws NullPointerException {@inheritDoc}
360 * @throws IllegalArgumentException {@inheritDoc}
361 * @see javax.swing.JComponent#getBaseline(int, int)
362 * @since 1.6
363 */
364 public int getBaseline(JComponent c, int width, int height) {
365 if (c == null) {
366 throw new NullPointerException("Component must be non-null");
367 }
368
369 if (width < 0 || height < 0) {
370 throw new IllegalArgumentException("Width and height must be >= 0");
371 }
372
373 JViewport viewport = scrollpane.getViewport();
374 Insets spInsets = scrollpane.getInsets();
375 int y = spInsets.top;
376 height = height - spInsets.top - spInsets.bottom;
377 width = width - spInsets.left - spInsets.right;
378 JViewport columnHeader = scrollpane.getColumnHeader();
379 if (columnHeader != null && columnHeader.isVisible()) {
380 Component header = columnHeader.getView();
381 if (header != null && header.isVisible()) {
382 // Header is always given it's preferred size.
383 Dimension headerPref = header.getPreferredSize();
384 int baseline = header.getBaseline(headerPref.width,
385 headerPref.height);
386 if (baseline >= 0) {
387 return y + baseline;
388 }
389 }
390 Dimension columnPref = columnHeader.getPreferredSize();
391 height -= columnPref.height;
392 y += columnPref.height;
393 }
394 Component view = (viewport == null) ? null : viewport.getView();
395 if (view != null && view.isVisible() &&
396 view.getBaselineResizeBehavior() ==
397 Component.BaselineResizeBehavior.CONSTANT_ASCENT) {
398 Border viewportBorder = scrollpane.getViewportBorder();
399 if (viewportBorder != null) {
400 Insets vpbInsets = viewportBorder.getBorderInsets(scrollpane);
401 y += vpbInsets.top;
402 height = height - vpbInsets.top - vpbInsets.bottom;
403 width = width - vpbInsets.left - vpbInsets.right;
404 }
405 if (view.getWidth() > 0 && view.getHeight() > 0) {
406 Dimension min = view.getMinimumSize();
407 width = Math.max(min.width, view.getWidth());
408 height = Math.max(min.height, view.getHeight());
409 }
410 if (width > 0 && height > 0) {
411 int baseline = view.getBaseline(width, height);
412 if (baseline > 0) {
413 return y + baseline;
414 }
415 }
416 }
417 return -1;
418 }
419
420 /**
421 * Returns an enum indicating how the baseline of the component
422 * changes as the size changes.
423 *
424 * @throws NullPointerException {@inheritDoc}
425 * @see javax.swing.JComponent#getBaseline(int, int)
426 * @since 1.6
427 */
428 public Component.BaselineResizeBehavior getBaselineResizeBehavior(
429 JComponent c) {
430 super.getBaselineResizeBehavior(c);
431 // Baseline is either from the header, in which case it's always
432 // the same size and therefor can be created as CONSTANT_ASCENT.
433 // If the header doesn't have a baseline than the baseline will only
434 // be valid if it's BaselineResizeBehavior is
435 // CONSTANT_ASCENT, so, return CONSTANT_ASCENT.
436 return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
437 }
438
439
440 /**
441 * Listener for viewport events.
442 */
443 public class ViewportChangeHandler implements ChangeListener
444 {
445
446 // NOTE: This class exists only for backward compatability. All
447 // its functionality has been moved into Handler. If you need to add
448 // new functionality add it to the Handler, but make sure this
449 // class calls into the Handler.
450
451 public void stateChanged(ChangeEvent e) {
452 getHandler().stateChanged(e);
453 }
454 }
455
456 protected ChangeListener createViewportChangeListener() {
457 return getHandler();
458 }
459
460
461 /**
462 * Horizontal scrollbar listener.
463 */
464 public class HSBChangeListener implements ChangeListener
465 {
466
467 // NOTE: This class exists only for backward compatability. All
468 // its functionality has been moved into Handler. If you need to add
469 // new functionality add it to the Handler, but make sure this
470 // class calls into the Handler.
471
472 public void stateChanged(ChangeEvent e)
473 {
474 getHandler().stateChanged(e);
475 }
476 }
477
478 /**
479 * Returns a <code>PropertyChangeListener</code> that will be installed
480 * on the horizontal <code>JScrollBar</code>.
481 */
482 private PropertyChangeListener createHSBPropertyChangeListener() {
483 return getHandler();
484 }
485
486 protected ChangeListener createHSBChangeListener() {
487 return getHandler();
488 }
489
490
491 /**
492 * Vertical scrollbar listener.
493 */
494 public class VSBChangeListener implements ChangeListener
495 {
496
497 // NOTE: This class exists only for backward compatability. All
498 // its functionality has been moved into Handler. If you need to add
499 // new functionality add it to the Handler, but make sure this
500 // class calls into the Handler.
501
502 public void stateChanged(ChangeEvent e)
503 {
504 getHandler().stateChanged(e);
505 }
506 }
507
508
509 /**
510 * Returns a <code>PropertyChangeListener</code> that will be installed
511 * on the vertical <code>JScrollBar</code>.
512 */
513 private PropertyChangeListener createVSBPropertyChangeListener() {
514 return getHandler();
515 }
516
517 protected ChangeListener createVSBChangeListener() {
518 return getHandler();
519 }
520
521 /**
522 * MouseWheelHandler is an inner class which implements the
523 * MouseWheelListener interface. MouseWheelHandler responds to
524 * MouseWheelEvents by scrolling the JScrollPane appropriately.
525 * If the scroll pane's
526 * <code>isWheelScrollingEnabled</code>
527 * method returns false, no scrolling occurs.
528 *
529 * @see javax.swing.JScrollPane#isWheelScrollingEnabled
530 * @see #createMouseWheelListener
531 * @see java.awt.event.MouseWheelListener
532 * @see java.awt.event.MouseWheelEvent
533 * @since 1.4
534 */
535 protected class MouseWheelHandler implements MouseWheelListener {
536
537 // NOTE: This class exists only for backward compatability. All
538 // its functionality has been moved into Handler. If you need to add
539 // new functionality add it to the Handler, but make sure this
540 // class calls into the Handler.
541
542 /**
543 * Called when the mouse wheel is rotated while over a
544 * JScrollPane.
545 *
546 * @param e MouseWheelEvent to be handled
547 * @since 1.4
548 */
549 public void mouseWheelMoved(MouseWheelEvent e) {
550 getHandler().mouseWheelMoved(e);
551 }
552 }
553
554 /**
555 * Creates an instance of MouseWheelListener, which is added to the
556 * JScrollPane by installUI(). The returned MouseWheelListener is used
557 * to handle mouse wheel-driven scrolling.
558 *
559 * @return MouseWheelListener which implements wheel-driven scrolling
560 * @see #installUI
561 * @see MouseWheelHandler
562 * @since 1.4
563 */
564 protected MouseWheelListener createMouseWheelListener() {
565 return getHandler();
566 }
567
568 protected void updateScrollBarDisplayPolicy(PropertyChangeEvent e) {
569 scrollpane.revalidate();
570 scrollpane.repaint();
571 }
572
573
574 protected void updateViewport(PropertyChangeEvent e)
575 {
576 JViewport oldViewport = (JViewport)(e.getOldValue());
577 JViewport newViewport = (JViewport)(e.getNewValue());
578
579 if (oldViewport != null) {
580 oldViewport.removeChangeListener(viewportChangeListener);
581 }
582
583 if (newViewport != null) {
584 Point p = newViewport.getViewPosition();
585 if (scrollpane.getComponentOrientation().isLeftToRight()) {
586 p.x = Math.max(p.x, 0);
587 } else {
588 int max = newViewport.getViewSize().width;
589 int extent = newViewport.getExtentSize().width;
590 if (extent > max) {
591 p.x = max - extent;
592 } else {
593 p.x = Math.max(0, Math.min(max - extent, p.x));
594 }
595 }
596 p.y = Math.max(p.y, 0);
597 newViewport.setViewPosition(p);
598 newViewport.addChangeListener(viewportChangeListener);
599 }
600 }
601
602
603 protected void updateRowHeader(PropertyChangeEvent e)
604 {
605 JViewport newRowHead = (JViewport)(e.getNewValue());
606 if (newRowHead != null) {
607 JViewport viewport = scrollpane.getViewport();
608 Point p = newRowHead.getViewPosition();
609 p.y = (viewport != null) ? viewport.getViewPosition().y : 0;
610 newRowHead.setViewPosition(p);
611 }
612 }
613
614
615 protected void updateColumnHeader(PropertyChangeEvent e)
616 {
617 JViewport newColHead = (JViewport)(e.getNewValue());
618 if (newColHead != null) {
619 JViewport viewport = scrollpane.getViewport();
620 Point p = newColHead.getViewPosition();
621 if (viewport == null) {
622 p.x = 0;
623 } else {
624 if (scrollpane.getComponentOrientation().isLeftToRight()) {
625 p.x = viewport.getViewPosition().x;
626 } else {
627 p.x = Math.max(0, viewport.getViewPosition().x);
628 }
629 }
630 newColHead.setViewPosition(p);
631 scrollpane.add(newColHead, COLUMN_HEADER);
632 }
633 }
634
635 private void updateHorizontalScrollBar(PropertyChangeEvent pce) {
636 updateScrollBar(pce, hsbChangeListener, hsbPropertyChangeListener);
637 }
638
639 private void updateVerticalScrollBar(PropertyChangeEvent pce) {
640 updateScrollBar(pce, vsbChangeListener, vsbPropertyChangeListener);
641 }
642
643 private void updateScrollBar(PropertyChangeEvent pce, ChangeListener cl,
644 PropertyChangeListener pcl) {
645 JScrollBar sb = (JScrollBar)pce.getOldValue();
646 if (sb != null) {
647 if (cl != null) {
648 sb.getModel().removeChangeListener(cl);
649 }
650 if (pcl != null) {
651 sb.removePropertyChangeListener(pcl);
652 }
653 }
654 sb = (JScrollBar)pce.getNewValue();
655 if (sb != null) {
656 if (cl != null) {
657 sb.getModel().addChangeListener(cl);
658 }
659 if (pcl != null) {
660 sb.addPropertyChangeListener(pcl);
661 }
662 }
663 }
664
665 public class PropertyChangeHandler implements PropertyChangeListener
666 {
667
668 // NOTE: This class exists only for backward compatability. All
669 // its functionality has been moved into Handler. If you need to add
670 // new functionality add it to the Handler, but make sure this
671 // class calls into the Handler.
672
673 public void propertyChange(PropertyChangeEvent e)
674 {
675 getHandler().propertyChange(e);
676 }
677 }
678
679
680
681 /**
682 * Creates an instance of PropertyChangeListener that's added to
683 * the JScrollPane by installUI(). Subclasses can override this method
684 * to return a custom PropertyChangeListener, e.g.
685 * <pre>
686 * class MyScrollPaneUI extends BasicScrollPaneUI {
687 * protected PropertyChangeListener <b>createPropertyChangeListener</b>() {
688 * return new MyPropertyChangeListener();
689 * }
690 * public class MyPropertyChangeListener extends PropertyChangeListener {
691 * public void propertyChange(PropertyChangeEvent e) {
692 * if (e.getPropertyName().equals("viewport")) {
693 * // do some extra work when the viewport changes
694 * }
695 * super.propertyChange(e);
696 * }
697 * }
698 * }
699 * </pre>
700 *
701 * @see java.beans.PropertyChangeListener
702 * @see #installUI
703 */
704 protected PropertyChangeListener createPropertyChangeListener() {
705 return getHandler();
706 }
707
708
709 private static class Actions extends UIAction {
710 private static final String SCROLL_UP = "scrollUp";
711 private static final String SCROLL_DOWN = "scrollDown";
712 private static final String SCROLL_HOME = "scrollHome";
713 private static final String SCROLL_END = "scrollEnd";
714 private static final String UNIT_SCROLL_UP = "unitScrollUp";
715 private static final String UNIT_SCROLL_DOWN = "unitScrollDown";
716 private static final String SCROLL_LEFT = "scrollLeft";
717 private static final String SCROLL_RIGHT = "scrollRight";
718 private static final String UNIT_SCROLL_LEFT = "unitScrollLeft";
719 private static final String UNIT_SCROLL_RIGHT = "unitScrollRight";
720
721
722 Actions(String key) {
723 super(key);
724 }
725
726 public void actionPerformed(ActionEvent e) {
727 JScrollPane scrollPane = (JScrollPane)e.getSource();
728 boolean ltr = scrollPane.getComponentOrientation().isLeftToRight();
729 String key = getName();
730
731 if (key == SCROLL_UP) {
732 scroll(scrollPane, SwingConstants.VERTICAL, -1, true);
733 }
734 else if (key == SCROLL_DOWN) {
735 scroll(scrollPane, SwingConstants.VERTICAL, 1, true);
736 }
737 else if (key == SCROLL_HOME) {
738 scrollHome(scrollPane);
739 }
740 else if (key == SCROLL_END) {
741 scrollEnd(scrollPane);
742 }
743 else if (key == UNIT_SCROLL_UP) {
744 scroll(scrollPane, SwingConstants.VERTICAL, -1, false);
745 }
746 else if (key == UNIT_SCROLL_DOWN) {
747 scroll(scrollPane, SwingConstants.VERTICAL, 1, false);
748 }
749 else if (key == SCROLL_LEFT) {
750 scroll(scrollPane, SwingConstants.HORIZONTAL, ltr ? -1 : 1,
751 true);
752 }
753 else if (key == SCROLL_RIGHT) {
754 scroll(scrollPane, SwingConstants.HORIZONTAL, ltr ? 1 : -1,
755 true);
756 }
757 else if (key == UNIT_SCROLL_LEFT) {
758 scroll(scrollPane, SwingConstants.HORIZONTAL, ltr ? -1 : 1,
759 false);
760 }
761 else if (key == UNIT_SCROLL_RIGHT) {
762 scroll(scrollPane, SwingConstants.HORIZONTAL, ltr ? 1 : -1,
763 false);
764 }
765 }
766
767 private void scrollEnd(JScrollPane scrollpane) {
768 JViewport vp = scrollpane.getViewport();
769 Component view;
770 if (vp != null && (view = vp.getView()) != null) {
771 Rectangle visRect = vp.getViewRect();
772 Rectangle bounds = view.getBounds();
773 if (scrollpane.getComponentOrientation().isLeftToRight()) {
774 vp.setViewPosition(new Point(bounds.width - visRect.width,
775 bounds.height - visRect.height));
776 } else {
777 vp.setViewPosition(new Point(0,
778 bounds.height - visRect.height));
779 }
780 }
781 }
782
783 private void scrollHome(JScrollPane scrollpane) {
784 JViewport vp = scrollpane.getViewport();
785 Component view;
786 if (vp != null && (view = vp.getView()) != null) {
787 if (scrollpane.getComponentOrientation().isLeftToRight()) {
788 vp.setViewPosition(new Point(0, 0));
789 } else {
790 Rectangle visRect = vp.getViewRect();
791 Rectangle bounds = view.getBounds();
792 vp.setViewPosition(new Point(bounds.width - visRect.width, 0));
793 }
794 }
795 }
796
797 private void scroll(JScrollPane scrollpane, int orientation,
798 int direction, boolean block) {
799 JViewport vp = scrollpane.getViewport();
800 Component view;
801 if (vp != null && (view = vp.getView()) != null) {
802 Rectangle visRect = vp.getViewRect();
803 Dimension vSize = view.getSize();
804 int amount;
805
806 if (view instanceof Scrollable) {
807 if (block) {
808 amount = ((Scrollable)view).getScrollableBlockIncrement
809 (visRect, orientation, direction);
810 }
811 else {
812 amount = ((Scrollable)view).getScrollableUnitIncrement
813 (visRect, orientation, direction);
814 }
815 }
816 else {
817 if (block) {
818 if (orientation == SwingConstants.VERTICAL) {
819 amount = visRect.height;
820 }
821 else {
822 amount = visRect.width;
823 }
824 }
825 else {
826 amount = 10;
827 }
828 }
829 if (orientation == SwingConstants.VERTICAL) {
830 visRect.y += (amount * direction);
831 if ((visRect.y + visRect.height) > vSize.height) {
832 visRect.y = Math.max(0, vSize.height - visRect.height);
833 }
834 else if (visRect.y < 0) {
835 visRect.y = 0;
836 }
837 }
838 else {
839 if (scrollpane.getComponentOrientation().isLeftToRight()) {
840 visRect.x += (amount * direction);
841 if ((visRect.x + visRect.width) > vSize.width) {
842 visRect.x = Math.max(0, vSize.width - visRect.width);
843 } else if (visRect.x < 0) {
844 visRect.x = 0;
845 }
846 } else {
847 visRect.x -= (amount * direction);
848 if (visRect.width > vSize.width) {
849 visRect.x = vSize.width - visRect.width;
850 } else {
851 visRect.x = Math.max(0, Math.min(vSize.width - visRect.width, visRect.x));
852 }
853 }
854 }
855 vp.setViewPosition(visRect.getLocation());
856 }
857 }
858 }
859
860
861 class Handler implements ChangeListener, PropertyChangeListener, MouseWheelListener {
862 //
863 // MouseWheelListener
864 //
865 public void mouseWheelMoved(MouseWheelEvent e) {
866 if (scrollpane.isWheelScrollingEnabled() &&
867 e.getWheelRotation() != 0) {
868 JScrollBar toScroll = scrollpane.getVerticalScrollBar();
869 int direction = e.getWheelRotation() < 0 ? -1 : 1;
870 int orientation = SwingConstants.VERTICAL;
871
872 // find which scrollbar to scroll, or return if none
873 if (toScroll == null || !toScroll.isVisible()) {
874 toScroll = scrollpane.getHorizontalScrollBar();
875 if (toScroll == null || !toScroll.isVisible()) {
876 return;
877 }
878 orientation = SwingConstants.HORIZONTAL;
879 }
880
881 e.consume();
882
883 if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
884 JViewport vp = scrollpane.getViewport();
885 if (vp == null) { return; }
886 Component comp = vp.getView();
887 int units = Math.abs(e.getUnitsToScroll());
888
889 // When the scrolling speed is set to maximum, it's possible
890 // for a single wheel click to scroll by more units than
891 // will fit in the visible area. This makes it
892 // hard/impossible to get to certain parts of the scrolling
893 // Component with the wheel. To make for more accurate
894 // low-speed scrolling, we limit scrolling to the block
895 // increment if the wheel was only rotated one click.
896 boolean limitScroll = Math.abs(e.getWheelRotation()) == 1;
897
898 // Check if we should use the visibleRect trick
899 Object fastWheelScroll = toScroll.getClientProperty(
900 "JScrollBar.fastWheelScrolling");
901 if (Boolean.TRUE == fastWheelScroll &&
902 comp instanceof Scrollable) {
903 // 5078454: Under maximum acceleration, we may scroll
904 // by many 100s of units in ~1 second.
905 //
906 // BasicScrollBarUI.scrollByUnits() can bog down the EDT
907 // with repaints in this situation. However, the
908 // Scrollable interface allows us to pass in an
909 // arbitrary visibleRect. This allows us to accurately
910 // calculate the total scroll amount, and then update
911 // the GUI once. This technique provides much faster
912 // accelerated wheel scrolling.
913 Scrollable scrollComp = (Scrollable) comp;
914 Rectangle viewRect = vp.getViewRect();
915 int startingX = viewRect.x;
916 boolean leftToRight =
917 comp.getComponentOrientation().isLeftToRight();
918 int scrollMin = toScroll.getMinimum();
919 int scrollMax = toScroll.getMaximum() -
920 toScroll.getModel().getExtent();
921
922 if (limitScroll) {
923 int blockIncr =
924 scrollComp.getScrollableBlockIncrement(viewRect,
925 orientation,
926 direction);
927 if (direction < 0) {
928 scrollMin = Math.max(scrollMin,
929 toScroll.getValue() - blockIncr);
930 }
931 else {
932 scrollMax = Math.min(scrollMax,
933 toScroll.getValue() + blockIncr);
934 }
935 }
936
937 for (int i = 0; i < units; i++) {
938 int unitIncr =
939 scrollComp.getScrollableUnitIncrement(viewRect,
940 orientation, direction);
941 // Modify the visible rect for the next unit, and
942 // check to see if we're at the end already.
943 if (orientation == SwingConstants.VERTICAL) {
944 if (direction < 0) {
945 viewRect.y -= unitIncr;
946 if (viewRect.y <= scrollMin) {
947 viewRect.y = scrollMin;
948 break;
949 }
950 }
951 else { // (direction > 0
952 viewRect.y += unitIncr;
953 if (viewRect.y >= scrollMax) {
954 viewRect.y = scrollMax;
955 break;
956 }
957 }
958 }
959 else {
960 // Scroll left
961 if ((leftToRight && direction < 0) ||
962 (!leftToRight && direction > 0)) {
963 viewRect.x -= unitIncr;
964 if (leftToRight) {
965 if (viewRect.x < scrollMin) {
966 viewRect.x = scrollMin;
967 break;
968 }
969 }
970 }
971 // Scroll right
972 else if ((leftToRight && direction > 0) ||
973 (!leftToRight && direction < 0)) {
974 viewRect.x += unitIncr;
975 if (leftToRight) {
976 if (viewRect.x > scrollMax) {
977 viewRect.x = scrollMax;
978 break;
979 }
980 }
981 }
982 else {
983 assert false : "Non-sensical ComponentOrientation / scroll direction";
984 }
985 }
986 }
987 // Set the final view position on the ScrollBar
988 if (orientation == SwingConstants.VERTICAL) {
989 toScroll.setValue(viewRect.y);
990 }
991 else {
992 if (leftToRight) {
993 toScroll.setValue(viewRect.x);
994 }
995 else {
996 // rightToLeft scrollbars are oriented with
997 // minValue on the right and maxValue on the
998 // left.
999 int newPos = toScroll.getValue() -
1000 (viewRect.x - startingX);
1001 if (newPos < scrollMin) {
1002 newPos = scrollMin;
1003 }
1004 else if (newPos > scrollMax) {
1005 newPos = scrollMax;
1006 }
1007 toScroll.setValue(newPos);
1008 }
1009 }
1010 }
1011 else {
1012 // Viewport's view is not a Scrollable, or fast wheel
1013 // scrolling is not enabled.
1014 BasicScrollBarUI.scrollByUnits(toScroll, direction,
1015 units, limitScroll);
1016 }
1017 }
1018 else if (e.getScrollType() ==
1019 MouseWheelEvent.WHEEL_BLOCK_SCROLL) {
1020 BasicScrollBarUI.scrollByBlock(toScroll, direction);
1021 }
1022 }
1023 }
1024
1025 //
1026 // ChangeListener: This is added to the vieport, and hsb/vsb models.
1027 //
1028 public void stateChanged(ChangeEvent e) {
1029 JViewport viewport = scrollpane.getViewport();
1030
1031 if (viewport != null) {
1032 if (e.getSource() == viewport) {
1033 syncScrollPaneWithViewport();
1034 }
1035 else {
1036 JScrollBar hsb = scrollpane.getHorizontalScrollBar();
1037 if (hsb != null && e.getSource() == hsb.getModel()) {
1038 hsbStateChanged(viewport, e);
1039 }
1040 else {
1041 JScrollBar vsb = scrollpane.getVerticalScrollBar();
1042 if (vsb != null && e.getSource() == vsb.getModel()) {
1043 vsbStateChanged(viewport, e);
1044 }
1045 }
1046 }
1047 }
1048 }
1049
1050 private void vsbStateChanged(JViewport viewport, ChangeEvent e) {
1051 BoundedRangeModel model = (BoundedRangeModel)(e.getSource());
1052 Point p = viewport.getViewPosition();
1053 p.y = model.getValue();
1054 viewport.setViewPosition(p);
1055 }
1056
1057 private void hsbStateChanged(JViewport viewport, ChangeEvent e) {
1058 BoundedRangeModel model = (BoundedRangeModel)(e.getSource());
1059 Point p = viewport.getViewPosition();
1060 int value = model.getValue();
1061 if (scrollpane.getComponentOrientation().isLeftToRight()) {
1062 p.x = value;
1063 } else {
1064 int max = viewport.getViewSize().width;
1065 int extent = viewport.getExtentSize().width;
1066 int oldX = p.x;
1067
1068 /* Set new X coordinate based on "value".
1069 */
1070 p.x = max - extent - value;
1071
1072 /* If setValue() was called before "extent" was fixed,
1073 * turn setValueCalled flag on.
1074 */
1075 if ((extent == 0) && (value != 0) && (oldX == max)) {
1076 setValueCalled = true;
1077 } else {
1078 /* When a pane without a horizontal scroll bar was
1079 * reduced and the bar appeared, the viewport should
1080 * show the right side of the view.
1081 */
1082 if ((extent != 0) && (oldX < 0) && (p.x == 0)) {
1083 p.x += value;
1084 }
1085 }
1086 }
1087 viewport.setViewPosition(p);
1088 }
1089
1090 //
1091 // PropertyChangeListener: This is installed on both the JScrollPane
1092 // and the horizontal/vertical scrollbars.
1093 //
1094
1095 // Listens for changes in the model property and reinstalls the
1096 // horizontal/vertical PropertyChangeListeners.
1097 public void propertyChange(PropertyChangeEvent e) {
1098 if (e.getSource() == scrollpane) {
1099 scrollPanePropertyChange(e);
1100 }
1101 else {
1102 sbPropertyChange(e);
1103 }
1104 }
1105
1106 private void scrollPanePropertyChange(PropertyChangeEvent e) {
1107 String propertyName = e.getPropertyName();
1108
1109 if (propertyName == "verticalScrollBarDisplayPolicy") {
1110 updateScrollBarDisplayPolicy(e);
1111 }
1112 else if (propertyName == "horizontalScrollBarDisplayPolicy") {
1113 updateScrollBarDisplayPolicy(e);
1114 }
1115 else if (propertyName == "viewport") {
1116 updateViewport(e);
1117 }
1118 else if (propertyName == "rowHeader") {
1119 updateRowHeader(e);
1120 }
1121 else if (propertyName == "columnHeader") {
1122 updateColumnHeader(e);
1123 }
1124 else if (propertyName == "verticalScrollBar") {
1125 updateVerticalScrollBar(e);
1126 }
1127 else if (propertyName == "horizontalScrollBar") {
1128 updateHorizontalScrollBar(e);
1129 }
1130 else if (propertyName == "componentOrientation") {
1131 scrollpane.revalidate();
1132 scrollpane.repaint();
1133 }
1134 }
1135
1136 // PropertyChangeListener for the horizontal and vertical scrollbars.
1137 private void sbPropertyChange(PropertyChangeEvent e) {
1138 String propertyName = e.getPropertyName();
1139 Object source = e.getSource();
1140
1141 if ("model" == propertyName) {
1142 JScrollBar sb = scrollpane.getVerticalScrollBar();
1143 BoundedRangeModel oldModel = (BoundedRangeModel)e.
1144 getOldValue();
1145 ChangeListener cl = null;
1146
1147 if (source == sb) {
1148 cl = vsbChangeListener;
1149 }
1150 else if (source == scrollpane.getHorizontalScrollBar()) {
1151 sb = scrollpane.getHorizontalScrollBar();
1152 cl = hsbChangeListener;
1153 }
1154 if (cl != null) {
1155 if (oldModel != null) {
1156 oldModel.removeChangeListener(cl);
1157 }
1158 if (sb.getModel() != null) {
1159 sb.getModel().addChangeListener(cl);
1160 }
1161 }
1162 }
1163 else if ("componentOrientation" == propertyName) {
1164 if (source == scrollpane.getHorizontalScrollBar()) {
1165 JScrollBar hsb = scrollpane.getHorizontalScrollBar();
1166 JViewport viewport = scrollpane.getViewport();
1167 Point p = viewport.getViewPosition();
1168 if (scrollpane.getComponentOrientation().isLeftToRight()) {
1169 p.x = hsb.getValue();
1170 } else {
1171 p.x = viewport.getViewSize().width - viewport.getExtentSize().width - hsb.getValue();
1172 }
1173 viewport.setViewPosition(p);
1174 }
1175 }
1176 }
1177 }
1178}