/* class JScrollPane
*
* Copyright (C) 2001, 2002, 2003 R M Pitman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package charvax.swing;
import charva.awt.Component;
import charva.awt.Container;
import charva.awt.Dimension;
import charva.awt.Insets;
import charva.awt.Point;
import charva.awt.Rectangle;
import charva.awt.Scrollable;
import charva.awt.Toolkit;
import charva.awt.event.ScrollEvent;
import charva.awt.event.ScrollListener;
import charvax.swing.border.Border;
import charvax.swing.table.TableHeader;
/**
* provides a scrollable view of a component.
*/
public class JScrollPane extends Container implements ScrollListener {
/**
* Creates an empty JScrollPane.
*/
public JScrollPane() {
}
/**
* Create a JScrollPane that displays the contents of the specified
* component.
*
* @param component_
* The component to be displayed. This component must implement
* the Scrollable interface.
*/
public JScrollPane(Component component_) {
setViewportView(component_);
}
/**
* Creates a viewport if necessary, and then sets its view.
*
* @param component_
* the view to set in the viewport.
*/
public void setViewportView(Component component_) {
_child = component_;
_childViewport.setView(component_);
add(_childViewport);
_childViewport.setParent(this);
/*
* This will cause a ClassCastException if the component does not
* implement the Scrollable interface.
*/
Scrollable scrollable = (Scrollable) component_;
scrollable.addScrollListener(this);
/*
* If the child component is a JTable, we display two viewports instead
* of one; the TableHeader corresponding to the table is displayed in
* the top viewport and scrolls left and right but not up and down. The
* contents of the table are displayed in the bottom viewport and
* scroll in all directions.
*/
if (component_ instanceof JTable) {
JTable table = (JTable) component_;
TableHeader header = new TableHeader(table.getModel());
_headerViewport = new JViewport();
_headerViewport.setView(header);
add(_headerViewport);
_headerViewport.setParent(this);
_childViewport.setLocation(new Point(0, 1));
_childViewport.setViewPosition(new Point(0, -1));
}
}
/**
* Returns the viewport of the component being displayed.
*/
public JViewport getViewport() {
return _childViewport;
}
/**
* Overrides the corresponding method in Container.
*/
public void setSize(int width_, int height_) {
super.setSize(width_, height_);
Dimension size = new Dimension(width_, height_);
if (_border != null) {
Insets borderInsets = _border.getBorderInsets(this);
size.height -= (borderInsets.top + borderInsets.bottom);
size.width -= (borderInsets.left + borderInsets.right);
}
// Set the size of the viewport(s) as well
setViewportExtents(size);
}
/**
* Overrides the corresponding method in Container.
*/
public void setSize(Dimension size_) {
this.setSize(size_.width, size_.height);
}
/**
* Overrides the minimumSize() method of Container.
*/
public Dimension minimumSize() {
Dimension size = new Dimension();
Component view = getViewport().getView();
if (view instanceof Scrollable) {
Scrollable s = (Scrollable) view;
size.setSize(s.getPreferredScrollableViewportSize());
} else {
size.setSize(view.getSize());
}
// Set the size of the viewport(s) as well
setViewportExtents(size);
if (_border != null) {
Insets borderInsets = _border.getBorderInsets(this);
size.height += (borderInsets.top + borderInsets.bottom);
size.width += (borderInsets.left + borderInsets.right);
}
return size;
}
/**
* Called by a Scrollable object such as JTable or JList, when its state
* changes in such a way that it may need to be scrolled.
*/
public void scroll(ScrollEvent e_) {
Scrollable scrollable = e_.getScrollable();
int direction = e_.getDirection();
/*
* "limit" gives the row and column of the view component that must
* appear just inside the JViewport after scrolling.
*/
Point limit = e_.getLimit();
/*
* Determine the value of "limit" relative to the top left corner of
* the JScrollPane.
*/
Point viewportLocation = getViewport().getLocation();
limit.translate(viewportLocation.x, viewportLocation.y);
limit.translate(scrollable.getLocation());
/*
* Get the bounding rectangle of the child viewport, relative to the
* top left corner of the JScrollPane.
*/
Rectangle viewport = _childViewport.getBounds();
//Dimension viewportExtent = _childViewport.getExtentSize();
Point viewPosition = _childViewport.getViewPosition();
Point headerPosition = null;
if (_headerViewport != null)
headerPosition = _headerViewport.getViewPosition();
/*
* If the limit is inside the viewport, the component doesn't need to
* be scrolled. First do the left/right scrolling.
*/
if (limit.x > viewport.getRight()) {
if ((direction == ScrollEvent.LEFT
|| direction == ScrollEvent.UP_LEFT || direction == ScrollEvent.DOWN_LEFT)) {
viewPosition.x -= (limit.x - viewport.getRight());
if (_headerViewport != null)
headerPosition.x -= (limit.x - viewport.getRight());
} else if (direction == ScrollEvent.RIGHT
|| direction == ScrollEvent.UP_RIGHT
|| direction == ScrollEvent.DOWN_RIGHT) {
viewPosition.x += (viewport.getLeft() - limit.x);
if (_headerViewport != null)
headerPosition.x += (viewport.getLeft() - limit.x);
}
} else if (limit.x < viewport.getLeft()) {
if (direction == ScrollEvent.RIGHT
|| direction == ScrollEvent.UP_RIGHT
|| direction == ScrollEvent.DOWN_RIGHT) {
viewPosition.x += (viewport.getLeft() - limit.x);
if (_headerViewport != null)
headerPosition.x += (viewport.getLeft() - limit.x);
} else if (direction == ScrollEvent.LEFT
|| direction == ScrollEvent.UP_LEFT
|| direction == ScrollEvent.DOWN_LEFT) {
viewPosition.x -= (limit.x - viewport.getRight());
if (_headerViewport != null)
headerPosition.x -= (limit.x - viewport.getRight());
}
}
// Now do the up/down scrolling
if (limit.y < viewport.getTop()
&& (direction == ScrollEvent.DOWN
|| direction == ScrollEvent.DOWN_LEFT || direction == ScrollEvent.DOWN_RIGHT)) {
viewPosition.y += (viewport.getTop() - limit.y);
} else if (limit.y > viewport.getBottom()
&& (direction == ScrollEvent.UP
|| direction == ScrollEvent.UP_LEFT || direction == ScrollEvent.UP_RIGHT)) {
viewPosition.y -= (limit.y - viewport.getBottom());
}
_childViewport.setViewPosition(viewPosition);
if (_headerViewport != null)
_headerViewport.setViewPosition(headerPosition);
draw(Toolkit.getDefaultToolkit());
/*
* Ensure the cursor is within the viewport (if the component contained
* within the viewport is offset a long way to the left, the cursor
* position can get scrambled).
*/
Toolkit toolkit = Toolkit.getDefaultToolkit();
Point cursor = toolkit.getCursor();
Point viewportOrigin = _childViewport.getLocationOnScreen();
if (cursor.x < viewportOrigin.x || cursor.y < viewportOrigin.y) {
if (cursor.x < viewportOrigin.x) cursor.x = viewportOrigin.x;
if (cursor.y < viewportOrigin.y) cursor.y = viewportOrigin.y;
toolkit.setCursor(cursor);
}
}
/**
* @param toolkit
*/
public void draw(Toolkit toolkit) {
/*
* Get the absolute origin of this component.
*/
Point origin = getLocationOnScreen();
Insets borderInsets;
if (getViewportBorder() != null) {
borderInsets = getViewportBorder().getBorderInsets(this);
} else
borderInsets = new Insets(0, 0, 0, 0);
int colorpair = getCursesColor();
Dimension size = minimumSize();
if (_border != null) {
_border.paintBorder(this, 0, origin.x, origin.y, size.width,
size.height, toolkit);
}
// Don't draw scrollbars if the child component is a TableHeader.
if (_child instanceof TableHeader) return;
/*
* If the child component is larger than the viewport, draw scrollbars.
*/
// The size of the component displayed within the viewport.
Dimension childSize = getViewport().getViewSize();
// The size of the viewport
Dimension extentSize = getViewport().getExtentSize();
Point viewPosition = getViewport().getViewPosition();
// If the child is a JTable, we have to adjust the
// parameters a bit because the viewport includes the header.
/*
* if (_child instanceof JTable) { viewport_height--; child_height--;
* view_y++;
*/
if (childSize.height > extentSize.height) {
int scrollbar_height = (extentSize.height * extentSize.height)
/ childSize.height;
// Round the height upwards to the nearest integer.
if (((extentSize.height * extentSize.height) % childSize.height) != 0)
scrollbar_height++;
int scrollbar_offset = (-1 * viewPosition.y * extentSize.height)
/ childSize.height;
for (int i = 0; i < extentSize.height; i++) {
toolkit.setCursor(origin.addOffset(borderInsets.left
+ extentSize.width, borderInsets.top + i));
if (i >= scrollbar_offset
&& i < scrollbar_offset + scrollbar_height) {
toolkit.addChar(Toolkit.ACS_CKBOARD, 0, colorpair);
}
}
}
if (childSize.width > extentSize.width) {
int scrollbar_width = (extentSize.width * extentSize.width)
/ childSize.width;
// Round the width upwards to the nearest integer.
if (((extentSize.width * extentSize.width) % childSize.width) != 0)
scrollbar_width++;
int scrollbar_offset = (-1 * viewPosition.x * extentSize.width)
/ childSize.width;
for (int i = 0; i < extentSize.width; i++) {
toolkit.setCursor(origin.addOffset(borderInsets.left + i,
borderInsets.top + extentSize.height));
if (i >= scrollbar_offset
&& i < scrollbar_offset + scrollbar_width) {
toolkit.addChar(Toolkit.ACS_CKBOARD, 0, colorpair);
}
}
}
// Draw the child viewport(s) by calling the draw() method
// of the Container class.
super.draw(toolkit);
}
/**
* Adds a border around the viewport.
*/
public void setViewportBorder(Border viewportBorder_) {
_border = viewportBorder_;
Insets insets = _border.getBorderInsets(this);
if (_headerViewport != null) {
// This must be a JTable.
_headerViewport.setLocation(new Point(insets.left, insets.top));
_childViewport.setLocation(new Point(insets.left, insets.top + 1));
} else
_childViewport.setLocation(new Point(insets.left, insets.top));
}
/**
* Returns a reference to the border around the JScrollPane's viewport.
*/
public Border getViewportBorder() {
return _border;
}
public void debug(int level_) {
for (int i = 0; i < level_; i++)
System.err.print(" ");
System.err.println("JScrollPane origin=" + _origin + " size="
+ getSize());
super.debug(level_ + 1);
}
public String toString() {
return ("JScrollPane origin=" + _origin + " size=" + getSize());
}
/**
* Sets the size of the visible part of the view.
*/
private void setViewportExtents(Dimension size) {
if (_headerViewport != null) {
_headerViewport.setExtentSize(size.width, 1);
_childViewport.setExtentSize(size.width, size.height - 1);
} else {
_childViewport.setExtentSize(size.width, size.height);
}
}
//====================================================================
// INSTANCE VARIABLES
private Component _child;
/**
* This is used only if the contained component is a JTable.
*/
private JViewport _headerViewport = null;
/**
* A JViewport container that holds the (single) child component.
*/
private JViewport _childViewport = new JViewport();
//private JScrollBar _vertical;
//private JScrollBar _horiz;
private Border _border;
}