/*
* 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.menu;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;
import bibliothek.util.Todo;
import bibliothek.util.Todo.Compatibility;
import bibliothek.util.Todo.Priority;
import bibliothek.util.Todo.Version;
/**
* The default {@link CustomizationMenu} makes use of a {@link JDialog} to show its contents.
* @author Benjamin Sigg
*/
@Todo( compatibility=Compatibility.COMPATIBLE, target=Version.VERSION_1_1_2, priority=Priority.MINOR,
description="This class is almost identical with 'DialogWindow' from Common, maybe these two classes can be merged?")
public class DefaultCustomizationMenu extends AbstractCustomizationMenu{
/** the dialog that is this menu */
private JDialog dialog;
/** whether the dialog has any decorations at all */
private boolean undecorated = true;
/** whether to close this menu if the focus is lost */
private boolean closeOnFocusLost = true;
/**
* Makes the dialog that is used by this menu undecorated. Calling this method has no effect if
* the menu is already visible.
* @param undecorated whether to {@link JDialog#setUndecorated(boolean) undecorate} the dialog
*/
public void setUndecorated( boolean undecorated ){
this.undecorated = undecorated;
}
/**
* Tells whether the {@link JDialog} of this menu is undecorated.
* @return the result of {@link JDialog#isUndecorated()}
*/
public boolean isUndecorated(){
return undecorated;
}
/**
* Automatically closes this menu if the dialog loses focus.
* @param closeOnFocusLost whether to automatically close the menu
*/
public void setCloseOnFocusLost( boolean closeOnFocusLost ){
this.closeOnFocusLost = closeOnFocusLost;
}
/**
* Whether the menu automatically is closed if it loses focus.
* @return whether to automatically close the menu
*/
public boolean isCloseOnFocusLost(){
return closeOnFocusLost;
}
@Override
protected void doOpen( int x, int y, Component content ){
dialog = createDialog( getCallback().getParent() );
dialog.setDefaultCloseOperation( JDialog.DO_NOTHING_ON_CLOSE );
dialog.addWindowListener( new WindowAdapter(){
@Override
public void windowClosed( WindowEvent e ){
closed();
}
public void windowClosing( WindowEvent e ){
close();
}
@Override
public void windowDeactivated( WindowEvent e ){
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
CustomizationMenuCallback callback = getCallback();
if( isCloseOnFocusLost() && (callback == null || callback.isAutoCloseAllowed()) ){
close();
}
}
});
}
});
dialog.setUndecorated( undecorated );
dialog.add( content, BorderLayout.CENTER );
dialog.pack();
dialog.setLocation( x, y );
validateBounds();
dialog.setVisible( true );
}
@Override
protected void doClose(){
dialog.dispose();
dialog.getContentPane().removeAll();
dialog = null;
}
private void validateBounds(){
Rectangle bounds = dialog.getBounds();
Point location = dialog.getLocation();
GraphicsConfiguration bestConfiguration = null;
int bestDistance = 0;
GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
for( GraphicsDevice device : environment.getScreenDevices() ){
GraphicsConfiguration configuration = device.getDefaultConfiguration();
Rectangle screenBounds = configuration.getBounds();
if( screenBounds.contains( location )){
bestConfiguration = configuration;
bestDistance = 0;
}
else{
int dx;
int dy;
if( screenBounds.x <= location.x && screenBounds.x + screenBounds.width >= location.x ){
dx = 0;
}
else{
dx = Math.min(Math.abs(screenBounds.x - location.x), Math.abs(screenBounds.x + screenBounds.width - location.x));
}
if( screenBounds.y <= location.y && screenBounds.y + screenBounds.height >= location.y ){
dy = 0;
}
else{
dy = Math.min(Math.abs(screenBounds.y - location.y), Math.abs(screenBounds.y + screenBounds.height - location.y));
}
int delta = dx + dy;
if( delta < bestDistance || bestConfiguration == null ){
bestDistance = delta;
bestConfiguration = configuration;
}
}
}
bounds = validateBounds( bounds, bestConfiguration );
if( bounds != null ){
dialog.setBounds( bounds );
}
}
/**
* Should be called before this window is made visible, ensure that the boundaries are valid.
* @param bounds the proposed boundaries
* @param configuration the screen on which this window is going to be visible, might be <code>null</code>
* @return the actual boundaries, can be <code>null</code> to indicate that <code>bounds</code> is valid
*/
protected Rectangle validateBounds( Rectangle bounds, GraphicsConfiguration configuration ){
if( configuration == null ){
return null;
}
Rectangle screen = configuration.getBounds();
bounds = new Rectangle( bounds );
bounds.width = Math.min( bounds.width, screen.width );
bounds.height = Math.min( bounds.height, screen.height );
bounds.x = Math.min( Math.max( bounds.x, screen.x ), screen.x + screen.width - bounds.width );
bounds.y = Math.min( Math.max( bounds.y, screen.y ), screen.y + screen.height - bounds.height );
return bounds;
}
/**
* Creates a new dialog with the ancestor window of <code>owner</code>
* as owner.
* @param owner some component
* @return the new dialog
*/
protected JDialog createDialog( Component owner ){
Window window = SwingUtilities.getWindowAncestor( owner );
if( window instanceof Frame )
return new JDialog( (Frame)window );
if( window instanceof Dialog )
return new JDialog( (Dialog)window );
return new JDialog();
}
}