org.apache.pivot.wtk.skin.ScrollPaneSkin Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.pivot.wtk.skin;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Transparency;
import org.apache.pivot.wtk.ApplicationContext;
import org.apache.pivot.wtk.Bounds;
import org.apache.pivot.wtk.Component;
import org.apache.pivot.wtk.DesktopApplicationContext;
import org.apache.pivot.wtk.Dimensions;
import org.apache.pivot.wtk.Keyboard;
import org.apache.pivot.wtk.Keyboard.KeyCode;
import org.apache.pivot.wtk.Mouse;
import org.apache.pivot.wtk.Orientation;
import org.apache.pivot.wtk.ScrollBar;
import org.apache.pivot.wtk.ScrollBarValueListener;
import org.apache.pivot.wtk.ScrollPane;
import org.apache.pivot.wtk.ScrollPane.Corner;
import org.apache.pivot.wtk.ScrollPane.ScrollBarPolicy;
import org.apache.pivot.wtk.ScrollPaneListener;
import org.apache.pivot.wtk.Viewport;
import org.apache.pivot.wtk.ViewportListener;
/**
* Scroll pane skin.
*/
public class ScrollPaneSkin extends ContainerSkin
implements Viewport.Skin, ScrollPaneListener, ViewportListener,
ScrollBarValueListener {
private ScrollBar horizontalScrollBar = new ScrollBar(Orientation.HORIZONTAL);
private ScrollBar verticalScrollBar = new ScrollBar(Orientation.VERTICAL);
private Corner topLeftCorner = new Corner(Corner.Placement.TOP_LEFT);
private Corner bottomLeftCorner = new Corner(Corner.Placement.BOTTOM_LEFT);
private Corner bottomRightCorner = new Corner(Corner.Placement.BOTTOM_RIGHT);
private Corner topRightCorner = new Corner(Corner.Placement.TOP_RIGHT);
private int horizontalReveal = 30;
private int verticalReveal = 30;
private int cachedHorizontalScrollBarHeight = 0;
private int cachedVerticalScrollBarWidth = 0;
private boolean optimizeScrolling = true;
private static final int DEFAULT_HORIZONTAL_INCREMENT = 10;
private static final int DEFAULT_VERTICAL_INCREMENT = 10;
public ScrollPaneSkin() {
setBackgroundPaint(Color.WHITE);
horizontalScrollBar.setUnitIncrement(DEFAULT_HORIZONTAL_INCREMENT);
verticalScrollBar.setUnitIncrement(DEFAULT_VERTICAL_INCREMENT);
}
@Override
public void install(Component component) {
super.install(component);
ScrollPane scrollPane = (ScrollPane)component;
scrollPane.getViewportListeners().add(this);
scrollPane.getScrollPaneListeners().add(this);
scrollPane.add(horizontalScrollBar);
scrollPane.add(verticalScrollBar);
scrollPane.add(topLeftCorner);
scrollPane.add(bottomLeftCorner);
scrollPane.add(bottomRightCorner);
scrollPane.add(topRightCorner);
horizontalScrollBar.getScrollBarValueListeners().add(this);
verticalScrollBar.getScrollBarValueListeners().add(this);
}
@Override
public int getPreferredWidth(int height) {
int preferredWidth = 0;
ScrollPane scrollPane = (ScrollPane)getComponent();
Component view = scrollPane.getView();
if (view != null) {
int preferredRowHeaderWidth = 0;
Component rowHeader = scrollPane.getRowHeader();
if (rowHeader != null) {
preferredRowHeaderWidth = rowHeader.getPreferredWidth(-1);
}
int preferredColumnHeaderHeight = 0;
Component columnHeader = scrollPane.getColumnHeader();
if (columnHeader != null) {
preferredColumnHeaderHeight = columnHeader.getPreferredHeight(-1);
}
ScrollBarPolicy verticalPolicy = scrollPane.getVerticalScrollBarPolicy();
if (verticalPolicy != ScrollBarPolicy.FILL) {
// Get the unconstrained preferred size of the view
Dimensions preferredViewSize = view.getPreferredSize();
// If the policy is FILL_TO_CAPACITY, and the sum of the
// unconstrained preferred heights of the view and the column
// header is less than the height constraint, apply the FILL
// policy; otherwise, apply the AUTO policy
if (verticalPolicy == ScrollBarPolicy.FILL_TO_CAPACITY) {
if (height < 0) {
verticalPolicy = ScrollBarPolicy.AUTO;
} else {
int preferredHeight = preferredViewSize.height +
preferredColumnHeaderHeight;
if (preferredHeight < height) {
verticalPolicy = ScrollBarPolicy.FILL;
} else {
verticalPolicy = ScrollBarPolicy.AUTO;
}
}
}
// If the policy is ALWAYS, NEVER, or AUTO, the preferred
// width is the sum of the unconstrained preferred widths of
// the view and row header, plus the width of the scroll
// bar if policy is ALWAYS or if the view's preferred height is
// greater than the height constraint and the policy is AUTO
if (verticalPolicy == ScrollBarPolicy.ALWAYS
|| verticalPolicy == ScrollBarPolicy.NEVER
|| verticalPolicy == ScrollBarPolicy.AUTO) {
preferredWidth = preferredViewSize.width +
preferredRowHeaderWidth;
// If the sum of the preferred heights of the view and the
// column header is greater than the height constraint,
// include the preferred width of the scroll bar in the
// preferred width calculation
if (verticalPolicy == ScrollBarPolicy.ALWAYS
|| (verticalPolicy == ScrollBarPolicy.AUTO
&& height > 0
&& preferredViewSize.height + preferredColumnHeaderHeight > height)) {
preferredWidth += verticalScrollBar.getPreferredWidth(-1);
}
}
}
if (verticalPolicy == ScrollBarPolicy.FILL) {
// Preferred width is the sum of the constrained preferred
// width of the view and the unconstrained preferred width of
// the row header
int heightUpdated = height;
if (heightUpdated >= 0) {
// Subtract the unconstrained preferred height of the
// column header from the height constraint
heightUpdated = Math.max(heightUpdated - preferredColumnHeaderHeight, 0);
}
preferredWidth = view.getPreferredWidth(heightUpdated) +
preferredRowHeaderWidth;
}
}
return preferredWidth;
}
@Override
public int getPreferredHeight(int width) {
int preferredHeight = 0;
ScrollPane scrollPane = (ScrollPane)getComponent();
Component view = scrollPane.getView();
if (view != null) {
int preferredRowHeaderWidth = 0;
Component rowHeader = scrollPane.getRowHeader();
if (rowHeader != null) {
preferredRowHeaderWidth = rowHeader.getPreferredWidth(-1);
}
int preferredColumnHeaderHeight = 0;
Component columnHeader = scrollPane.getColumnHeader();
if (columnHeader != null) {
preferredColumnHeaderHeight = columnHeader.getPreferredHeight(-1);
}
ScrollBarPolicy horizontalPolicy = scrollPane.getHorizontalScrollBarPolicy();
if (horizontalPolicy != ScrollBarPolicy.FILL) {
// Get the unconstrained preferred size of the view
Dimensions preferredViewSize = view.getPreferredSize();
// If the policy is FILL_TO_CAPACITY, and the sum of the
// unconstrained preferred widths of the view and the row
// header is less than the width constraint, apply the FILL
// policy; otherwise, apply the AUTO policy
if (horizontalPolicy == ScrollBarPolicy.FILL_TO_CAPACITY) {
if (width < 0) {
horizontalPolicy = ScrollBarPolicy.AUTO;
} else {
int preferredWidth = preferredViewSize.width +
preferredRowHeaderWidth;
if (preferredWidth < width) {
horizontalPolicy = ScrollBarPolicy.FILL;
} else {
horizontalPolicy = ScrollBarPolicy.AUTO;
}
}
}
// If the policy is ALWAYS, NEVER, or AUTO, the preferred
// height is the sum of the unconstrained preferred heights of
// the view and column header, plus the height of the scroll
// bar if policy is ALWAYS or if the view's preferred width is
// greater than the width constraint and the policy is AUTO
if (horizontalPolicy == ScrollBarPolicy.ALWAYS
|| horizontalPolicy == ScrollBarPolicy.NEVER
|| horizontalPolicy == ScrollBarPolicy.AUTO) {
preferredHeight = preferredViewSize.height +
preferredColumnHeaderHeight;
// If the sum of the preferred widths of the view and the
// row header is greater than the width constraint, include
// the preferred height of the scroll bar in the preferred
// height calculation
if (horizontalPolicy == ScrollBarPolicy.ALWAYS
|| (horizontalPolicy == ScrollBarPolicy.AUTO
&& width > 0
&& preferredViewSize.width + preferredRowHeaderWidth > width)) {
preferredHeight += horizontalScrollBar.getPreferredHeight(-1);
}
}
}
if (horizontalPolicy == ScrollBarPolicy.FILL) {
// Preferred height is the sum of the constrained preferred height
// of the view and the unconstrained preferred height of the column
// header
int widthUpdated = width;
if (widthUpdated >= 0) {
// Subtract the unconstrained preferred width of the row header
// from the width constraint
widthUpdated = Math.max(widthUpdated - preferredRowHeaderWidth, 0);
}
preferredHeight = view.getPreferredHeight(widthUpdated) +
preferredColumnHeaderHeight;
}
}
return preferredHeight;
}
@Override
public Dimensions getPreferredSize() {
ScrollPane scrollPane = (ScrollPane)getComponent();
int preferredWidth = 0;
int preferredHeight = 0;
Component view = scrollPane.getView();
if (view != null) {
Dimensions preferredViewSize = view.getPreferredSize();
preferredWidth += preferredViewSize.width;
preferredHeight += preferredViewSize.height;
Component rowHeader = scrollPane.getRowHeader();
if (rowHeader != null) {
preferredWidth += rowHeader.getPreferredWidth(-1);
}
Component columnHeader = scrollPane.getColumnHeader();
if (columnHeader != null) {
preferredHeight += columnHeader.getPreferredHeight(-1);
}
if (scrollPane.getHorizontalScrollBarPolicy() == ScrollBarPolicy.ALWAYS) {
preferredHeight += horizontalScrollBar.getPreferredHeight(-1);
}
if (scrollPane.getVerticalScrollBarPolicy() == ScrollBarPolicy.ALWAYS) {
preferredWidth += verticalScrollBar.getPreferredWidth(-1);
}
}
return new Dimensions(preferredWidth, preferredHeight);
}
@Override
public int getBaseline(int width, int height) {
ScrollPane scrollPane = (ScrollPane)getComponent();
Component view = scrollPane.getView();
Component rowHeader = scrollPane.getRowHeader();
Component columnHeader = scrollPane.getColumnHeader();
int baseline = -1;
int clientWidth = width;
int clientHeight = height;
int rowHeaderWidth = 0;
if (rowHeader != null) {
rowHeaderWidth = rowHeader.getPreferredWidth(-1);
clientWidth -= rowHeaderWidth;
}
int columnHeaderHeight = 0;
if (columnHeader != null) {
columnHeaderHeight = columnHeader.getPreferredHeight(-1);
clientHeight -= columnHeaderHeight;
baseline = columnHeader.getBaseline(clientWidth, columnHeaderHeight);
}
if (baseline == -1
&& rowHeader != null) {
baseline = rowHeader.getBaseline(rowHeaderWidth, clientHeight);
if (baseline != -1) {
baseline += columnHeaderHeight;
}
}
if (baseline == -1
&& view != null) {
baseline = view.getBaseline(clientWidth, clientHeight);
if (baseline != -1) {
baseline += columnHeaderHeight;
}
}
return baseline;
}
@Override
public boolean mouseWheel(Component component, Mouse.ScrollType scrollType, int scrollAmount,
int wheelRotation, int x, int y) {
boolean consumed = false;
ScrollPane scrollPane = (ScrollPane)getComponent();
Component view = scrollPane.getView();
if (view != null) {
// The scroll orientation is tied to whether the shift key was
// pressed while the mouse wheel was scrolled
if (Keyboard.isPressed(Keyboard.Modifier.SHIFT)) {
// Treat the mouse wheel as a horizontal scroll event
int previousScrollLeft = scrollPane.getScrollLeft();
int newScrollLeft = previousScrollLeft + (scrollAmount * wheelRotation *
horizontalScrollBar.getUnitIncrement());
if (wheelRotation > 0) {
int maxScrollLeft = getMaxScrollLeft();
newScrollLeft = Math.min(newScrollLeft, maxScrollLeft);
if (previousScrollLeft < maxScrollLeft) {
consumed = true;
}
} else {
newScrollLeft = Math.max(newScrollLeft, 0);
if (previousScrollLeft > 0) {
consumed = true;
}
}
scrollPane.setScrollLeft(newScrollLeft);
} else {
// Treat the mouse wheel as a vertical scroll event
int previousScrollTop = scrollPane.getScrollTop();
int newScrollTop = previousScrollTop + (scrollAmount * wheelRotation *
verticalScrollBar.getUnitIncrement());
if (wheelRotation > 0) {
int maxScrollTop = getMaxScrollTop();
newScrollTop = Math.min(newScrollTop, maxScrollTop);
if (previousScrollTop < maxScrollTop) {
consumed = true;
}
} else {
newScrollTop = Math.max(newScrollTop, 0);
if (previousScrollTop > 0) {
consumed = true;
}
}
scrollPane.setScrollTop(newScrollTop);
}
}
return consumed;
}
/**
* Key presses have no effect if the event has already been consumed.
* {@link KeyCode#UP UP} Scroll up a single scroll unit.
* {@link KeyCode#DOWN DOWN} Scroll down a single scroll unit.
* {@link KeyCode#LEFT LEFT} Scroll left a single scroll unit.
* {@link KeyCode#RIGHT RIGHT} Scroll right a single scroll unit.
* {@link KeyCode#PAGE_UP PAGE_UP} Scroll up a single scroll block.
* {@link KeyCode#PAGE_DOWN PAGE_DOWN} Scroll down a single scroll block.
*
* @see ScrollBar#getBlockIncrement()
* @see ScrollBar#getUnitIncrement()
*/
@Override
public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
boolean consumed = super.keyPressed(component, keyCode, keyLocation);
if (!consumed) {
ScrollPane scrollPane = (ScrollPane)getComponent();
int scrollTop = scrollPane.getScrollTop();
int scrollLeft = scrollPane.getScrollLeft();
if (keyCode == Keyboard.KeyCode.UP) {
int newScrollTop = Math.max(scrollTop -
verticalScrollBar.getUnitIncrement(), 0);
scrollPane.setScrollTop(newScrollTop);
consumed = (newScrollTop != scrollTop);
} else if (keyCode == Keyboard.KeyCode.DOWN) {
int newScrollTop = Math.min(scrollTop +
verticalScrollBar.getUnitIncrement(), getMaxScrollTop());
scrollPane.setScrollTop(newScrollTop);
consumed = (newScrollTop != scrollTop);
} else if (keyCode == Keyboard.KeyCode.LEFT) {
int newScrollLeft = Math.max(scrollLeft -
horizontalScrollBar.getUnitIncrement(), 0);
scrollPane.setScrollLeft(newScrollLeft);
consumed = (newScrollLeft != scrollLeft);
} else if (keyCode == Keyboard.KeyCode.RIGHT) {
int newScrollLeft = Math.min(scrollLeft +
horizontalScrollBar.getUnitIncrement(), getMaxScrollLeft());
scrollPane.setScrollLeft(newScrollLeft);
consumed = (newScrollLeft != scrollLeft);
} else if (keyCode == Keyboard.KeyCode.PAGE_UP) {
int increment = verticalScrollBar.getBlockIncrement();
int newScrollTop = Math.max(scrollTop - increment, 0);
scrollPane.setScrollTop(newScrollTop);
consumed = (newScrollTop != scrollTop);
} else if (keyCode == Keyboard.KeyCode.PAGE_DOWN) {
int increment = verticalScrollBar.getBlockIncrement();
int newScrollTop = Math.min(scrollTop + increment, getMaxScrollTop());
scrollPane.setScrollTop(newScrollTop);
consumed = (newScrollTop != scrollTop);
}
}
return consumed;
}
/**
* Gets the maximum legal scrollTop value this this skin imposes.
* This is the largest value possible that still shows as much of the view
* component as it can.
*
* @return
* The maximum scrollTop value
*/
private int getMaxScrollTop() {
int maxScrollTop = 0;
ScrollPane scrollPane = (ScrollPane)getComponent();
Component view = scrollPane.getView();
if (view != null) {
int viewHeight = view.getHeight();
int columnHeaderHeight = 0;
int horizontalScrollBarHeight = 0;
int height = getHeight();
Component columnHeader = scrollPane.getColumnHeader();
if (columnHeader != null) {
columnHeaderHeight = columnHeader.getHeight();
}
if (horizontalScrollBar.isVisible()) {
horizontalScrollBarHeight = horizontalScrollBar.getHeight();
}
maxScrollTop = Math.max(viewHeight + columnHeaderHeight +
horizontalScrollBarHeight - height, 0);
}
return maxScrollTop;
}
/**
* Gets the maximum legal scrollLeft value this this skin imposes.
* This is the largest value possible that still shows as much of the view
* component as it can.
*
* @return
* The maximum scrollLeft value
*/
private int getMaxScrollLeft() {
int maxScrollLeft = 0;
ScrollPane scrollPane = (ScrollPane)getComponent();
Component view = scrollPane.getView();
if (view != null) {
int viewWidth = view.getWidth();
int rowHeaderWidth = 0;
int verticalScrollBarWidth = 0;
int width = getWidth();
Component rowHeader = scrollPane.getRowHeader();
if (rowHeader != null) {
rowHeaderWidth = rowHeader.getWidth();
}
if (verticalScrollBar.isVisible()) {
verticalScrollBarWidth = verticalScrollBar.getWidth();
}
maxScrollLeft = Math.max(viewWidth + rowHeaderWidth +
verticalScrollBarWidth - width, 0);
}
return maxScrollLeft;
}
@Override
public void layout() {
ScrollPane scrollPane = (ScrollPane)getComponent();
ScrollBarPolicy horizontalPolicy = scrollPane.getHorizontalScrollBarPolicy();
ScrollBarPolicy verticalPolicy = scrollPane.getVerticalScrollBarPolicy();
boolean fillWidthToCapacity = false;
boolean fillHeightToCapacity = false;
// The FILL_TO_CAPACITY policy means that we try to use AUTO, and only
// if it ends up not being wide or tall enough do we use FILL
if (horizontalPolicy == ScrollBarPolicy.FILL_TO_CAPACITY) {
horizontalPolicy = ScrollBarPolicy.AUTO;
fillWidthToCapacity = true;
}
if (verticalPolicy == ScrollBarPolicy.FILL_TO_CAPACITY) {
verticalPolicy = ScrollBarPolicy.AUTO;
fillHeightToCapacity = true;
}
layoutHelper(horizontalPolicy, verticalPolicy);
Component view = scrollPane.getView();
if (view != null && (fillWidthToCapacity || fillHeightToCapacity)) {
// We assumed AUTO. Now we check our assumption to see if we
// need to adjust it to use FILL
boolean adjustWidth = false, adjustHeight = false;
if (fillWidthToCapacity) {
Component rowHeader = scrollPane.getRowHeader();
int rowHeaderWidth = rowHeader != null ? rowHeader.getWidth() : 0;
int verticalScrollBarWidth = verticalScrollBar.isVisible() ?
verticalScrollBar.getWidth() : 0;
int minViewWidth = getWidth() - rowHeaderWidth - verticalScrollBarWidth;
if (view.getWidth() < minViewWidth) {
horizontalPolicy = ScrollBarPolicy.FILL;
adjustWidth = true;
}
}
if (fillHeightToCapacity) {
Component columnHeader = scrollPane.getColumnHeader();
int columnHeaderHeight = columnHeader != null ?
columnHeader.getHeight() : 0;
int horizontalScrollBarHeight = horizontalScrollBar.isVisible() ?
horizontalScrollBar.getHeight() : 0;
int minViewHeight = getHeight() - columnHeaderHeight -
horizontalScrollBarHeight;
if (view.getHeight() < minViewHeight) {
verticalPolicy = ScrollBarPolicy.FILL;
adjustHeight = true;
}
}
if (adjustWidth || adjustHeight) {
layoutHelper(horizontalPolicy, verticalPolicy);
}
}
cachedHorizontalScrollBarHeight = horizontalScrollBar.getHeight();
cachedVerticalScrollBarWidth = verticalScrollBar.getWidth();
}
/**
* Layout helper method that assumes that the FILL_TO_CAPACITY
* scroll policy doesn't exist.
*
* @param horizontalPolicy
* The assumed horizontal scroll policy; musn't be FILL_TO_CAPACITY
*
* @param verticalPolicy
* The assumed vertical scroll policy; musn't be FILL_TO_CAPACITY
*/
private void layoutHelper(ScrollBarPolicy horizontalPolicy,
ScrollBarPolicy verticalPolicy) {
ScrollPane scrollPane = (ScrollPane)getComponent();
int width = getWidth();
int height = getHeight();
boolean constrainWidth = (horizontalPolicy == ScrollBarPolicy.FILL);
boolean constrainHeight = (verticalPolicy == ScrollBarPolicy.FILL);
Component view = scrollPane.getView();
Component columnHeader = scrollPane.getColumnHeader();
Component rowHeader = scrollPane.getRowHeader();
Component corner = scrollPane.getCorner();
int rowHeaderWidth = 0;
if (rowHeader != null) {
rowHeaderWidth = rowHeader.getPreferredWidth(-1);
}
int columnHeaderHeight = 0;
if (columnHeader != null) {
columnHeaderHeight = columnHeader.getPreferredHeight(-1);
}
int previousViewWidth, viewWidth = 0;
int previousViewHeight, viewHeight = 0;
int previousHorizontalScrollBarHeight, horizontalScrollBarHeight = cachedHorizontalScrollBarHeight;
int previousVerticalScrollBarWidth, verticalScrollBarWidth = cachedVerticalScrollBarWidth;
int i = 0;
do {
previousViewWidth = viewWidth;
previousViewHeight = viewHeight;
previousHorizontalScrollBarHeight = horizontalScrollBarHeight;
previousVerticalScrollBarWidth = verticalScrollBarWidth;
if (view != null) {
if (constrainWidth && constrainHeight) {
viewWidth = Math.max
(width - rowHeaderWidth - verticalScrollBarWidth, 0);
viewHeight = Math.max
(height - columnHeaderHeight - horizontalScrollBarHeight, 0);
} else if (constrainWidth) {
viewWidth = Math.max
(width - rowHeaderWidth - verticalScrollBarWidth, 0);
viewHeight = view.getPreferredHeight(viewWidth);
} else if (constrainHeight) {
viewHeight = Math.max
(height - columnHeaderHeight - horizontalScrollBarHeight, 0);
viewWidth = view.getPreferredWidth(viewHeight);
} else {
Dimensions viewPreferredSize = view.getPreferredSize();
viewWidth = viewPreferredSize.width;
viewHeight = viewPreferredSize.height;
}
}
if (horizontalPolicy == ScrollBarPolicy.ALWAYS
|| (horizontalPolicy == ScrollBarPolicy.AUTO
&& viewWidth > width - rowHeaderWidth - verticalScrollBarWidth)) {
horizontalScrollBarHeight = horizontalScrollBar.getPreferredHeight(-1);
} else {
horizontalScrollBarHeight = 0;
}
if (verticalPolicy == ScrollBarPolicy.ALWAYS
|| (verticalPolicy == ScrollBarPolicy.AUTO
&& viewHeight > height - columnHeaderHeight - horizontalScrollBarHeight)) {
verticalScrollBarWidth = verticalScrollBar.getPreferredWidth(-1);
} else {
verticalScrollBarWidth = 0;
}
if (++i > 4) {
// Infinite loop protection
System.err.println("Breaking out of potential infinite loop");
break;
}
} while (viewWidth != previousViewWidth
|| viewHeight != previousViewHeight
|| horizontalScrollBarHeight != previousHorizontalScrollBarHeight
|| verticalScrollBarWidth != previousVerticalScrollBarWidth);
int scrollTop = scrollPane.getScrollTop();
int scrollLeft = scrollPane.getScrollLeft();
if (view != null) {
view.setSize(viewWidth, viewHeight);
view.setLocation(rowHeaderWidth - scrollLeft, columnHeaderHeight - scrollTop);
}
if (columnHeader != null) {
columnHeader.setSize(viewWidth, columnHeaderHeight);
columnHeader.setLocation(rowHeaderWidth - scrollLeft, 0);
}
if (rowHeader != null) {
rowHeader.setSize(rowHeaderWidth, viewHeight);
rowHeader.setLocation(0, columnHeaderHeight - scrollTop);
}
if (horizontalScrollBarHeight > 0) {
horizontalScrollBar.setVisible(true);
int horizontalScrollBarWidth = Math.max
(width - rowHeaderWidth - verticalScrollBarWidth, 0);
horizontalScrollBar.setSize(horizontalScrollBarWidth,
horizontalScrollBarHeight);
horizontalScrollBar.setLocation(rowHeaderWidth,
height - horizontalScrollBarHeight);
} else {
horizontalScrollBar.setVisible(false);
}
if (verticalScrollBarWidth > 0) {
verticalScrollBar.setVisible(true);
int verticalScrollBarHeight = Math.max
(height - columnHeaderHeight - horizontalScrollBarHeight, 0);
verticalScrollBar.setSize(verticalScrollBarWidth,
verticalScrollBarHeight);
verticalScrollBar.setLocation(width - verticalScrollBarWidth,
columnHeaderHeight);
} else {
verticalScrollBar.setVisible(false);
}
// Handle corner components
if (columnHeaderHeight > 0
&& rowHeaderWidth > 0) {
if (corner != null) {
corner.setVisible(true);
corner.setSize(rowHeaderWidth, columnHeaderHeight);
corner.setLocation(0, 0);
topLeftCorner.setVisible(false);
} else {
topLeftCorner.setVisible(true);
topLeftCorner.setSize(rowHeaderWidth, columnHeaderHeight);
topLeftCorner.setLocation(0, 0);
}
} else {
if (corner != null) {
corner.setVisible(false);
}
topLeftCorner.setVisible(false);
}
if (rowHeaderWidth > 0
&& horizontalScrollBarHeight > 0) {
bottomLeftCorner.setVisible(true);
bottomLeftCorner.setSize(rowHeaderWidth, horizontalScrollBarHeight);
bottomLeftCorner.setLocation(0, height - horizontalScrollBarHeight);
} else {
bottomLeftCorner.setVisible(false);
}
if (verticalScrollBarWidth > 0
&& horizontalScrollBarHeight > 0) {
bottomRightCorner.setVisible(true);
bottomRightCorner.setSize(verticalScrollBarWidth, horizontalScrollBarHeight);
bottomRightCorner.setLocation(width - verticalScrollBarWidth,
height - horizontalScrollBarHeight);
} else {
bottomRightCorner.setVisible(false);
}
if (columnHeaderHeight > 0
&& verticalScrollBarWidth > 0) {
topRightCorner.setVisible(true);
topRightCorner.setSize(verticalScrollBarWidth, columnHeaderHeight);
topRightCorner.setLocation(width - verticalScrollBarWidth, 0);
} else {
topRightCorner.setVisible(false);
}
// Perform bounds checking on the scrollTop and scrollLeft values,
// and adjust them as necessary. Make sure to do this after we've laid
// everything out, since our ViewPortListener methods rely on valid
// sizes from our components.
int maxScrollTop = getMaxScrollTop();
if (scrollTop > maxScrollTop) {
scrollPane.setScrollTop(maxScrollTop);
}
int maxScrollLeft = getMaxScrollLeft();
if (scrollLeft > maxScrollLeft) {
scrollPane.setScrollLeft(maxScrollLeft);
}
// Adjust the structure of our scroll bars. Make sure to do this after
// we adjust the scrollTop and scrollLeft values; otherwise we might
// try to set structure values that are out of bounds.
int viewportWidth = Math.max(width - rowHeaderWidth - verticalScrollBarWidth, 0);
horizontalScrollBar.setScope(0, viewWidth, Math.min(viewWidth, viewportWidth));
horizontalScrollBar.setBlockIncrement(Math.max(1, viewportWidth - horizontalReveal));
int viewportHeight = Math.max(height - columnHeaderHeight - horizontalScrollBarHeight, 0);
verticalScrollBar.setScope(0, viewHeight, Math.min(viewHeight, viewportHeight));
verticalScrollBar.setBlockIncrement(Math.max(1, viewportHeight - verticalReveal));
}
@Override
public void setBackgroundPaint(Paint backgroundPaint) {
super.setBackgroundPaint(backgroundPaint);
optimizeScrolling = (backgroundPaint != null
&& backgroundPaint.getTransparency() == Transparency.OPAQUE);
}
public int getHorizontalIncrement() {
return horizontalScrollBar.getUnitIncrement();
}
public void setHorizontalIncrement(int horizontalIncrement) {
horizontalScrollBar.setUnitIncrement(horizontalIncrement);
}
public int getVerticalIncrement() {
return verticalScrollBar.getUnitIncrement();
}
public void setVerticalIncrement(int verticalIncrement) {
verticalScrollBar.setUnitIncrement(verticalIncrement);
}
public int getHorizontalReveal() {
return horizontalReveal;
}
public void setHorizontalReveal(int horizontalReveal) {
this.horizontalReveal = horizontalReveal;
}
public int getVerticalReveal() {
return verticalReveal;
}
public void setVerticalReveal(int verticalReveal) {
this.verticalReveal = verticalReveal;
}
private boolean isOptimizeScrolling() {
boolean optimizeScrollingLocal = this.optimizeScrolling;
if (optimizeScrollingLocal) {
// Due to Sun bug #6293145, we cannot call copyArea if scaling is
// applied to the graphics context.
// Due to Sun bug #4033851, we cannot call copyArea if the display
// host is obscured. For a full description of why this is the case,
// see http://people.apache.org/~tvolkert/tests/scrolling/
ScrollPane scrollPane = (ScrollPane)getComponent();
ApplicationContext.DisplayHost displayHost = scrollPane.getDisplay().getDisplayHost();
optimizeScrollingLocal = (displayHost.getScale() == 1
&& (DesktopApplicationContext.isActive()
&& displayHost.isDisplayable()));
}
return optimizeScrollingLocal;
}
// Viewport.Skin methods
@Override
public Bounds getViewportBounds() {
int x = 0;
int y = 0;
int width = getWidth();
int height = getHeight();
ScrollPane scrollPane = (ScrollPane)getComponent();
Component rowHeader = scrollPane.getRowHeader();
if (rowHeader != null) {
int rowHeaderWidth = rowHeader.getWidth();
x += rowHeaderWidth;
width -= rowHeaderWidth;
}
Component columnHeader = scrollPane.getColumnHeader();
if (columnHeader != null) {
int columnHeaderHeight = columnHeader.getHeight();
y += columnHeaderHeight;
height -= columnHeaderHeight;
}
if (horizontalScrollBar.isVisible()) {
height -= horizontalScrollBar.getHeight();
}
if (verticalScrollBar.isVisible()) {
width -= verticalScrollBar.getWidth();
}
return new Bounds(x, y, width, height);
}
// ScrollPaneListener methods
@Override
public void horizontalScrollBarPolicyChanged(ScrollPane scrollPane,
ScrollBarPolicy previousHorizontalScrollBarPolicy) {
invalidateComponent();
}
@Override
public void verticalScrollBarPolicyChanged(ScrollPane scrollPane,
ScrollBarPolicy previousVerticalScrollBarPolicy) {
invalidateComponent();
}
@Override
public void rowHeaderChanged(ScrollPane scrollPane, Component previousRowHeader) {
invalidateComponent();
}
@Override
public void columnHeaderChanged(ScrollPane scrollPane, Component previousColumnHeader) {
invalidateComponent();
}
@Override
public void cornerChanged(ScrollPane scrollPane, Component previousCorner) {
invalidateComponent();
}
// ViewportListener methods
@Override
public void scrollTopChanged(Viewport viewport, int previousScrollTop) {
// NOTE we don't invalidate the component here because we need only
// reposition the view and row header. Invalidating would yield
// the correct positioning, but it would do much more work than needed.
ScrollPane scrollPane = (ScrollPane)viewport;
Component view = scrollPane.getView();
Component rowHeader = scrollPane.getRowHeader();
Component columnHeader = scrollPane.getColumnHeader();
int columnHeaderHeight = 0;
if (columnHeader != null) {
columnHeaderHeight = columnHeader.getHeight();
}
int scrollTop = scrollPane.getScrollTop();
if (view != null
&& view.isShowing()
&& isOptimizeScrolling()) {
Bounds blitArea = view.getVisibleArea();
int blitX = blitArea.x + view.getX();
int blitY = blitArea.y + view.getY();
int blitWidth = blitArea.width;
int blitHeight = blitArea.height;
if (rowHeader != null) {
// Blit the row header as well
int rowHeaderWidth = rowHeader.getWidth();
blitX -= rowHeaderWidth;
blitWidth += rowHeaderWidth;
}
int deltaScrollTop = scrollTop - previousScrollTop;
blitY += Math.max(deltaScrollTop, 0);
blitHeight -= Math.abs(deltaScrollTop);
Graphics2D graphics = scrollPane.getGraphics();
graphics.copyArea(blitX, blitY, blitWidth, blitHeight, 0, -deltaScrollTop);
scrollPane.setConsumeRepaint(true);
try {
view.setLocation(view.getX(), columnHeaderHeight - scrollTop);
if (rowHeader != null) {
rowHeader.setLocation(0, columnHeaderHeight - scrollTop);
}
} finally {
scrollPane.setConsumeRepaint(false);
}
boolean repaintAllViewport = scrollPane.isRepaintAllViewport();
if (!repaintAllViewport) {
scrollPane.repaint(blitX, (columnHeaderHeight + (deltaScrollTop > 0 ? blitHeight : 0)),
blitWidth, Math.abs(deltaScrollTop), true);
} else {
Bounds viewportBounds = getViewportBounds();
scrollPane.repaint(viewportBounds.x, viewportBounds.y,
viewportBounds.width, viewportBounds.height, true);
}
} else {
if (view != null) {
view.setLocation(view.getX(), columnHeaderHeight - scrollTop);
}
if (rowHeader != null) {
rowHeader.setLocation(0, columnHeaderHeight - scrollTop);
}
}
if (scrollTop >= 0 && scrollTop <= getMaxScrollTop()) {
verticalScrollBar.setValue(scrollTop);
}
}
@Override
public void scrollLeftChanged(Viewport viewport, int previousScrollLeft) {
// NOTE we don't invalidate the component here because we need only
// reposition the view and column header. Invalidating would yield
// the correct positioning, but it would do much more work than needed.
ScrollPane scrollPane = (ScrollPane)viewport;
Component view = scrollPane.getView();
Component rowHeader = scrollPane.getRowHeader();
Component columnHeader = scrollPane.getColumnHeader();
int rowHeaderWidth = 0;
if (rowHeader != null) {
rowHeaderWidth = rowHeader.getWidth();
}
int scrollLeft = scrollPane.getScrollLeft();
if (view != null
&& view.isShowing()
&& isOptimizeScrolling()) {
Bounds blitArea = view.getVisibleArea();
int blitX = blitArea.x + view.getX();
int blitY = blitArea.y + view.getY();
int blitWidth = blitArea.width;
int blitHeight = blitArea.height;
if (columnHeader != null) {
// Blit the column header as well
int columnHeaderHeight = columnHeader.getHeight();
blitY -= columnHeaderHeight;
blitHeight += columnHeaderHeight;
}
int deltaScrollLeft = scrollLeft - previousScrollLeft;
blitX += Math.max(deltaScrollLeft, 0);
blitWidth -= Math.abs(deltaScrollLeft);
Graphics2D graphics = scrollPane.getGraphics();
graphics.copyArea(blitX, blitY, blitWidth, blitHeight, -deltaScrollLeft, 0);
scrollPane.setConsumeRepaint(true);
try {
view.setLocation(rowHeaderWidth - scrollLeft, view.getY());
if (columnHeader != null) {
columnHeader.setLocation(rowHeaderWidth - scrollLeft, 0);
}
} finally {
scrollPane.setConsumeRepaint(false);
}
boolean repaintAllViewport = scrollPane.isRepaintAllViewport();
if (!repaintAllViewport) {
scrollPane.repaint(rowHeaderWidth + (deltaScrollLeft > 0 ? blitWidth : 0), blitY,
Math.abs(deltaScrollLeft), blitHeight, true);
} else {
Bounds viewportBounds = getViewportBounds();
scrollPane.repaint(viewportBounds.x, viewportBounds.y,
viewportBounds.width, viewportBounds.height, true);
}
} else {
if (view != null) {
view.setLocation(rowHeaderWidth - scrollLeft, view.getY());
}
if (columnHeader != null) {
columnHeader.setLocation(rowHeaderWidth - scrollLeft, 0);
}
}
if (scrollLeft >= 0 && scrollLeft <= getMaxScrollLeft()) {
horizontalScrollBar.setValue(scrollLeft);
}
}
@Override
public void viewChanged(Viewport viewport, Component previousView) {
invalidateComponent();
}
// ScrollBarValueListener methods
@Override
public void valueChanged(ScrollBar scrollBar, int previousValue) {
ScrollPane scrollPane = (ScrollPane)getComponent();
int value = scrollBar.getValue();
if (scrollBar == horizontalScrollBar) {
scrollPane.setScrollLeft(value);
} else {
scrollPane.setScrollTop(value);
}
}
}