Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / javax / swing / plaf / basic / BasicScrollPaneUI.java
blobe6a4eaf4fc1a77e955cabe57ddaf221cfb4036f7
1 /* BasicScrollPaneUI.java
2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax.swing.plaf.basic;
41 import gnu.classpath.NotImplementedException;
43 import java.awt.Component;
44 import java.awt.Dimension;
45 import java.awt.Graphics;
46 import java.awt.Point;
47 import java.awt.Rectangle;
48 import java.awt.event.ContainerEvent;
49 import java.awt.event.ContainerListener;
50 import java.awt.event.MouseWheelEvent;
51 import java.awt.event.MouseWheelListener;
52 import java.beans.PropertyChangeEvent;
53 import java.beans.PropertyChangeListener;
55 import javax.swing.JComponent;
56 import javax.swing.JScrollBar;
57 import javax.swing.JScrollPane;
58 import javax.swing.JViewport;
59 import javax.swing.LookAndFeel;
60 import javax.swing.ScrollPaneConstants;
61 import javax.swing.ScrollPaneLayout;
62 import javax.swing.Scrollable;
63 import javax.swing.SwingConstants;
64 import javax.swing.event.ChangeEvent;
65 import javax.swing.event.ChangeListener;
66 import javax.swing.plaf.ComponentUI;
67 import javax.swing.plaf.ScrollPaneUI;
69 public class BasicScrollPaneUI extends ScrollPaneUI
70 implements ScrollPaneConstants
73 /**
74 * Listens for changes in the state of the horizontal scrollbar's model and
75 * updates the scrollpane accordingly.
77 * @author Roman Kennke (kennke@aicas.com)
79 public class HSBChangeListener implements ChangeListener
82 /**
83 * Receives notification when the state of the horizontal scrollbar
84 * model has changed.
86 * @param event the change event
88 public void stateChanged(ChangeEvent event)
90 JScrollBar hsb = scrollpane.getHorizontalScrollBar();
91 JViewport vp = scrollpane.getViewport();
92 Point viewPosition = vp.getViewPosition();
93 int xpos = hsb.getValue();
95 if (xpos != viewPosition.x)
97 viewPosition.x = xpos;
98 vp.setViewPosition(viewPosition);
101 viewPosition.y = 0;
102 JViewport columnHeader = scrollpane.getColumnHeader();
103 if (columnHeader != null
104 && !columnHeader.getViewPosition().equals(viewPosition))
105 columnHeader.setViewPosition(viewPosition);
111 * Listens for changes in the state of the vertical scrollbar's model and
112 * updates the scrollpane accordingly.
114 * @author Roman Kennke (kennke@aicas.com)
116 public class VSBChangeListener implements ChangeListener
120 * Receives notification when the state of the vertical scrollbar
121 * model has changed.
123 * @param event the change event
125 public void stateChanged(ChangeEvent event)
127 JScrollBar vsb = scrollpane.getVerticalScrollBar();
128 JViewport vp = scrollpane.getViewport();
129 Point viewPosition = vp.getViewPosition();
130 int ypos = vsb.getValue();
131 if (ypos != viewPosition.y)
133 viewPosition.y = ypos;
134 vp.setViewPosition(viewPosition);
137 viewPosition.x = 0;
138 JViewport rowHeader = scrollpane.getRowHeader();
139 if (rowHeader != null
140 && !rowHeader.getViewPosition().equals(viewPosition))
141 rowHeader.setViewPosition(viewPosition);
147 * Listens for changes of the viewport's extent size and updates the
148 * scrollpane accordingly.
150 * @author Roman Kennke (kennke@aicas.com)
152 public class ViewportChangeHandler implements ChangeListener
156 * Receives notification when the view's size, position or extent size
157 * changes. When the extents size has changed, this method calls
158 * {@link BasicScrollPaneUI#syncScrollPaneWithViewport()} to adjust the
159 * scrollbars extents as well.
161 * @param event the change event
163 public void stateChanged(ChangeEvent event)
165 JViewport vp = scrollpane.getViewport();
166 JScrollBar hsb = scrollpane.getHorizontalScrollBar();
167 JScrollBar vsb = scrollpane.getVerticalScrollBar();
168 syncScrollPaneWithViewport();
174 * Listens for property changes on the scrollpane and update the view
175 * accordingly.
177 * @author Roman Kennke (kennke@aicas.com)
179 public class PropertyChangeHandler implements PropertyChangeListener
183 * Receives notification when any of the scrollpane's bound property
184 * changes. This method calls the appropriate update method on the
185 * <code>ScrollBarUI</code>.
187 * @param e the property change event
189 * @see BasicScrollPaneUI#updateColumnHeader(PropertyChangeEvent)
190 * @see BasicScrollPaneUI#updateRowHeader(PropertyChangeEvent)
191 * @see BasicScrollPaneUI#updateScrollBarDisplayPolicy(PropertyChangeEvent)
192 * @see BasicScrollPaneUI#updateViewport(PropertyChangeEvent)
194 public void propertyChange(PropertyChangeEvent e)
196 String propName = e.getPropertyName();
197 if (propName.equals("viewport"))
198 updateViewport(e);
199 else if (propName.equals("rowHeader"))
200 updateRowHeader(e);
201 else if (propName.equals("columnHeader"))
202 updateColumnHeader(e);
203 else if (propName.equals("horizontalScrollBarPolicy")
204 || e.getPropertyName().equals("verticalScrollBarPolicy"))
205 updateScrollBarDisplayPolicy(e);
206 else if (propName.equals("verticalScrollBar"))
208 JScrollBar oldSb = (JScrollBar) e.getOldValue();
209 oldSb.getModel().removeChangeListener(vsbChangeListener);
210 JScrollBar newSb = (JScrollBar) e.getNewValue();
211 newSb.getModel().addChangeListener(vsbChangeListener);
213 else if (propName.equals("horizontalScrollBar"))
215 JScrollBar oldSb = (JScrollBar) e.getOldValue();
216 oldSb.getModel().removeChangeListener(hsbChangeListener);
217 JScrollBar newSb = (JScrollBar) e.getNewValue();
218 newSb.getModel().addChangeListener(hsbChangeListener);
225 * Listens for mouse wheel events and update the scrollpane accordingly.
227 * @author Roman Kennke (kennke@aicas.com)
229 * @since 1.4
231 protected class MouseWheelHandler implements MouseWheelListener
234 * Use to compute the visible rectangle.
236 final Rectangle rect = new Rectangle();
239 * Scroll with the mouse whell.
241 * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
243 public void mouseWheelMoved(MouseWheelEvent e)
245 if (scrollpane.getViewport().getComponentCount() == 0)
246 return;
248 Component target = scrollpane.getViewport().getComponent(0);
249 JScrollBar bar = scrollpane.getVerticalScrollBar();
250 Scrollable scrollable = (target instanceof Scrollable) ? (Scrollable) target
251 : null;
253 boolean tracksHeight = scrollable != null
254 && scrollable.getScrollableTracksViewportHeight();
255 int wheel = e.getWheelRotation() * ROWS_PER_WHEEL_CLICK;
256 int delta;
258 // If possible, scroll vertically.
259 if (bar != null && ! tracksHeight)
261 if (scrollable != null)
263 bounds(target);
264 delta = scrollable.getScrollableUnitIncrement(
265 rect, SwingConstants.VERTICAL, wheel);
267 else
269 // Scroll non scrollables.
270 delta = wheel * SCROLL_NON_SCROLLABLES;
272 scroll(bar, delta);
274 // If not, try to scroll horizontally
275 else
277 bar = scrollpane.getHorizontalScrollBar();
278 boolean tracksWidth = scrollable != null
279 && scrollable.getScrollableTracksViewportWidth();
281 if (bar != null && ! tracksWidth)
283 if (scrollable != null)
285 bounds(target);
286 delta = scrollable.getScrollableUnitIncrement(
287 rect, SwingConstants.HORIZONTAL, wheel);
289 else
291 // Scroll non scrollables.
292 delta = wheel * SCROLL_NON_SCROLLABLES;
294 scroll(bar, delta);
300 * Place the component bounds into rect. The x and y values
301 * need to be reversed.
303 * @param target the target being scrolled
305 final void bounds(Component target)
307 // Viewport bounds, translated by the scroll bar positions.
308 target.getParent().getBounds(rect);
309 rect.x = getValue(scrollpane.getHorizontalScrollBar());
310 rect.y = getValue(scrollpane.getVerticalScrollBar());
314 * Get the scroll bar value or null if there is no such scroll bar.
316 final int getValue(JScrollBar bar)
318 return bar != null ? bar.getValue() : 0;
322 * Scroll the given distance.
324 * @param bar the scrollbar to scroll
325 * @param delta the distance
327 final void scroll(JScrollBar bar, int delta)
329 int y = bar.getValue() + delta;
331 if (y < bar.getMinimum())
332 y = bar.getMinimum();
333 if (y > bar.getMaximum())
334 y = bar.getMaximum();
336 bar.setValue(y);
341 * Adds/removes the mouse wheel listener when the component is added/removed
342 * to/from the scroll pane view port.
344 * @author Audrius Meskauskas (audriusa@bioinformatics.org)
346 class ViewportContainerListener implements ContainerListener
349 * Add the mouse wheel listener, allowing to scroll with the mouse.
351 public void componentAdded(ContainerEvent e)
353 e.getChild().addMouseWheelListener(mouseWheelListener);
357 * Remove the mouse wheel listener.
359 public void componentRemoved(ContainerEvent e)
361 e.getChild().removeMouseWheelListener(mouseWheelListener);
366 * The number of pixels by that we should scroll the content that does
367 * not implement Scrollable.
369 static int SCROLL_NON_SCROLLABLES = 10;
372 * The number of rows to scroll per mouse wheel click. From impression,
373 * Sun seems using the value 3.
375 static int ROWS_PER_WHEEL_CLICK = 3;
377 /** The Scrollpane for which the UI is provided by this class. */
378 protected JScrollPane scrollpane;
381 * The horizontal scrollbar listener.
383 protected ChangeListener hsbChangeListener;
386 * The vertical scrollbar listener.
388 protected ChangeListener vsbChangeListener;
391 * The viewport listener.
393 protected ChangeListener viewportChangeListener;
396 * The scrollpane property change listener.
398 protected PropertyChangeListener spPropertyChangeListener;
401 * The mousewheel listener for the scrollpane.
403 MouseWheelListener mouseWheelListener;
406 * The listener to add and remove the mouse wheel listener to/from
407 * the component container.
409 ContainerListener containerListener;
411 public static ComponentUI createUI(final JComponent c)
413 return new BasicScrollPaneUI();
416 protected void installDefaults(JScrollPane p)
418 scrollpane = p;
419 LookAndFeel.installColorsAndFont(p, "ScrollPane.background",
420 "ScrollPane.foreground",
421 "ScrollPane.font");
422 LookAndFeel.installBorder(p, "ScrollPane.border");
423 p.setOpaque(true);
426 protected void uninstallDefaults(JScrollPane p)
428 p.setForeground(null);
429 p.setBackground(null);
430 p.setFont(null);
431 p.setBorder(null);
432 scrollpane = null;
435 public void installUI(final JComponent c)
437 super.installUI(c);
438 installDefaults((JScrollPane) c);
439 installListeners((JScrollPane) c);
440 installKeyboardActions((JScrollPane) c);
444 * Installs the listeners on the scrollbars, the viewport and the scrollpane.
446 * @param sp the scrollpane on which to install the listeners
448 protected void installListeners(JScrollPane sp)
450 if (spPropertyChangeListener == null)
451 spPropertyChangeListener = createPropertyChangeListener();
452 sp.addPropertyChangeListener(spPropertyChangeListener);
454 if (hsbChangeListener == null)
455 hsbChangeListener = createHSBChangeListener();
456 sp.getHorizontalScrollBar().getModel().addChangeListener(hsbChangeListener);
458 if (vsbChangeListener == null)
459 vsbChangeListener = createVSBChangeListener();
460 sp.getVerticalScrollBar().getModel().addChangeListener(vsbChangeListener);
462 if (viewportChangeListener == null)
463 viewportChangeListener = createViewportChangeListener();
465 if (mouseWheelListener == null)
466 mouseWheelListener = createMouseWheelListener();
468 if (containerListener == null)
469 containerListener = new ViewportContainerListener();
471 JViewport v = sp.getViewport();
472 v.addChangeListener(viewportChangeListener);
473 v.addContainerListener(containerListener);
475 // Add mouse wheel listeners to the componets that are probably already
476 // in the view port.
477 for (int i = 0; i < v.getComponentCount(); i++)
478 v.getComponent(i).addMouseWheelListener(mouseWheelListener);
482 * Installs additional keyboard actions on the scrollpane. This is a hook
483 * method provided to subclasses in order to install their own keyboard
484 * actions.
486 * @param sp the scrollpane to install keyboard actions on
488 protected void installKeyboardActions(JScrollPane sp)
489 throws NotImplementedException
491 // TODO: Is this only a hook method or should we actually do something
492 // here? If the latter, than figure out what and implement this.
496 * Creates and returns the change listener for the horizontal scrollbar.
498 * @return the change listener for the horizontal scrollbar
500 protected ChangeListener createHSBChangeListener()
502 return new HSBChangeListener();
506 * Creates and returns the change listener for the vertical scrollbar.
508 * @return the change listener for the vertical scrollbar
510 protected ChangeListener createVSBChangeListener()
512 return new VSBChangeListener();
516 * Creates and returns the change listener for the viewport.
518 * @return the change listener for the viewport
520 protected ChangeListener createViewportChangeListener()
522 return new ViewportChangeHandler();
526 * Creates and returns the property change listener for the scrollpane.
528 * @return the property change listener for the scrollpane
530 protected PropertyChangeListener createPropertyChangeListener()
532 return new PropertyChangeHandler();
536 * Creates and returns the mouse wheel listener for the scrollpane.
538 * @return the mouse wheel listener for the scrollpane
540 protected MouseWheelListener createMouseWheelListener()
542 return new MouseWheelHandler();
545 public void uninstallUI(final JComponent c)
547 super.uninstallUI(c);
548 this.uninstallDefaults((JScrollPane)c);
549 uninstallListeners((JScrollPane) c);
550 installKeyboardActions((JScrollPane) c);
554 * Uninstalls all the listeners that have been installed in
555 * {@link #installListeners(JScrollPane)}.
557 * @param c the scrollpane from which to uninstall the listeners
559 protected void uninstallListeners(JComponent c)
561 JScrollPane sp = (JScrollPane) c;
562 sp.removePropertyChangeListener(spPropertyChangeListener);
563 sp.getHorizontalScrollBar().getModel()
564 .removeChangeListener(hsbChangeListener);
565 sp.getVerticalScrollBar().getModel()
566 .removeChangeListener(vsbChangeListener);
568 JViewport v = sp.getViewport();
569 v.removeChangeListener(viewportChangeListener);
570 v.removeContainerListener(containerListener);
572 for (int i = 0; i < v.getComponentCount(); i++)
573 v.getComponent(i).removeMouseWheelListener(mouseWheelListener);
578 * Uninstalls all keyboard actions from the JScrollPane that have been
579 * installed by {@link #installKeyboardActions}. This is a hook method
580 * provided to subclasses to add their own keyboard actions.
582 * @param sp the scrollpane to uninstall keyboard actions from
584 protected void uninstallKeyboardActions(JScrollPane sp)
585 throws NotImplementedException
587 // TODO: Is this only a hook method or should we actually do something
588 // here? If the latter, than figure out what and implement this.
591 public Dimension getMinimumSize(JComponent c)
593 JScrollPane p = (JScrollPane ) c;
594 ScrollPaneLayout sl = (ScrollPaneLayout) p.getLayout();
595 return sl.minimumLayoutSize(c);
598 public void paint(Graphics g, JComponent c)
600 // do nothing; the normal painting-of-children algorithm, along with
601 // ScrollPaneLayout, does all the relevant work.
605 * Synchronizes the scrollbars with the viewport's extents.
607 protected void syncScrollPaneWithViewport()
609 JViewport vp = scrollpane.getViewport();
611 // Update the horizontal scrollbar.
612 JScrollBar hsb = scrollpane.getHorizontalScrollBar();
613 hsb.setMaximum(vp.getViewSize().width);
614 hsb.setValue(vp.getViewPosition().x);
615 hsb.setVisibleAmount(vp.getExtentSize().width);
617 // Update the vertical scrollbar.
618 JScrollBar vsb = scrollpane.getVerticalScrollBar();
619 vsb.setMaximum(vp.getViewSize().height);
620 vsb.setValue(vp.getViewPosition().y);
621 vsb.setVisibleAmount(vp.getExtentSize().height);
625 * Receives notification when the <code>columnHeader</code> property has
626 * changed on the scrollpane.
628 * @param ev the property change event
630 protected void updateColumnHeader(PropertyChangeEvent ev)
632 // TODO: Find out what should be done here. Or is this only a hook?
636 * Receives notification when the <code>rowHeader</code> property has changed
637 * on the scrollpane.
639 * @param ev the property change event
641 protected void updateRowHeader(PropertyChangeEvent ev)
643 // TODO: Find out what should be done here. Or is this only a hook?
647 * Receives notification when the <code>scrollBarDisplayPolicy</code>
648 * property has changed on the scrollpane.
650 * @param ev the property change event
652 protected void updateScrollBarDisplayPolicy(PropertyChangeEvent ev)
654 // TODO: Find out what should be done here. Or is this only a hook?
658 * Receives notification when the <code>viewport</code> property has changed
659 * on the scrollpane.
661 * This method sets removes the viewportChangeListener from the old viewport
662 * and adds it to the new viewport.
664 * @param ev the property change event
666 protected void updateViewport(PropertyChangeEvent ev)
668 JViewport oldViewport = (JViewport) ev.getOldValue();
669 oldViewport.removeChangeListener(viewportChangeListener);
670 JViewport newViewport = (JViewport) ev.getNewValue();
671 newViewport.addChangeListener(viewportChangeListener);
672 syncScrollPaneWithViewport();