/*
* Bibliothek - DockingFrames
* Library built on Java/Swing, allows the user to "drag and drop"
* panels containing any Swing-Component the developer likes to add.
*
* Copyright (C) 2012 Herve Guillaume, Benjamin Sigg
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Herve Guillaume
* rvguillaume@hotmail.com
* FR - France
*
* Benjamin Sigg
* benjamin_sigg@gmx.ch
* CH - Switzerland
*/
package bibliothek.gui.dock.station.toolbar.group;
import java.awt.Adjustable;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BoundedRangeModel;
import javax.swing.DefaultBoundedRangeModel;
import javax.swing.JComponent;
import javax.swing.JScrollBar;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import bibliothek.gui.Orientation;
import bibliothek.gui.dock.ToolbarGroupDockStation;
/**
* A slim version of a {@link JScrollBar}.
*
* @author Benjamin Sigg
*/
public class SlimScrollbar extends JComponent implements ColumnScrollBar,
Adjustable{
private BoundedRangeModel model;
private Orientation orientation = Orientation.HORIZONTAL;
private List<AdjustmentListener> listeners = new ArrayList<AdjustmentListener>();
private boolean hover = false;
private boolean mousePressed = false;
private float mouseOffset = 0;
/**
* A factory creating new {@link SlimScrollbar}s.
*/
public static final ColumnScrollBarFactory FACTORY = new ColumnScrollBarFactory(){
@Override
public ColumnScrollBar create( ToolbarGroupDockStation station ){
return new SlimScrollbar();
}
};
/**
* Creates a new scrollbar
*/
public SlimScrollbar(){
model = new DefaultBoundedRangeModel(0, 0, 0, 1);
model.addChangeListener(new ChangeListener(){
@Override
public void stateChanged( ChangeEvent e ){
AdjustmentEvent event = new AdjustmentEvent(SlimScrollbar.this,
0, 0, getValue());
for (AdjustmentListener listener : listeners){
listener.adjustmentValueChanged(event);
}
}
});
MouseAdapter adapter = new MouseAdapter(){
@Override
public void mousePressed( MouseEvent e ){
onMouseEvent(e, true);
}
@Override
public void mouseReleased( MouseEvent e ){
mousePressed = (e.getModifiersEx() & (MouseEvent.BUTTON1_DOWN_MASK
| MouseEvent.BUTTON2_DOWN_MASK | MouseEvent.BUTTON3_DOWN_MASK)) != 0;
onMouseEvent(e, false);
}
@Override
public void mouseDragged( MouseEvent e ){
onMouseEvent(e, true);
}
@Override
public void mouseEntered( MouseEvent e ){
onMouseEvent(e, false);
}
@Override
public void mouseExited( MouseEvent e ){
hover = false;
repaint();
}
@Override
public void mouseMoved( MouseEvent e ){
onMouseEvent(e, false);
}
};
addMouseListener(adapter);
addMouseMotionListener(adapter);
}
private void onMouseEvent( MouseEvent event, boolean mouseDown ){
int value = model.getValue();
int thumb = getThumbSize();
float track = getTrack();
int position;
if (orientation == Orientation.HORIZONTAL){
position = event.getX();
} else{
position = event.getY();
}
if (track > 0){
float range = model.getMaximum() - model.getMinimum();
float factor = range / track;
if (mouseDown && !mousePressed){
mouseOffset = position - getThumbStart();
if (mouseOffset < 0 || mouseOffset >= thumb){
mouseOffset = thumb / 2;
}
mousePressed = true;
}
if (mouseDown && mousePressed){
setValue((int) ((position - mouseOffset) * factor));
}
}
if (mouseDown){
hover = true;
} else if (contains(event.getPoint())){
hover = value <= position && position <= value + thumb;
} else{
hover = false;
}
repaint();
}
@Override
public Dimension getMinimumSize(){
return new Dimension(6, 6);
}
@Override
public Dimension getPreferredSize(){
return new Dimension(6, 6);
}
@Override
protected void paintComponent( Graphics g ){
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
int value = getThumbStart();
int thumb = getThumbSize();
Rectangle2D rec = null;
final int baseColorValue = 120;
if (hover){
if (orientation == Orientation.HORIZONTAL){
g2D.setPaint(new GradientPaint(new Point(value, 0), new Color(
baseColorValue + 120, baseColorValue + 120,
baseColorValue + 120), new Point(value, 6), new Color(
baseColorValue + 40, baseColorValue + 40,
baseColorValue + 40)));
rec = new Rectangle2D.Double(value, 0, thumb, 6);
} else{
g2D.setPaint(new GradientPaint(new Point(0, value), new Color(
baseColorValue + 120, baseColorValue + 120,
baseColorValue + 120), new Point(6, value), new Color(
baseColorValue + 40, baseColorValue + 40,
baseColorValue + 40)));
rec = new Rectangle2D.Double(0, value, 6, thumb);
}
} else{
if (orientation == Orientation.HORIZONTAL){
g2D.setPaint(new GradientPaint(new Point(value, 0), new Color(
baseColorValue + 90, baseColorValue + 90,
baseColorValue + 90), new Point(value, 6), new Color(
baseColorValue, baseColorValue, baseColorValue)));
rec = new Rectangle2D.Double(value, 0, thumb, 6);
} else{
g2D.setPaint(new GradientPaint(new Point(0, value), new Color(
baseColorValue + 90, baseColorValue + 90,
baseColorValue + 90), new Point(6, value), new Color(
baseColorValue, baseColorValue, baseColorValue)));
rec = new Rectangle2D.Double(0, value, 6, thumb);
}
}
g2D.fill(rec);
}
private int getThumbStart(){
float range = model.getMaximum() - model.getMinimum();
float track = getTrack();
return (int) (model.getValue() * (track / range));
}
private int getThumbSize(){
float range = model.getMaximum() - model.getMinimum();
float extent = model.getExtent();
float track = getTrack();
int thumb = (int) (track * (extent / range));
thumb = Math.max(thumb, 20);
thumb = Math.min(thumb, (int) track);
return thumb;
}
private int getTrack(){
if (orientation == Orientation.HORIZONTAL){
return getWidth();
} else{
return getHeight();
}
}
@Override
public void setValues( int required, int available ){
model.setMaximum(required);
model.setExtent(available);
}
@Override
public int getValue(){
return model.getValue();
}
@Override
public Component getComponent(){
return this;
}
@Override
public void setOrientation( Orientation orientation ){
this.orientation = orientation;
revalidate();
}
@Override
public void addAdjustmentListener( AdjustmentListener listener ){
listeners.add(listener);
}
@Override
public void removeAdjustmentListener( AdjustmentListener listener ){
listeners.remove(listener);
}
@Override
public int getOrientation(){
if (orientation == Orientation.HORIZONTAL){
return HORIZONTAL;
} else{
return VERTICAL;
}
}
@Override
public void setMinimum( int min ){
model.setMinimum(min);
}
@Override
public int getMinimum(){
return model.getMinimum();
}
@Override
public void setMaximum( int max ){
model.setMaximum(max);
}
@Override
public int getMaximum(){
return model.getMaximum();
}
@Override
public void setUnitIncrement( int u ){
// ignore
}
@Override
public int getUnitIncrement(){
// ignore
return 0;
}
@Override
public void setBlockIncrement( int b ){
// ignore
}
@Override
public int getBlockIncrement(){
// ignore
return 0;
}
@Override
public void setVisibleAmount( int v ){
// ignore
}
@Override
public int getVisibleAmount(){
// ignore
return 0;
}
@Override
public void setValue( int v ){
model.setValue(v);
}
}