/*
* 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.extension.gui.dock.theme.eclipse;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.Icon;
import javax.swing.JComponent;
import bibliothek.gui.dock.action.DropDownAction;
import bibliothek.gui.dock.control.focus.FocusAwareComponent;
import bibliothek.gui.dock.themes.basic.action.BasicButtonModel;
import bibliothek.gui.dock.themes.basic.action.BasicButtonModelAdapter;
import bibliothek.gui.dock.themes.basic.action.BasicDropDownButtonHandler;
import bibliothek.gui.dock.themes.basic.action.BasicDropDownButtonModel;
import bibliothek.gui.dock.themes.basic.action.buttons.MiniButtonContent;
import bibliothek.gui.dock.util.AbstractPaintableComponent;
import bibliothek.gui.dock.util.BackgroundComponent;
import bibliothek.gui.dock.util.BackgroundPaint;
import bibliothek.gui.dock.util.Transparency;
import bibliothek.gui.dock.util.color.ColorCodes;
import bibliothek.util.Colors;
/**
* A button with a shape of a roundrect, displaying a {@link DropDownAction}.
* @author Benjamin Sigg
*/
@ColorCodes({
"action.button.text",
"action.button.text.disabled"
})
public class RoundRectDropDownButton extends JComponent implements FocusAwareComponent {
/** a model containing all information needed to paint this button */
private BasicDropDownButtonModel model;
/** a handler reacting if this button is pressed */
private BasicDropDownButtonHandler handler;
/** the icon to show for the area in which the popup-menu could be opened */
private Icon dropIcon;
/** a disabled version of {@link #dropIcon} */
private Icon disabledDropIcon;
/** a piece of code that will be executed after this component requests focus */
private Runnable afterFocusRequest;
/** Component painting icon and text */
private MiniButtonContent content;
/**
* Creates a new button
* @param handler a handler used to announce that this button is clicked
*/
public RoundRectDropDownButton( BasicDropDownButtonHandler handler ){
this.handler = handler;
model = new BasicDropDownButtonModel( this, handler, handler, true ){
@Override
protected boolean inDropDownArea( int x, int y ) {
return RoundRectDropDownButton.this.inDropDownArea( x, y );
}
@Override
public void changed() {
revalidate();
super.changed();
}
};
setOpaque( false );
content = createButtonContent();
setLayout( null );
add( content );
content.setModel( model );
content.setForegroundColorId( "action.button.text", "action.button.text.disabled" );
dropIcon = handler.getDropDownIcon();
model.addListener( new BasicButtonModelAdapter(){
@Override
public void mousePressed( BasicButtonModel model, boolean mousePressed ){
if( !mousePressed ){
requestFocusInWindow();
invokeAfterFocusRequest();
}
}
});
addFocusListener( new FocusListener(){
public void focusGained( FocusEvent e ) {
repaint();
}
public void focusLost( FocusEvent e ) {
repaint();
}
});
}
/**
* Creates a new component that paints icon and text
* @return the new component
*/
protected MiniButtonContent createButtonContent(){
return new MiniButtonContent();
}
public void maybeRequestFocus(){
afterFocusRequest = null;
EventQueue.invokeLater(new Runnable(){
public void run(){
if( !model.isMousePressed() ){
requestFocusInWindow();
invokeAfterFocusRequest();
}
}
});
}
public void invokeOnFocusRequest( Runnable run ){
afterFocusRequest = run;
}
private void invokeAfterFocusRequest(){
EventQueue.invokeLater(new Runnable(){
public void run(){
if( afterFocusRequest != null ){
afterFocusRequest.run();
afterFocusRequest = null;
}
}
});
}
/**
* Gets the model which represents the inner states of this button.
* @return the model
*/
public BasicDropDownButtonModel getModel() {
return model;
}
@Override
public Dimension getPreferredSize() {
if( isPreferredSizeSet() )
return super.getPreferredSize();
Dimension size = content.getPreferredSize();
if( model.getOrientation().isHorizontal() )
return new Dimension( size.width + 6 + dropIcon.getIconWidth(), size.height+2 );
else
return new Dimension( size.width+2, size.height + 6 + dropIcon.getIconHeight() );
}
@Override
public void doLayout(){
if( model.getOrientation().isHorizontal() ){
content.setBounds( 1, 1, getWidth()-5-dropIcon.getIconWidth(), getHeight()-2 );
}
else{
content.setBounds( 1, 1, getWidth()-2, getHeight()-5-dropIcon.getIconHeight() );
}
}
@Override
protected void paintComponent( Graphics g ) {
BasicDropDownButtonModel model = getModel();
BackgroundPaint paint = model.getBackground();
BackgroundComponent component = model.getBackgroundComponent();
AbstractPaintableComponent paintable = new AbstractPaintableComponent( component, this, paint ){
protected void foreground( Graphics g ){
doPaintForeground( g );
}
protected void background( Graphics g ){
doPaintBackground( g );
}
protected void border( Graphics g ){
// ignore
}
protected void children( Graphics g ){
// ignore
}
protected void overlay( Graphics g ){
// ignore
}
public Transparency getTransparency(){
return Transparency.DEFAULT;
}
};
paintable.paint( g );
}
private void doPaintBackground( Graphics g ){
Color background = getBackground();
Color border = null;
if( model.isMousePressed() ){
border = Colors.diffMirror( background, 0.3 );
background = Colors.undiffMirror( background, 0.8 );
}
else if( model.isSelected() || model.isMouseInside() ){
border = Colors.diffMirror( background, 0.3 );
background = Colors.undiffMirror( background, 0.4 );
}
int w = getWidth()-1;
int h = getHeight()-1;
if( border != null ){
g.setColor( background );
g.fillRoundRect( 0, 0, w, h, 4, 4 );
}
}
private void doPaintForeground( Graphics g ){
Icon drop = dropIcon;
if( !isEnabled() ){
if( disabledDropIcon == null )
disabledDropIcon = handler.getDisabledDropDownIcon();
drop = disabledDropIcon;
}
Color background = getBackground();
Color border = null;
if( model.isMousePressed() ){
border = Colors.diffMirror( background, 0.3 );
background = Colors.undiffMirror( background, 0.8 );
}
else if( model.isSelected() || model.isMouseInside() ){
border = Colors.diffMirror( background, 0.3 );
background = Colors.undiffMirror( background, 0.4 );
}
int w = getWidth()-1;
int h = getHeight()-1;
if( border != null ){
g.setColor( border );
g.drawRoundRect( 0, 0, w, h, 4, 4 );
if( model.isMouseOverDropDown() ){
if( model.getOrientation().isHorizontal() ){
int x = w - drop.getIconWidth() - 5;
g.drawLine( x, 0, x, h );
}
else{
int y = h - drop.getIconHeight() - 5;
g.drawLine( 0, y, w, y );
}
}
}
if( model.getOrientation().isHorizontal() ){
drop.paintIcon( this, g, w - drop.getIconWidth() - 2, (h - drop.getIconHeight())/2 );
}
else{
drop.paintIcon( this, g, (w - drop.getIconWidth())/2, h - drop.getIconHeight() - 2 );
}
if( hasFocus() && isFocusable() && isEnabled() ){
g.setColor( Colors.diffMirror( background, 0.4 ) );
// top left
g.drawLine( 2, 3, 2, 4 );
g.drawLine( 3, 2, 4, 2 );
// top right
g.drawLine( w-2, 3, w-2, 4 );
g.drawLine( w-3, 2, w-4, 2 );
// bottom left
g.drawLine( 2, h-3, 2, h-4 );
g.drawLine( 3, h-2, 4, h-2 );
// bottom right
g.drawLine( w-2, h-3, w-2, h-4 );
g.drawLine( w-3, h-2, w-4, h-2 );
}
}
/**
* Tells whether the location <code>x/y</code> is within the area
* that will always trigger the dropdown menu.
* @param x some x coordinate
* @param y some y coordinate
* @return <code>true</code> if the point x/y is within the dropdown-area
*/
public boolean inDropDownArea( int x, int y ){
if( !contains( x, y ))
return false;
if( model.getOrientation().isHorizontal() ){
return x >= getWidth() - dropIcon.getIconWidth() - 5;
}
else{
return y >= getHeight() - dropIcon.getIconHeight() - 5;
}
}
@Override
public void updateUI() {
disabledDropIcon = null;
super.updateUI();
if( handler != null )
handler.updateUI();
}
}