/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.yoursway.swt.scrollbar;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseWheelListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Shell;
public class CoolScrolledComposite extends Composite {
Control content;
Listener contentListener;
Listener filter;
int minHeight = 0;
int minWidth = 0;
boolean expandHorizontal = false;
boolean expandVertical = false;
boolean showFocusedControl = false;
CoolScrollBar horizontalBar;
CoolScrollBar verticalBar;
Composite composite;
public Composite parentComposite() {
return composite;
}
@Override
public ScrollBar getHorizontalBar() {
throw new UnsupportedOperationException();
}
@Override
public ScrollBar getVerticalBar() {
throw new UnsupportedOperationException();
}
public CoolScrolledComposite(Composite parent, int style) {
super(parent, SWT.NONE);
composite = new Composite(this, style);
composite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL)
.create());
composite.setLayout(new CoolScrolledCompositeLayout());
verticalBar = new CoolScrollBar(this, SWT.TRANSPARENT, true);
verticalBar.setLayoutData(GridDataFactory.fillDefaults().grab(false, true).create());
horizontalBar = new CoolScrollBar(this, SWT.TRANSPARENT, false);
horizontalBar.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
verticalBar.animateShow();
horizontalBar.animateShow();
super.setLayout(GridLayoutFactory.fillDefaults().numColumns(2).spacing(1, 1).create());
super.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
this.addMouseWheelListener(new MouseWheelListener() {
public void mouseScrolled(MouseEvent e) {
float position = verticalBar.getPosition();
position -= e.count;
verticalBar.setPosition(position);
vScroll();
}
});
// hBar.setVisible(false);
horizontalBar.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
hScroll();
}
});
// vBar.setVisible(false);
verticalBar.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
vScroll();
}
});
contentListener = new Listener() {
public void handleEvent(Event e) {
if (e.type != SWT.Resize)
return;
layout(false);
}
};
filter = new Listener() {
public void handleEvent(Event event) {
if (event.widget instanceof Control) {
Control control = (Control) event.widget;
if (contains(control))
showControl(control);
}
}
};
addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
getDisplay().removeFilter(SWT.FocusIn, filter);
}
});
}
boolean contains(Control control) {
if (control == null || control.isDisposed())
return false;
Composite parent = control.getParent();
while (parent != null && !(parent instanceof Shell)) {
if (composite == parent)
return true;
parent = parent.getParent();
}
return false;
}
/**
* Returns <code>true</code> if the content control will be expanded to fill
* available horizontal space.
*
* @return the receiver's horizontal expansion state
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*
* @since 3.2
*/
public boolean getExpandHorizontal() {
checkWidget();
return expandHorizontal;
}
/**
* Returns <code>true</code> if the content control will be expanded to fill
* available vertical space.
*
* @return the receiver's vertical expansion state
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*
* @since 3.2
*/
public boolean getExpandVertical() {
checkWidget();
return expandVertical;
}
/**
* Returns the minimum width of the content control.
*
* @return the minimum width
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*
* @since 3.2
*/
public int getMinWidth() {
checkWidget();
return minWidth;
}
/**
* Returns the minimum height of the content control.
*
* @return the minimum height
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*
* @since 3.2
*/
public int getMinHeight() {
checkWidget();
return minHeight;
}
/**
* Get the content that is being scrolled.
*
* @return the control displayed in the content area
*/
public Control getContent() {
// checkWidget();
return content;
}
/**
* Returns <code>true</code> if the receiver automatically scrolls to a
* focused child control to make it visible. Otherwise, returns
* <code>false</code>.
*
* @return a boolean indicating whether focused child controls are
* automatically scrolled into the viewport
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*
* @since 3.4
*/
public boolean getShowFocusedControl() {
checkWidget();
return showFocusedControl;
}
void hScroll() {
if (content == null)
return;
Point location = content.getLocation();
content.setLocation((int) -horizontalBar.getPosition(), location.y);
}
boolean needHScroll(Rectangle contentRect, boolean vVisible) {
Rectangle hostRect = composite.getBounds();
if (!expandHorizontal && contentRect.width > hostRect.width)
return true;
if (expandHorizontal && minWidth > hostRect.width)
return true;
return false;
}
boolean needVScroll(Rectangle contentRect, boolean hVisible) {
Rectangle hostRect = getBounds();
if (!expandVertical && contentRect.height > hostRect.height)
return true;
if (expandVertical && minHeight > hostRect.height)
return true;
return false;
}
/**
* Return the point in the content that currently appears in the top left
* corner of the scrolled composite.
*
* @return the point in the content that currently appears in the top left
* corner of the scrolled composite. If no content has been set,
* this returns (0, 0).
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*
* @since 2.0
*/
public Point getOrigin() {
checkWidget();
if (content == null)
return new Point(0, 0);
Point location = content.getLocation();
return new Point(-location.x, -location.y);
}
/**
* Scrolls the content so that the specified point in the content is in the
* top left corner. If no content has been set, nothing will occur.
*
* Negative values will be ignored. Values greater than the maximum scroll
* distance will result in scrolling to the end of the scrollbar.
*
* @param origin
* the point on the content to appear in the top left corner
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* <li>ERROR_INVALID_ARGUMENT - value of origin is outside of
* content
* </ul>
* @since 2.0
*/
public void setOrigin(Point origin) {
setOrigin(origin.x, origin.y);
}
/**
* Scrolls the content so that the specified point in the content is in the
* top left corner. If no content has been set, nothing will occur.
*
* Negative values will be ignored. Values greater than the maximum scroll
* distance will result in scrolling to the end of the scrollbar.
*
* @param x
* the x coordinate of the content to appear in the top left
* corner
*
* @param y
* the y coordinate of the content to appear in the top left
* corner
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*
* @since 2.0
*/
public void setOrigin(int x, int y) {
checkWidget();
if (content == null)
return;
horizontalBar.setPosition(-x);
verticalBar.setPosition(-y);
//
// ScrollBar hBar = getHorizontalBar();
// if (hBar != null) {
// hBar.setSelection(x);
// x = -hBar.getSelection();
// } else {
// x = 0;
// }
// ScrollBar vBar = getVerticalBar();
// if (vBar != null) {
// vBar.setSelection(y);
// y = -vBar.getSelection();
// } else {
// y = 0;
// }
content.setLocation(x, y);
}
/**
* Set the content that will be scrolled.
*
* @param content
* the control to be displayed in the content area
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*/
public void setContent(Control content) {
if (content == null)
throw new NullPointerException("content is null");
checkWidget();
if (this.content != null && !this.content.isDisposed()) {
this.content.removeListener(SWT.Resize, contentListener);
this.content.setBounds(new Rectangle(-200, -200, 0, 0)); // ??? ;)
}
// content.setParent(composite);
this.content = content;
// if (vBar != null) {
// vBar.setMaximum(0);
// vBar.setThumb(0);
// vBar.setSelection(0);
// }
// if (hBar != null) {
// hBar.setMaximum(0);
// hBar.setThumb(0);
// hBar.setSelection(0);
// }
content.setLocation(0, 0);
layout(false);
this.content.addListener(SWT.Resize, contentListener);
}
/**
* Configure the ScrolledComposite to resize the content object to be as
* wide as the ScrolledComposite when the width of the ScrolledComposite is
* greater than the minimum width specified in setMinWidth. If the
* ScrolledComposite is less than the minimum width, the content will not be
* resized and instead the horizontal scroll bar will be used to view the
* entire width. If expand is false, this behaviour is turned off. By
* default, this behaviour is turned off.
*
* @param expand
* true to expand the content control to fill available
* horizontal space
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*/
public void setExpandHorizontal(boolean expand) {
checkWidget();
if (expand == expandHorizontal)
return;
expandHorizontal = expand;
layout(false);
}
/**
* Configure the ScrolledComposite to resize the content object to be as
* tall as the ScrolledComposite when the height of the ScrolledComposite is
* greater than the minimum height specified in setMinHeight. If the
* ScrolledComposite is less than the minimum height, the content will not
* be resized and instead the vertical scroll bar will be used to view the
* entire height. If expand is false, this behaviour is turned off. By
* default, this behaviour is turned off.
*
* @param expand
* true to expand the content control to fill available vertical
* space
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*/
public void setExpandVertical(boolean expand) {
checkWidget();
if (expand == expandVertical)
return;
expandVertical = expand;
layout(false);
}
/**
* Sets the layout which is associated with the receiver to be the argument
* which may be null.
* <p>
* Note: No Layout can be set on this Control because it already manages the
* size and position of its children.
* </p>
*
* @param layout
* the receiver's new layout or null
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*/
@Override
public void setLayout(Layout layout) {
checkWidget();
return;
}
/**
* Specify the minimum height at which the ScrolledComposite will begin
* scrolling the content with the vertical scroll bar. This value is only
* relevant if setExpandVertical(true) has been set.
*
* @param height
* the minimum height or 0 for default height
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*/
public void setMinHeight(int height) {
setMinSize(minWidth, height);
}
/**
* Specify the minimum width and height at which the ScrolledComposite will
* begin scrolling the content with the horizontal scroll bar. This value is
* only relevant if setExpandHorizontal(true) and setExpandVertical(true)
* have been set.
*
* @param size
* the minimum size or null for the default size
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*/
public void setMinSize(Point size) {
if (size == null) {
setMinSize(0, 0);
} else {
setMinSize(size.x, size.y);
}
}
/**
* Specify the minimum width and height at which the ScrolledComposite will
* begin scrolling the content with the horizontal scroll bar. This value is
* only relevant if setExpandHorizontal(true) and setExpandVertical(true)
* have been set.
*
* @param width
* the minimum width or 0 for default width
* @param height
* the minimum height or 0 for default height
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*/
public void setMinSize(int width, int height) {
checkWidget();
if (width == minWidth && height == minHeight)
return;
minWidth = Math.max(0, width);
minHeight = Math.max(0, height);
layout(false);
}
/**
* Specify the minimum width at which the ScrolledComposite will begin
* scrolling the content with the horizontal scroll bar. This value is only
* relevant if setExpandHorizontal(true) has been set.
*
* @param width
* the minimum width or 0 for default width
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*/
public void setMinWidth(int width) {
setMinSize(width, minHeight);
}
/**
* Configure the receiver to automatically scroll to a focused child control
* to make it visible.
*
* If show is <code>false</code>, show a focused control is off. By default,
* show a focused control is off.
*
* @param show
* <code>true</code> to show a focused control.
*
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*
* @since 3.4
*/
public void setShowFocusedControl(boolean show) {
checkWidget();
if (showFocusedControl == show)
return;
Display display = getDisplay();
display.removeFilter(SWT.FocusIn, filter);
showFocusedControl = show;
if (!showFocusedControl)
return;
display.addFilter(SWT.FocusIn, filter);
Control control = display.getFocusControl();
if (contains(control))
showControl(control);
}
/**
* Scrolls the content of the receiver so that the control is visible.
*
* @param control
* the control to be shown
*
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the control is null</li>
* <li>ERROR_INVALID_ARGUMENT - if the control has been
* disposed</li>
* </ul>
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been
* disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*
* @since 3.4
*/
public void showControl(Control control) {
checkWidget();
if (control == null)
SWT.error(SWT.ERROR_NULL_ARGUMENT);
if (control.isDisposed())
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
if (!contains(control))
SWT.error(SWT.ERROR_INVALID_ARGUMENT);
Rectangle itemRect = getDisplay().map(control.getParent(), composite, control.getBounds());
Rectangle area = composite.getClientArea();
Point origin = getOrigin();
if (itemRect.x < 0)
origin.x = Math.max(0, origin.x + itemRect.x);
if (itemRect.y < 0)
origin.y = Math.max(0, origin.y + itemRect.y);
if (area.width < itemRect.x + itemRect.width)
origin.x = Math.max(0, origin.x + itemRect.x + itemRect.width - area.width);
if (area.height < itemRect.y + itemRect.height)
origin.y = Math.max(0, origin.y + itemRect.y + itemRect.height - area.height);
setOrigin(origin);
}
void vScroll() {
if (content == null)
return;
Point location = content.getLocation();
content.setLocation(location.x, (int) -verticalBar.getPosition());
}
}