/* * 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.title; import java.awt.Dimension; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import javax.swing.Icon; import javax.swing.JComponent; import bibliothek.gui.DockController; import bibliothek.gui.Dockable; import bibliothek.gui.dock.action.ActionPopup; import bibliothek.gui.dock.action.DockAction; import bibliothek.gui.dock.action.DockActionSource; import bibliothek.gui.dock.themes.basic.action.BasicTitleViewItem; import bibliothek.gui.dock.themes.basic.action.buttons.ButtonPanel; import bibliothek.gui.dock.util.swing.OrientedLabel; /** * An abstract implementation of {@link DockTitle}. This title can have * an icon, a title-text and some small buttons to display {@link DockAction actions}. * The icon is at the top or left edge, the text in the middle, and the actions * at the lower or the right edge of the title. If the orientation of the * title is set to {@link DockTitle.Orientation vertical}, the text will be rotated * by 90 degrees.<br> * This title has also an {@link ActionPopup} which will appear when the user * presses the right mouse-button. The popup shows a list of all actions known * to this title.<br> * The whole logic a {@link DockTitle} needs is implemented in this class, * but subclasses may add graphical features - like a border or another * background.<br> * Subclasses may override {@link #getInnerInsets()} to add a space between * border and contents of this title. * * @author Benjamin Sigg * */ public class AbstractDockTitle extends AbstractMultiDockTitle { /** A panel that displays the action-buttons of this title */ private ButtonPanel itemPanel; /** The actions that were suggested to this title */ private DockActionSource suggestedSource; /** * Constructs a new title * @param dockable the Dockable which is the owner of this title * @param origin the version which was used to create this title */ public AbstractDockTitle( Dockable dockable, DockTitleVersion origin ){ init( dockable, origin, true ); } /** * Standard constructor * @param dockable The Dockable whose title this will be * @param origin The version which was used to create this title * @param showMiniButtons <code>true</code> if the actions of the Dockable * should be shown, <code>false</code> if they should not be visible */ public AbstractDockTitle( Dockable dockable, DockTitleVersion origin, boolean showMiniButtons ){ init( dockable, origin, showMiniButtons ); } /** * Constructor which does not do anything. Subclasses should call * {@link #init(Dockable, DockTitleVersion, boolean)} to initialize * the title. */ protected AbstractDockTitle(){ // ignore } /** * Initializer called by the constructor. * @param dockable The Dockable whose title this will be * @param origin The version which was used to create this title * @param showMiniButtons <code>true</code> if the actions of the Dockable * should be shown, <code>false</code> if they should not be visible */ protected void init( Dockable dockable, DockTitleVersion origin, boolean showMiniButtons ){ super.init( dockable, origin ); setShowMiniButtons( showMiniButtons ); } /** * Tells whether this title is able to show any {@link DockAction}. * @return <code>true</code> if {@link DockAction}s are enabled * @see #setShowMiniButtons(boolean) */ public boolean isShowMiniButtons(){ return itemPanel != null; } /** * Enables or disables {@link DockAction}s for this title. * @param showMiniButtons whether to show actions or not */ public void setShowMiniButtons( boolean showMiniButtons ){ if( showMiniButtons ){ if( itemPanel == null ){ itemPanel = new ButtonPanel( true ){ @Override protected BasicTitleViewItem<JComponent> createItemFor( DockAction action, Dockable dockable ){ return AbstractDockTitle.this.createItemFor( action, dockable ); } }; itemPanel.setOpaque( false ); itemPanel.setOrientation( getOrientation() ); itemPanel.setToolTipText( getToolTipText() ); add( itemPanel ); if( isBound() ){ itemPanel.setController( getDockable().getController() ); itemPanel.set( getDockable(), getActionSourceFor( getDockable() ) ); } } } else{ if( itemPanel != null ){ itemPanel.set( null ); remove( itemPanel ); } } } /** * Sets the tooltip that will be shown on this title. * @param text the new tooltip, can be <code>null</code> */ protected void setTooltip( String text ){ super.setToolTipText( text ); if( itemPanel != null ) itemPanel.setToolTipText( text ); } public void setOrientation( Orientation orientation ) { if( itemPanel != null ){ itemPanel.setOrientation( orientation ); } super.setOrientation( orientation ); } @Override protected void doTitleLayout(){ Insets insets = titleInsets(); int x = insets.left; int y = insets.top; int width = getWidth() - insets.left - insets.right; int height = getHeight() - insets.top - insets.bottom; OrientedLabel label = getLabel(); Orientation orientation = getOrientation(); Icon icon = getIcon(); int iconTextGap = getIconTextGap(); Dimension labelPreferred; String text = getText(); if( text == null || text.length() == 0 ){ labelPreferred = new Dimension( 5, 5 ); } else{ labelPreferred = label.getPreferredSize(); } if( orientation.isHorizontal() ){ if( icon != null ){ x += icon.getIconWidth() + iconTextGap; width -= icon.getIconWidth() + iconTextGap; } if( itemPanel != null && itemPanel.getItemCount() > 0 ){ Dimension[] buttonPreferred = itemPanel.getPreferredSizes(); int remaining = width - labelPreferred.width; int count = buttonPreferred.length-1; while( count > 0 && buttonPreferred[count].width > remaining ) count--; itemPanel.setVisibleActions( count ); int buttonWidth = buttonPreferred[count].width; int buttonX = width - buttonWidth; label.setBounds( x, y, buttonX, height ); itemPanel.setBounds( x + buttonX, y, width - buttonX, height ); } else label.setBounds( x, y, width, height ); } else{ if( icon != null ){ y += icon.getIconWidth() + iconTextGap; height -= icon.getIconWidth() + iconTextGap; } if( itemPanel != null && itemPanel.getItemCount() > 0 ){ Dimension[] buttonPreferred = itemPanel.getPreferredSizes(); int remaining = height - labelPreferred.height; int count = buttonPreferred.length-1; while( count > 0 && buttonPreferred[count].height > remaining ) count--; itemPanel.setVisibleActions( count ); int buttonHeight = buttonPreferred[count].height; int buttonY = height - buttonHeight; label.setBounds( x, y, width, buttonY ); itemPanel.setBounds( x, y + buttonY, width, height - buttonY ); } else label.setBounds( x, y, width, height ); } } public Point getPopupLocation( Point click, boolean popupTrigger ){ if( popupTrigger ) return click; boolean restrained = getText() == null || getText().length() == 0; Rectangle icon = getIconBounds(); if( icon != null ){ if( icon.contains( click )){ if( restrained ){ // icon must not be the whole title int size = getWidth() * getHeight(); if( itemPanel != null ) size -= itemPanel.getWidth() * itemPanel.getHeight(); if( size <= 2 * icon.width * icon.height ) return null; } if( getOrientation().isHorizontal() ) return new Point( icon.x, icon.y + icon.height ); else return new Point( icon.x + icon.width, icon.y ); } } return null; } @Override public void changed( DockTitleEvent event ){ super.changed( event ); if( event instanceof ActionsDockTitleEvent ){ suggestActions( ((ActionsDockTitleEvent)event).getSuggestions() ); } } @Override public Dimension getPreferredSize() { Dimension size = super.getPreferredSize(); if( itemPanel != null ){ Dimension items = itemPanel.getPreferredSize(); Insets insets = titleInsets(); if( getOrientation().isHorizontal() ){ size.width += items.width; size.height = Math.max( size.height, items.height + insets.top + insets.bottom ); } else{ size.height += items.height; size.width = Math.max( size.width, items.width + insets.left + insets.right ); } } if( size.width < 10 ){ size.width = 10; } if( size.height < 10 ){ size.height = 10; } return size; } /** * Gets a list of all actions which will be shown on this title. * @param dockable the owner of the actions * @return the list of actions */ protected DockActionSource getActionSourceFor( Dockable dockable ){ if( suggestedSource != null ){ return suggestedSource; } return dockable.getGlobalActionOffers(); } /** * Called if a module using the {@link DockTitle} suggests using a specific set of {@link DockAction}s. It is * up to the {@link DockTitle} to follow the suggestions or to ignore them. The default behavior of this * {@link AbstractDockTitle} is to set the result of {@link #getActionSourceFor(Dockable)} equal to * <code>actions</code> and update the {@link #itemPanel} if necessary. * @param actions the set of actions that should be used */ protected void suggestActions( DockActionSource actions ){ if( suggestedSource != actions ){ suggestedSource = actions; if( isShowMiniButtons() ){ Dockable dockable = getDockable(); itemPanel.set( dockable, getActionSourceFor( dockable ) ); } } } /** * Gets the {@link DockActionSource} that was {@link #suggestActions(DockActionSource) suggested} to this * title. * @return the source, can be <code>null</code> */ protected DockActionSource getSuggestedSource(){ return suggestedSource; } @Override public void bind() { DockController controller = getDockable().getController(); if( itemPanel != null ){ Dockable dockable = getDockable(); itemPanel.set( dockable, getActionSourceFor( dockable ) ); itemPanel.setController( controller ); } super.bind(); } @Override public void unbind() { if( itemPanel != null ){ itemPanel.set( null ); itemPanel.setController( null ); } super.unbind(); } }