/*
* 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) 2007 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
*
* Benjamin Sigg
* benjamin_sigg@gmx.ch
* CH - Switzerland
*/
package bibliothek.gui.dock.station.flap;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import bibliothek.gui.DockController;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.FlapDockStation;
import bibliothek.gui.dock.FlapDockStation.Direction;
import bibliothek.gui.dock.security.SecureContainer;
import bibliothek.gui.dock.station.span.Span;
import bibliothek.gui.dock.title.DockTitle;
import bibliothek.gui.dock.util.BackgroundAlgorithm;
import bibliothek.gui.dock.util.ConfiguredBackgroundPanel;
import bibliothek.gui.dock.util.Transparency;
/**
* This panel is used by the {@link FlapDockStation} to display some button-titles.
*/
public class ButtonPane extends SecureContainer{
/** The owner of this panel */
private FlapDockStation station;
/** Information where currently a {@link Dockable} is dropped */
private FlapDropInfo dropInfo;
/** whether {@link #resetTitles()} has been called but not yet executed */
private boolean resetStarted = false;
/** the content pane */
private Content content;
/** handles the {@link Span}s used on this panel */
private FlapSpanStrategy span;
/**
* Constructs a new panel.
* @param station The owner
*/
public ButtonPane( FlapDockStation station ){
span = new FlapSpanStrategy( station, this );
content = new Content();
setBasePane( content );
this.station = station;
}
/**
* Sets the background algorithm of this panel.
* @param background the algorithm
*/
public void setBackground( BackgroundAlgorithm background ){
content.setBackground( background );
}
/**
* Tells where a {@link Dockable} is to be put onto the station
* @param dropInfo the target or <code>null</code>
*/
public void setDropInfo( FlapDropInfo dropInfo ) {
if( dropInfo == null ){
span.untease();
}
else if( dropInfo.getCombineTarget() != null ){
span.tease( -1 );
}
else{
int index = dropInfo.getIndex();
if( index < station.getDockableCount() && station.getDockable( index ) == dropInfo.getDockable()){
dropInfo = null;
span.tease( -1 );
}
else if( index-1 >= 0 && index-1 < station.getDockableCount() && station.getDockable( index-1 ) == dropInfo.getDockable()){
dropInfo = null;
span.tease( -1 );
}
else{
span.tease( dropInfo.getIndex() );
}
}
this.dropInfo = dropInfo;
repaint();
}
/**
* Tells whether the given point is inside a button.
* @param x the x-coordinate
* @param y the y-coordinate
* @return <code>true</code> if the point is inside a child of this panel
*/
public boolean titleContains( int x, int y ){
if( getComponentCount() == 0 ){
int w = getWidth() / 3;
int h = getHeight() / 3;
return contains( x - w, y - h ) && contains( x + w, y + h );
}
for( int i = 0, n = getComponentCount(); i<n; i++ ){
Component c = getComponent( i );
if( c.contains( x - c.getX(), y - c.getY() ))
return true;
}
return false;
}
/**
* Called by the owning {@link FlapDockStation} if the {@link DockController} changes.
* @param controller the new controller, can be <code>null</code>
*/
public void setProperties( DockController controller ){
span.setController( controller );
}
/**
* Ensures that all titles of the title-map, which was given to the
* constructor, are shown on this panel. This method works asynchronous.
*/
public void resetTitles(){
if( !resetStarted ){
Runnable code = new Runnable() {
public void run(){
resetStarted = false;
getContentPane().removeAll();
for( int i = 0, n = station.getDockableCount(); i<n; i++ ){
DockTitle title = station.getButton( i );
if( title != null ){
getContentPane().add( title.getComponent() );
}
}
span.reset();
revalidate();
}
};
resetStarted = true;
if( EventQueue.isDispatchThread() ){
EventQueue.invokeLater( code );
}
else{
code.run();
}
}
}
@Override
protected void paintOverlay( Graphics g ) {
if( dropInfo != null && dropInfo.getCombineTarget() == null ){
int left = dropInfo.getIndex()-1;
int right = left+1;
DockTitle leftTitle = left < 0 ? null : station.getButton( left );
DockTitle rightTitle = right >= station.getDockableCount() ? null : station.getButton( right );
boolean horizontal = station.getDirection() == Direction.SOUTH || station.getDirection() == Direction.NORTH;
int x1, y1, x2, y2;
if( leftTitle == null && rightTitle == null ){
if( horizontal ){
x1 = 0;
y1 = 0;
x2 = getWidth();
y2 = getHeight();
}
else{
x1 = 0;
y1 = 0;
x2 = getWidth();
y2 = getHeight();
}
}
else if( leftTitle == null ){
if( horizontal ){
x1 = 0;
y1 = 0;
x2 = rightTitle.getComponent().getX();
y2 = getHeight();
}
else{
x1 = 0;
y1 = 0;
x2 = getWidth();
y2 = rightTitle.getComponent().getY();
}
}
else if( rightTitle == null ){
int last = getNumberOfButtons();
if( horizontal ){
x1 = leftTitle.getComponent().getX() + leftTitle.getComponent().getWidth();
y1 = 0;
x2 = x1 + span.getGap( last );
y2 = getHeight();
}
else{
x1 = 0;
y1 = leftTitle.getComponent().getY() + leftTitle.getComponent().getHeight();
x2 = getWidth();
y2 = y1 + span.getGap( last );
}
}
else{
if( horizontal ){
x1 = leftTitle.getComponent().getX() + leftTitle.getComponent().getWidth();
y1 = 0;
x2 = rightTitle.getComponent().getX();
y2 = getHeight();
}
else{
x1 = 0;
y1 = leftTitle.getComponent().getY() + leftTitle.getComponent().getHeight();
x2 = getWidth();
y2 = rightTitle.getComponent().getY();
}
}
x1++;
y1++;
x2--;
y2--;
if( x1 >= x2 || y1 >= y2 ){
if( horizontal ){
x1 = (x1 + x2) / 2;
x2 = x1;
}
else{
y1 = (y1 + y2) / 2;
y2 = y1;
}
station.getPaint().drawInsertionLine( g, x1, y1, x2, y2 );
}
else{
station.getPaint().drawInsertion( g, new Rectangle( 0, 0, getWidth(), getHeight()), new Rectangle( x1, y1, x2-x1, y2-y1 ));
}
}
}
/**
* Searches an location where a {@link Dockable} could be inserted.
* @param x the x-coordinate of the mouse
* @param y the y-coordinate of the mouse
* @return a location
*/
public int indexAt( int x, int y ){
if( station.getDirection() == Direction.SOUTH || station.getDirection() == Direction.NORTH ){
for( int i = 0, n = station.getDockableCount(); i<n; i++ ){
DockTitle title = station.getButton( i );
if( title != null ){
int tx = title.getComponent().getX();
int tw = title.getComponent().getWidth();
if( x <= tx )
return i;
if( x <= tx + tw ){
if( x < tx + tw/2 )
return i;
else
return i+1;
}
}
}
}
else{
for( int i = 0, n = station.getDockableCount(); i<n; i++ ){
DockTitle title = station.getButton( i );
if( title != null ){
int ty = title.getComponent().getY();
int th = title.getComponent().getHeight();
if( y <= ty )
return i;
if( y <= ty + th ){
if( y < ty + th/2 )
return i;
else
return i+1;
}
}
}
}
return station.getDockableCount();
}
@Override
public Dimension getMinimumSize() {
return getPreferredSize();
}
@Override
public Dimension getPreferredSize() {
int width = 0;
int height = 0;
int count = station.getDockableCount();
if( station.getDirection() == Direction.NORTH || station.getDirection() == Direction.SOUTH ){
height = span.getTeasing();
for( int i = 0; i<count; i++ ){
DockTitle title = station.getButton( i );
if( title != null ){
Dimension size = title.getComponent().getPreferredSize();
width += size.width;
height = Math.max( height, size.height );
}
width += span.getGap( i );
}
if( count > 0 ){
width += span.getGap( count );
}
}
else{
width = span.getTeasing();
for( int i = 0; i<count; i++ ){
DockTitle title = station.getButton( i );
if( title != null ){
Dimension size = title.getComponent().getPreferredSize();
height += size.height;
width = Math.max( width, size.width );
}
height += span.getGap( i );
}
if( count > 0 ){
height += span.getGap( count );
}
}
Dimension empty = station.getMinimumSize();
return new Dimension( Math.max( empty.width, width ), Math.max( empty.height, height ));
}
/**
* Gets the number of buttons that are actually shown.
* @return the number of buttons
*/
public int getNumberOfButtons(){
return getContentPane().getComponentCount();
}
/**
* Called if the {@link Span}s used by this {@link ButtonPane} changed their size.
*/
public void spanResized(){
getContentPane().revalidate();
revalidate();
}
/**
* The direct parent of the buttons-titles.
* @author Benjamin Sigg
*/
private class Content extends ConfiguredBackgroundPanel{
/**
* Creates a new panel
*/
public Content(){
super( null, Transparency.SOLID );
}
@Override
public void setTransparency( Transparency transparency ){
super.setTransparency( transparency );
ButtonPane.this.setSolid( transparency == Transparency.SOLID );
}
@Override
public void doLayout() {
Insets insets = getInsets();
int x = insets.left;
int y = insets.top;
int width = getWidth() - insets.left - insets.right;
int height = getHeight() - insets.top - insets.bottom;
if( station.getDirection() == Direction.NORTH || station.getDirection() == Direction.SOUTH ){
int count = getComponentCount();
int[] widths = new int[ count ];
int preferredHeight = 0;
int sum = 0;
for( int i = 0; i < count; i++ ){
Dimension size = getComponent( i ).getPreferredSize();
widths[i] = size.width;
preferredHeight = Math.max( preferredHeight, size.height );
sum += widths[i];
sum += span.getGap( i );
}
if( count > 0 ){
sum += span.getGap( count );
}
if( station.isSmallButtons() && preferredHeight < height ){
int delta = height - preferredHeight;
y += delta/2;
height = preferredHeight;
}
if( sum > width ){
double ratio = ((double)width) / sum;
for( int i = 0; i < count; i++ ){
x += span.getGap( i );
int temp = (int)(widths[i]*ratio);
getComponent( i ).setBounds( x, y, temp, height );
x += temp;
}
}
else{
for( int i = 0; i < count; i++ ){
x += span.getGap( i );
getComponent( i ).setBounds( x, y, widths[i], height );
x += widths[i];
}
}
}
else{
int count = getComponentCount();
int[] heights = new int[ count ];
int preferredWidth = 0;
int sum = 0;
for( int i = 0; i < count; i++ ){
Dimension size = getComponent( i ).getPreferredSize();
heights[i] = size.height;
preferredWidth = Math.max( preferredWidth, size.width );
sum += heights[i];
sum += span.getGap( i );
}
if( count > 0 ){
sum += span.getGap( count );
}
if( station.isSmallButtons() && preferredWidth < width ){
int delta = width - preferredWidth;
x += delta/2;
width = preferredWidth;
}
if( sum > height ){
double ratio = ((double)height) / sum;
for( int i = 0; i < count; i++ ){
y += span.getGap( i );
int temp = (int)(heights[i]*ratio);
getComponent( i ).setBounds( x, y, width, temp );
y += temp;
}
}
else{
for( int i = 0; i < count; i++ ){
y += span.getGap( i );
getComponent( i ).setBounds( x, y, width, heights[i] );
y += heights[i];
}
}
}
repaint();
}
}
}