/*
* This file is part of MoleculeViewer.
*
* MoleculeViewer 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 3 of the License, or
* (at your option) any later version.
*
* MoleculeViewer 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 MoleculeViewer. If not, see <http://www.gnu.org/licenses/>.
*/
package astex.splitter;
import java.awt.*;
import java.awt.event.*;
/** A class that implements a splitter bar. It is only intended to be used
* in a SplitterLayout. Because it is dervied from Panel, a SplitterBar
* can do anything a normal panel can do. However, if you add any
* components to this panel, the scroll handle will not be accessible. In a
* case like this, you need to explicitly add a SplitterSpace component to the
* SplitterBar, guaranteeing a place for the handle to be available.
*
* <p>Use this code at your own risk! MageLang Institute is not
* responsible for any damage caused directly or indirctly through
* use of this code.
* <p><p>
* <b>SOFTWARE RIGHTS</b>
* <p>
* MageLang support classes, version 1.0, MageLang Institute
* <p>
* We reserve no legal rights to this code--it is fully in the
* public domain. An individual or company may do whatever
* they wish with source code distributed with it, including
* including the incorporation of it into commerical software.
*
* <p>However, this code cannot be sold as a standalone product.
* <p>
* We encourage users to develop software with this code. However,
* we do ask that credit is given to us for developing it
* By "credit", we mean that if you use these components or
* incorporate any source code into one of your programs
* (commercial product, research project, or otherwise) that
* you acknowledge this fact somewhere in the documentation,
* research report, etc... If you like these components and have
* developed a nice tool with the output, please mention that
* you developed it using these components. In addition, we ask that
* the headers remain intact in our source code. As long as these
* guidelines are kept, we expect to continue enhancing this
* system and expect to make other tools available as they are
* completed.
* <p>
* The MageLang Support Classes Gang:
* @version MageLang Support Classes 1.0, MageLang Insitute, 1997
* @author <a href="http:www.scruz.net/~thetick">Scott Stanchfield</a>, <a href=http://www.MageLang.com>MageLang Institute</a>
* @see SplitterLayout
* @see SplitterSpace
*/
public class SplitterBar extends Panel {
static final Cursor VERT_CURSOR = new Cursor(Cursor.N_RESIZE_CURSOR);
static final Cursor HORIZ_CURSOR = new Cursor(Cursor.E_RESIZE_CURSOR);
static final Cursor DEF_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
private SplitterLayout.Orientation orientation = SplitterLayout.Orientation.VERTICAL;
private boolean alreadyDrawn = false;
private Rectangle originalBounds=null;
private Window wBar;
public Dimension getPreferredSize(){
return new Dimension(6, 6);
}
/** Creates a new SpliiterBar */
public SplitterBar() {
setBackground(new Color(0xe6e6e6));
addMouseMotionListener(new SplitterBarMouseMotionListener(this));
addMouseListener(new SplitterBarMouseListener(this));
}
private void checkOtherComponents() {
Rectangle currBounds = getBounds(); // get current position
Component comps[] = getParent().getComponents();
Insets insets = getParent().getInsets();
Rectangle parentBounds = getParent().getBounds();
// determine which component "this" is
int curr;
for(curr=0; (curr < comps.length) && (comps[curr] != this); curr++);
int origCurr = curr; // hold for part II check
if (orientation == SplitterLayout.Orientation.VERTICAL) {
if (currBounds.y < originalBounds.y) { // moved up
// could have moved _into_ splitter bars above (or top edge)
// and/or away from splitter bars below (or bottom edge)
// check to see it we've bumped into a splitter above us.
boolean done=false;
for (int temp=curr-1; !done && temp > -1; temp--) {
if (comps[temp] instanceof SplitterBar) {
Rectangle r = comps[temp].getBounds();
if (currBounds.y <= r.y + r.height) { // touching or above...
comps[temp].setLocation(r.x, currBounds.y - r.height);
// any comps in between should be hidden
for(int c=curr-1; c > temp; c--)
comps[c].setVisible(false);
curr = temp;
currBounds = comps[temp].getBounds();
} // touching or above
else done=true; // no more compression
} // it's a splitter bar
} // for each component before us
// did we push to far?
if (currBounds.y <= insets.top) {
int delta = currBounds.y - insets.top;
// hide all components before that one
for(int temp=curr-1; temp > -1; temp--)
comps[temp].setVisible(false);
// push all splitter bars into view
done = false;
for(int temp=curr;!done && temp <= origCurr; temp++)
if (comps[temp] instanceof SplitterBar) {
Point p = comps[temp].getLocation();
p.y -= delta;
comps[temp].setLocation(p);
}
else done = comps[temp].isVisible();
} // pushed highest component off top edge
// next, check if we exposed components below us
curr = origCurr;
// if the next component is not visible, show all between us & next
// splitter bar or bottom edge
for(int temp=curr+1; temp<comps.length && !comps[temp].isVisible(); temp++)
comps[temp].setVisible(true);
} // VERTICAL -- moved up
else if (currBounds.y > originalBounds.y) { // moved down
// could have moved _into_ splitter bars below (or bottom edge)
// and/or away from splitter bars above (or top edge)
// check to see it we've bumped into a splitter below us.
boolean done=false;
for (int temp=curr+1; !done && temp < comps.length; temp++) {
if (comps[temp] instanceof SplitterBar) {
Rectangle r = comps[temp].getBounds();
if (currBounds.y + currBounds.height >= r.y) { // touching or below...
comps[temp].setLocation(r.x, currBounds.y + currBounds.height);
// any comps in between should be hidden
for(int c=curr+1; c < temp; c++)
comps[c].setVisible(false);
curr = temp;
currBounds = comps[temp].getBounds();
} // touching or above
else done=true; // no more compression
} // it's a splitter bar
} // for each component before us
// did we push to far?
if ((currBounds.y + currBounds.height) >= (parentBounds.height-insets.bottom)) {
int delta = currBounds.y + currBounds.height - (parentBounds.height-insets.bottom);
// hide all components before that one
for(int temp=curr+1; temp < comps.length; temp++)
comps[temp].setVisible(false);
// push all splitter bars into view
done = false;
for(int temp=curr;!done && temp >= origCurr; temp--)
if (comps[temp] instanceof SplitterBar) {
Point p = comps[temp].getLocation();
p.y -= delta;
comps[temp].setLocation(p);
}
else done = comps[temp].isVisible();
} // pushed highest component off top edge
// next, check if we exposed components below us
curr = origCurr;
// if the next component is not visible, show all between us & next
// splitter bar or bottom edge
for(int temp=curr-1; temp>-1 && !comps[temp].isVisible(); temp--)
comps[temp].setVisible(true);
} // VERTICAL -- moved down
} // orientation==VERTICAL
else { // orientation == HORIZONTAL
if (currBounds.x < originalBounds.x) { // moved left
// could have moved _into_ splitter bars to left (or left edge)
// and/or away from splitter bars to right (or right edge)
// check to see it we've bumped into a splitter above us.
boolean done=false;
for (int temp=curr-1; !done && temp > -1; temp--) {
if (comps[temp] instanceof SplitterBar) {
Rectangle r = comps[temp].getBounds();
if (currBounds.x <= r.x + r.width) { // touching or above...
comps[temp].setLocation(currBounds.x - r.width, r.y);
// any comps in between should be hidden
for(int c=curr-1; c > temp; c--)
comps[c].setVisible(false);
curr = temp;
currBounds = comps[temp].getBounds();
} // touching or above
else done=true; // no more compression
} // it's a splitter bar
} // for each component before us
// did we push to far?
if (currBounds.x <= insets.left) {
int delta = currBounds.x - insets.left;
// hide all components before that one
for(int temp=curr-1; temp > -1; temp--)
comps[temp].setVisible(false);
// push all splitter bars into view
done = false;
for(int temp=curr;!done && temp <= origCurr; temp++)
if (comps[temp] instanceof SplitterBar) {
Point p = comps[temp].getLocation();
p.x -= delta;
comps[temp].setLocation(p);
}
else done = comps[temp].isVisible();
} // pushed highest component off top edge
// next, check if we exposed components below us
curr = origCurr;
// if the next component is not visible, show all between us & next
// splitter bar or bottom edge
for(int temp=curr+1; temp<comps.length && !comps[temp].isVisible(); temp++)
comps[temp].setVisible(true);
} // HORIZONTAL -- moved left
else if (currBounds.x > originalBounds.x) { // moved right
// could have moved _into_ splitter bars to right (or right edge)
// and/or away from splitter bars to left (or left edge)
// check to see it we've bumped into a splitter to our right us.
boolean done=false;
for (int temp=curr+1; !done && temp < comps.length; temp++) {
if (comps[temp] instanceof SplitterBar) {
Rectangle r = comps[temp].getBounds();
if (currBounds.x + currBounds.width >= r.x) { // touching or to right...
comps[temp].setLocation(currBounds.x + currBounds.width, r.y);
// any comps in between should be hidden
for(int c=curr+1; c < temp; c++)
comps[c].setVisible(false);
curr = temp;
currBounds = comps[temp].getBounds();
} // touching or above
else done=true; // no more compression
} // it's a splitter bar
} // for each component before us
// did we push to far?
if ((currBounds.x + currBounds.width) >= (parentBounds.width-insets.right)) {
int delta = currBounds.x + currBounds.width - (parentBounds.width-insets.right);
// hide all components before that one
for(int temp=curr+1; temp < comps.length; temp++)
comps[temp].setVisible(false);
// push all splitter bars into view
done = false;
for(int temp=curr;!done && temp >= origCurr; temp--)
if (comps[temp] instanceof SplitterBar) {
Point p = comps[temp].getLocation();
p.x -= delta;
comps[temp].setLocation(p);
}
else done = comps[temp].isVisible();
} // pushed highest component off top edge
// next, check if we exposed components below us
curr = origCurr;
// if the next component is not visible, show all between us & next
// splitter bar or bottom edge
for(int temp=curr-1; temp>-1 && !comps[temp].isVisible(); temp--)
comps[temp].setVisible(true);
} // HORIZONTAL -- moved right
} // orientation==HORIZONTAL
} // checkComponents()
public SplitterLayout.Orientation getOrientation() {return orientation;}
public void mouseDrag(MouseEvent e) {
if (SplitterLayout.dragee == null)
SplitterLayout.dragee = this;
else if (SplitterLayout.dragee != this)
return;
Component c = getParent();
Point fl = c.getLocationOnScreen();
while(c.getParent() != null)
c = c.getParent();
if (!alreadyDrawn) {
originalBounds = getBounds();
wBar = new Window((Frame)c);
wBar.setBackground(getBackground().darker());
}
Container cp = getParent();
Dimension parentDim = cp.getSize();
Point l = getLocationOnScreen();
Insets insets = (cp).getInsets();
if (orientation == SplitterLayout.Orientation.VERTICAL)
parentDim.width -= insets.right + insets.left;
else
parentDim.height -= insets.top + insets.bottom;
Rectangle r = getBounds(); // mouse event is relative to this...
int x = l.x+(orientation==SplitterLayout.Orientation.HORIZONTAL?e.getX():0);
int y = l.y+(orientation==SplitterLayout.Orientation.VERTICAL?e.getY():0);
if (x<fl.x+insets.left) x = fl.x+insets.left;
else if ((orientation==SplitterLayout.Orientation.HORIZONTAL) && (x > fl.x+parentDim.width-r.width))
x = fl.x+parentDim.width-r.width;
if (y<fl.y+insets.top) y = fl.y+insets.top;
else if ((orientation==SplitterLayout.Orientation.VERTICAL) && (y > fl.y+parentDim.height-r.height))
y = fl.y+parentDim.height-r.height;
wBar.setBounds(x,y,
(orientation==SplitterLayout.Orientation.HORIZONTAL)?r.width:parentDim.width,
(orientation==SplitterLayout.Orientation.VERTICAL)?r.height:parentDim.height);
if (!alreadyDrawn) {
wBar.setVisible(true);
alreadyDrawn=true;
}
}
public void mouseEnter(MouseEvent e) {
if (SplitterLayout.dragee != null) return;
setCursor((orientation == SplitterLayout.Orientation.VERTICAL)?VERT_CURSOR:HORIZ_CURSOR);
invalidate();
validate();
repaint();
}
public void mouseExit(MouseEvent e) {
if (SplitterLayout.dragee != null) return;
setCursor(DEF_CURSOR);
invalidate();
validate();
repaint();
}
public void mouseRelease(MouseEvent e) {
if (alreadyDrawn) {
if (SplitterLayout.dragee != this) return;
SplitterLayout.dragee = null;
wBar.setVisible(false); wBar.dispose(); wBar=null; alreadyDrawn=false;
Rectangle r = getBounds(); // mouse event is relative to this...
r.x += (orientation==SplitterLayout.Orientation.HORIZONTAL?e.getX():0);
r.y += (orientation==SplitterLayout.Orientation.VERTICAL?e.getY():0);
setLocation(r.x, r.y);
setCursor(DEF_CURSOR);
// check to see if we need to move other splitters and hide other
// components that are controlled by the layout
// First -- find what component this one is
checkOtherComponents();
invalidate();
getParent().validate();
SplitterLayout.dragee = null;
}
}
/** Paints the image of a SplitterBar. If nothing was added to
the SplitterBar, this image will only be a thin, 3D raised line that
will act like a handle for moving the SplitterBar.
If other components were added the SplitterBar, the thin 3D raised
line will onlty appear where SplitterSpace components were added.
*/
public void paint (Graphics g) {
g.setColor(new Color(0x89899a));
Component c[] = getComponents();
if (c == null || c.length == 0) {
Rectangle r = getBounds();
if (orientation == SplitterLayout.Orientation.VERTICAL){
g.drawLine(2, r.height/2 -12, 2, r.height/2 + 12);
g.drawLine(r.width - 2, r.height/2 -12, r.width - 2, r.height/2 + 12);
}else{
g.drawLine(2, r.height/2 -12, 2, r.height/2 + 12);
g.drawLine(r.width - 2, r.height/2 -12, r.width - 2, r.height/2 + 12);
}
}
}
public void setOrientation(SplitterLayout.Orientation o) {orientation = o;}
public void swapOrientation() {
setOrientation(orientation==SplitterLayout.Orientation.HORIZONTAL ? SplitterLayout.Orientation.VERTICAL : SplitterLayout.Orientation.HORIZONTAL);
}
/** Called by AWT to update the image produced by the SplitterBar */
public void update (Graphics g) {paint(g);}
}