package org.basex.gui.layout;
import org.basex.gui.GUIConstants;
import org.basex.gui.GUIConstants.Fill;
import org.basex.util.Performance;
import java.awt.*;
import java.awt.event.MouseEvent;
/**
* This is a scrollbar implementation, supporting arbitrary
* panel heights without increasing the memory consumption.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public final class BaseXBar extends BaseXPanel {
/** Scrollbar size. */
public static final int SIZE = 16;
/** Maximum scrolling speed. */
private static final int MAXSTEP = 15;
/** Animated scrollbar zooming steps. */
static final int[] STEPS = { -MAXSTEP, -14, -11, -8, -6, -4, -3,
-2, -1, -1, 0, 0, 1, 1, 2, 3, 4, 6, 8, 11, 14, MAXSTEP };
/** Minimum size for the scrollbar slider. */
private static final int MINSIZE = 20;
/** Reference to the scrolled component. */
final BaseXPanel comp;
/** Scrollbar width. */
final int ww;
/** Current scrolling speed. */
int step = STEPS.length / 2;
/** Flag reporting if the scrollbar animation is running. */
boolean animated;
/** Scrollbar height. */
int hh;
/** Scrollbar slider position. */
int barPos;
/** Scrollbar slider size. */
int barSize;
/** Scrollbar dragging position. */
int dragPos;
/** Flag for button clicks. */
boolean button;
/** Flag for scrolling downward. */
boolean down;
/** Flag for sliding the scrollbar. */
boolean sliding;
/** Flag for moving upward. */
boolean moving;
/** Current panel position. */
int pos;
/** Current panel height. */
int height;
/** Flag for scrolling upward. */
private boolean up;
/** Scrollbar slider offset. */
private int barOffset;
/** Flag for permanent scrollbar visibility. */
private final boolean visible;
/**
* Default constructor. By default, the scrollbar is switched off
* if the component is completely displayed.
* @param cmp reference to the scrolled component
*/
public BaseXBar(final BaseXPanel cmp) {
this(cmp, false);
}
/**
* Default constructor, allowing to modify the scrollbar visibility.
* @param cmp reference to the scrolled component
* @param vis states if scrollbar is always visible or hidden when
* the displayed content needs no scrollbar
*/
private BaseXBar(final BaseXPanel cmp, final boolean vis) {
super(cmp.gui);
comp = cmp;
visible = vis;
addMouseListener(this);
addKeyListener(this);
addMouseMotionListener(this);
mode(Fill.NONE);
BaseXLayout.setWidth(this, SIZE);
ww = SIZE;
}
/**
* Sets the vertical scrollbar slider position.
* @param p vertical position
*/
public void pos(final int p) {
final int pp = Math.max(0, Math.min(height - getHeight(), p));
if(pos != pp) {
pos = pp;
repaint();
}
}
/**
* Returns the vertical scrollbar slider position.
* @return vertical position
*/
public int pos() {
return pos;
}
/**
* Sets the panel height.
* @param h panel height
*/
public void height(final int h) {
if(height != h) {
height = h;
repaint();
}
}
@Override
public void paintComponent(final Graphics g) {
hh = getHeight();
super.paintComponent(g);
if(!visible && hh >= height) return;
// calculate bar size
final int barH = hh - ww * 2 + 4;
final float factor = (barH - barOffset) / (float) height;
int size = (int) (hh * factor);
// define minimum size for scrollbar mover
barOffset = size < MINSIZE ? MINSIZE - size : 0;
size += barOffset;
barSize = Math.min(size, barH - 1);
barPos = (int) Math.max(0, Math.min(pos * factor, barH - barSize));
// paint scrollbar background
g.setColor(GUIConstants.LGRAY);
g.fillRect(0, 0, ww, hh);
// draw scroll slider
int bh = ww - 2 + barPos;
BaseXLayout.drawCell(g, 0, ww, bh, bh + barSize, false);
bh += barSize >> 1;
g.setColor(GUIConstants.DGRAY);
g.drawLine(5, bh, ww - 6, bh);
g.drawLine(5, bh - 2, ww - 6, bh - 2);
g.drawLine(5, bh + 2, ww - 6, bh + 2);
smooth(g);
// draw scroll buttons
drawButton(g, new int[][] { { 0, 6, 3 }, { 6, 6, 0 } },
0, button && up);
drawButton(g, new int[][] { { 0, 6, 3 }, { 0, 0, 6 } },
Math.max(SIZE, hh - ww), button && down);
// paint scrollbar lines
g.setColor(GUIConstants.GRAY);
g.drawLine(0, 0, 0, hh);
g.drawLine(ww - 1, 0, ww - 1, hh);
}
/**
* Draws the down/up button.
* @param g graphics reference
* @param pol polygons
* @param y vertical start value
* @param focus focus flag
*/
private void drawButton(final Graphics g, final int[][] pol, final int y,
final boolean focus) {
BaseXLayout.drawCell(g, 0, ww, y, y + ww, focus);
for(int i = 0; i < pol[0].length; ++i) {
pol[0][i] += SIZE / 2 - 3;
pol[1][i] += y + SIZE / 2 - 3;
}
g.setColor(focus ? Color.black : GUIConstants.DGRAY);
g.fillPolygon(pol[0], pol[1], 3);
}
@Override
public void mousePressed(final MouseEvent e) {
final int y = e.getY();
sliding = y > ww + barPos && y < ww + barPos + barSize;
moving = !sliding;
up = y < ww + barPos;
down = y > ww + barPos + barSize;
button = y < ww || y > hh - ww;
if(sliding) dragPos = barPos - y;
// start dragging
if(sliding || animated) return;
new Thread() {
@Override
public void run() {
// scroll up/down/move slider
animated = moving;
while(animated) {
if(moving) step = Math.max(0, Math.min(STEPS.length - 1,
step + (down ? 1 : -1)));
else step += step < STEPS.length / 2 ? 1 : -1;
int offset = STEPS[step];
if(!button) offset = offset * hh / MAXSTEP / 4;
pos = Math.max(0, Math.min(height - hh, pos + offset));
comp.repaint();
Performance.sleep(25);
animated = step != STEPS.length / 2;
if(y > ww + barPos && y < ww + barPos + barSize) {
dragPos = barPos - y;
animated = false;
sliding = true;
step = STEPS.length / 2;
}
}
}
}.start();
}
@Override
public void mouseReleased(final MouseEvent e) {
up = false;
down = false;
moving = false;
sliding = false;
comp.repaint();
}
@Override
public void mouseDragged(final MouseEvent e) {
// no dragging...
if(!sliding) return;
pos = (int) ((long) (e.getY() + dragPos) * height / (hh - ww * 2));
pos = Math.max(0, Math.min(height - hh, pos));
comp.repaint();
}
}