All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bibliothek.gui.dock.station.toolbar.menu.DefaultCustomizationMenu Maven / Gradle / Ivy

There is a newer version: 1.1.2p6a
Show newest version
/*
 * 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
 * [email protected]
 * FR - France
 *
 * Benjamin Sigg
 * [email protected]
 * 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 null
	 * @return the actual boundaries, can be null to indicate that bounds 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 owner
	 * 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();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy