/*
* org.openmicroscopy.shoola.util.ui.tpane.TinyPane
*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2013 University of Dundee. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.util.ui.tpane;
//Java imports
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DesktopManager;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
//Third-party libraries
//Application-internal dependencies
/**
* A tiny-looking panel. We view it as a static internal frame, so behaves
* mostly like an internal frame.
* <p>This panel has a small title bar, small automatic scroll bars, and an
* layered pane as the content pane.
* <p>It is possible to collapse the panel: only the
* title bar will be showing. This feature accessibility depends on the
* title bar you select.</p>
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @version 2.2
* <small>
* (<b>Internal version:</b> $Revision: 4806 $ $Date: 2007-05-03 13:20:36 +0000 (Thu, 03 May 2007) $)
* </small>
* @since OME2.2
*/
public class TinyPane
extends JPanel
{
/**
* Specifies that the frame should be headless.
* Use this constant to remove the title bar from the frame completely.
*/
public final static int NO_BAR = 0;
/**
* Identifies the small title bar.
* Use this constant to fit the frame with a title bar having just a
* button to collapse/expand the frame and the frame title.
*/
public final static int SMALL_BAR = 1;
/**
* Identifies the header bar.
* Use this constant to fit the frame with a very thin header. No buttons,
* icon, nor title is shown.
*/
public final static int HEADER_BAR = 2;
/**
* Identifies the full title bar.
* Use this constant to fit the frame with a title bar having the frame
* icon and title, a button to collapse/expand the frame, and a button to
* switch between single-view and multiple-view mode.
* In the single-view mode, only one of the components in the internal
* desktop is shown at a time.
* The user can choose which one from a drop-down menu triggered by the
* button. In the multiple-view mode (the default) all of the components
* are shown in the internal desktop. (However, you're required to
* lay them out manually.)
*/
public final static int FULL_BAR = 3;
/**
* Identifies the static title bar.
* This title bar shows the frame icon and title, but has no buttons to
* control the frame behavior.
*/
public final static int STATIC_BAR = 4;
/**
* Identifies the small title bar.
* Use this constant to fit the frame with a title bar having just a
* collection of buttons and the frame title.
*/
public final static int SMALL_TITLE_BAR = 5;
/** Bound property name indicating if the frame is collapsed. */
public final static String CLOSED_PROPERTY = "closedPane";
/** Bound property name indicating if the frame is collapsed. */
public final static String COLLAPSED_PROPERTY = "collapsed";
/** Bound property name indicating if the frame is to be highlighted. */
public final static String HIGHLIGHT_PROPERTY = "highlight";
/** Bound property name indicating the type of the frame title bar. */
public final static String TITLEBAR_TYPE_PROPERTY = "titleBarType";
/** Bound property name indicating the value of title. */
public final static String TITLE_PROPERTY = "title";
/** Bound property name indicating if the frame is in single-view mode. */
public final static String SINGLE_VIEW_PROPERTY = "singleViewMode";
/** Bound property name. */
public final static String FRAME_ICON_PROPERTY = "frameIcon";
/** Bound property name indicating if the frame can be moved. */
public final static String BORDER_LISTENER_PROPERTY = "borderListener";
/** Bound property name indicating if the frame can be resized. */
public final static String RESIZABLE_PROPERTY = "resizable";
/** Bound property name indicating if the frame is decorated. */
public final static String DECORATION_PROPERTY = "decoration";
/**
* Bound property name indicating if the mouse pressed event happens
* on the frame icon.
*/
public final static String FRAME_ICON_PRESSED_PROPERTY =
"frameIconPressed";
/**
* Bound property indicating that the dragging or resizing event
* is finished.
*/
public final static String END_MOVING_PROPERTY = "endMoving";
/** The View component that renders this frame. */
private TinyPaneUI uiDelegate;
/** This component's Model. */
private TinyPaneModel model;
/**
* Contains the Component that focus is to go when
* <code>restoreSubcomponentFocus</code> is invoked, that is,
* <code>restoreSubcomponentFocus</code> sets this to the value returned
* from <code>getMostRecentFocusOwner</code>.
*/
private Component lastFocusOwner;
/**
* Called by the constructor methods to create the default
* <code>contentPane</code>.
* By default this method creates a new <code>JComponent</code> add sets a
* <code>BorderLayout</code> as its <code>LayoutManager</code>.
* @return the default <code>contentPane</code>
*/
protected Container createContentPane()
{
JComponent c = new JPanel();
c.setName(this.getName()+".contentPane");
c.setLayout(new BorderLayout() {
/* This BorderLayout subclass maps a null constraint to CENTER.
* Although the reference BorderLayout also does this, some VMs
* throw an IllegalArgumentException.
*/
public void addLayoutComponent(Component comp, Object constraints) {
if (constraints == null) {
constraints = BorderLayout.CENTER;
}
super.addLayoutComponent(comp, constraints);
}
});
return c;
}
/**
* Creates the View to use as UI delegate for this component.
*
* @return The UI delegate for this component.
*/
protected TinyPaneUI createUIDelegate() { return new TinyPaneUI(this); }
/** Creates a new instance. */
public TinyPane() { this("", ""); }
/**
* Creates a new instance.
*
* @param title The title displayed in the <code>TitleBar</code>.
*/
public TinyPane(String title) { this(title, ""); }
/**
* Creates a new instance.
*
* @param title The title displayed in the <code>TitleBar</code>.
* @param note The note added to the <code>TitleBar</code>.
*/
public TinyPane(String title, String note)
{
super();
model = null;
Container container = createContentPane();
model = new TinyPaneModel(container, title,
new Dimension(getWidth(), getHeight()));
model.setNote(note);
uiDelegate = createUIDelegate();
setLayout(new TinyPaneLayout());
add(uiDelegate.getTitleBar());
container.add(getInternalDesktop());
add(container);
setUI(uiDelegate);
model.setSingleViewMode(false, uiDelegate);
}
/**
* Returns the proper DesktopManager. Calls the {@link TinyPaneUI} to
* find and returns the desktopManager
*
* @return See above.
*/
DesktopManager getDesktopManager()
{
return uiDelegate.getDesktopManager();
}
/**
* Sets the given component to be the child view of the frame when in
* single-view mode.
* This method does nothing if the frame is not in single-view mode.
* Otherwise, the passed component is assumed to be a child of the
* internal desktop.
*
* @param child The child component.
*/
void setChildView(Component child) { model.setChildView(child); }
/**
* Returns the note added to the <code>TitleBar</code>.
*
* @return See above.
*/
String getNote() { return model.getNote(); }
/**
* Returns the collection of buttons to add to the <code>TitleBar</code>.
*
* @return See above.
*/
List getDecoration() { return model.getDecoration(); }
/**
* Fires a property change if the mouse pressed happens on the frame icon.
*
* @param p The location of the mouse pressed.
*/
void onFrameIconPressed(Point p)
{
Rectangle bounds = uiDelegate.getFrameIconBounds();
if (bounds.contains(p)) {
firePropertyChange(FRAME_ICON_PRESSED_PROPERTY, Boolean.FALSE,
Boolean.TRUE);
}
}
/**
* Invokes when the dragging or resizing of the component is finished.
*/
void notifyEndMoving()
{
firePropertyChange(END_MOVING_PROPERTY, null, this);
}
/**
* Tells if this frame is closed.
*
* @return <code>true</code> if closed, <code>false</code> otherwise.
*/
boolean isClosed() { return model.isClosed(); }
/**
* Closes this frame depending on the passed value.
* This is a bound property; property change listeners are notified
* of any change to this property.
*
* @param b Pass <code>true</code> to close.
*/
void setClosed(boolean b)
{
boolean closed = model.isClosed();
if (b == closed) return; //We're already in the requested state.
//Fire the state change.
model.setClosed(b);
setVisible(!b);
firePropertyChange(CLOSED_PROPERTY, null, this);
}
/** Resizes and repaints the component. */
public void pack()
{
setSize(getPreferredSize());
validate();
repaint();
}
/**
* Returns the title of this component.
*
* @return See above.
*/
public String getTitle() { return model.getTitle(); }
/**
* Sets the title of the frame.
*
* @param title The title to set.
*/
public void setTitle(String title)
{
String oldValue = model.getTitle();
model.setTitle(title);
firePropertyChange(TITLE_PROPERTY, oldValue, title);
}
/**
* Collapses or expands this frame depending on the passed value.
* This is a bound property; property change listeners are notified
* of any change to this property.
*
* @param b Pass <code>true</code> to collapse, <code>false</code> to
* expand.
*/
public void setCollapsed(boolean b)
{
boolean collapsed = model.isCollapsed();
if (b == collapsed) return; //We're already in the requested state.
if (!collapsed)
//Remember current size, so it can be restored later.
model.setRestoreSize(new Dimension(getWidth(), getHeight()));
//Fire the state change.
Boolean oldValue = collapsed ? Boolean.TRUE : Boolean.FALSE,
newValue = b ? Boolean.TRUE : Boolean.FALSE;
model.setCollapsed(b);
firePropertyChange(COLLAPSED_PROPERTY, oldValue, newValue);
}
/**
* Tells if this frame is expanded or collapsed.
*
* @return <code>true</code> if collapsed, <code>false</code> if expanded.
*/
public boolean isCollapsed() { return model.isCollapsed(); }
/**
* The size of the frame before the last collapse request.
* That is before the last call to the
* {@link #setCollapsed(boolean) setCollapsed} method with a
* <code>true</code> argument while the frame is expanded.
*
* @return The size of the frame before the last collapse request.
* @see #isCollapsed()
*/
public Dimension getRestoreSize() { return model.getRestoreSize(); }
/**
* Returns a color to use for highlighting the frame's title bar or
* <code>null</code> if the frame is not to be highlighted.
*
* @return See above.
*/
public Color getHighlight() { return model.getHighlight(); }
/**
* Returns a color to use for highlighting the frame's title bar or
* <code>null</code> if the frame is not to be highlighted.
*
* @return See above.
*/
public Color getPreviousHighlight() { return model.getPreviousHighlight(); }
/**
* Sets the highlight mode of this frame.
* If a color is specified, then the title bar will be highlighted using
* that color. If you pass <code>null</code>, then the title bar will
* be painted in the normal background.
*
* @param highlight A color for highlighting or <code>null</code> to
* restore the normal background.
*/
public void setHighlight(Color highlight)
{
Color oldValue = model.getHighlight();
model.setHighlight(highlight);
firePropertyChange(HIGHLIGHT_PROPERTY, oldValue, highlight);
}
/**
* Sets the color of the border only.
* @param borderColor A color for highlighting or <code>null</code> to
* restore the normal background.
*/
public void setBorderColor(Color borderColor)
{
uiDelegate.setBorderColor(borderColor);
repaint();
}
/**
* Returns the type of the title bar this frame is currently fitted with.
*
* @return One of the static constants defined by this class.
*/
public int getTitleBarType() { return model.getTitleBarType(); }
/**
* Fits this frame with the specified title bar.
*
* @param type One of the static constants defined by this class.
*/
public void setTitleBarType(int type)
{
Integer oldValue = Integer.valueOf(model.getTitleBarType()),
newValue = Integer.valueOf(type);
if (model.setTitleBarType(type))
firePropertyChange(TITLEBAR_TYPE_PROPERTY, oldValue, newValue);
}
/**
* Tells if this frame is in single or multiple-view mode
*
* @return <code>true</code> for single-view mode, <code>false</code> for
* multiple-view mode.
* @see #setSingleViewMode(boolean)
*/
public boolean isSingleViewMode() { return model.isSingleViewMode(); }
/**
* Sets the view mode of this frame.
* Two modes are possible: single-view and multiple-view. In the single-view
* mode, only one of the components in the internal desktop is shown at a
* time. The user can choose which one from a drop-down menu triggered by
* a button in the title bar. In the multiple-view mode (the default) all of
* the components are shown in the internal desktop. (However, you're
* required to lay them out manually.) Note that by default the frame is
* in multiple-view mode and the only title bar that has controls for
* managing the single-view mode is the {@link #FULL_BAR}.
* So you shouldn't set the single-view mode unless the frame is fitted
* with a {@link #FULL_BAR}.
*
* @param singleViewMode <code>true</code> to switch to the single-view
* mode, <code>false</code> to return to the default
* multiple-view mode.
*/
public void setSingleViewMode(boolean singleViewMode)
{
Boolean oldValue =
model.isSingleViewMode() ? Boolean.TRUE : Boolean.FALSE,
newValue = singleViewMode ? Boolean.TRUE : Boolean.FALSE;
model.setSingleViewMode(singleViewMode, uiDelegate);
firePropertyChange(SINGLE_VIEW_PROPERTY, oldValue, newValue);
}
/**
* Returns the component that is showing in the frame's desktop when
* in single-view mode.
* The returned value will always be <code>null</code> if the frame is
* not in single-view mode. However, it can also be <code>null</code>
* if the frame is in single-view mode but no components were added to
* the internal desktop.
*
* @return The child view, that is the component that is showing in the
* frame's desktop when in single-view mode.
* @see #setSingleViewMode(boolean)
* @see #setChildView(Component)
*/
public Component getChildView() { return model.getChildView(); }
/**
* Returns the component that serves as title bar for this frame.
*
* @return See above.
*/
public JComponent getTitleBar() { return uiDelegate.getTitleBar(); }
/**
* Returns the size this frame should have to fully display the internal
* desktop at its preferred size.
*
* @return See above.
*/
public Dimension getPreferredSize() { return uiDelegate.getIdealSize(); }
/**
* Returns the bounds of the area, within the internal desktop, which is
* occupied by the contained components.
*
* @return See above.
*/
public Rectangle getContentsBounds() { return model.getContentsBounds(); }
/**
* Returns the internal desktop, which serves as the content pane for
* this frame.
*
* @return See above.
*/
public JComponent getInternalDesktop() { return model.getDesktopPane(); }
/**
* Overridden to return the internal desktop pane.
*
* @return The internal desktop which is our substitute to the content pane.
*/
public Container getContentPane() { return model.getContentPane(); }
/**
* Returns the scroll pane that contains the internal desktop.
*
* @return See above.
*/
public JScrollPane getDeskDecorator()
{
return uiDelegate.getDeskDecorator();
}
/**
* Allows to resize the window, <code>true</code> if the window can be
* resized, <code>false</code> otherwise.
*
* @param resizable Passed <code>true</code> to resize the window
* <code>false</code> otherwise.
*/
public void setResizable(boolean resizable)
{
Boolean oldValue =
model.isResizable() ? Boolean.TRUE : Boolean.FALSE,
newValue = resizable ? Boolean.TRUE : Boolean.FALSE;
model.setResizable(resizable);
firePropertyChange(RESIZABLE_PROPERTY, oldValue, newValue);
}
/**
* Returns <code>true</code> if the component can be resized,
* <code>false</code> otherwise.
*
* @return See above.
*/
public boolean isResizable() { return model.isResizable(); }
/**
* Sets the icon of this frame.
*
* @param icon The icon to set.
*/
public void setFrameIcon(Icon icon)
{
Icon oldIcon = model.getFrameIcon();
model.setFrameIcon(icon);
firePropertyChange(FRAME_ICON_PROPERTY, oldIcon, icon);
}
/**
* Returns the frame's icon.
*
* @return See above.
*/
public Icon getFrameIcon() { return model.getFrameIcon(); }
/**
* Passes <code>true</code> to listen to the border.
* <code>false</code> otherwise.
*
* @param listenToBorder The flag to set.
*/
public void setListenToBorder(boolean listenToBorder)
{
Boolean oldValue =
model.isListenToBorder() ? Boolean.TRUE : Boolean.FALSE,
newValue = listenToBorder ? Boolean.TRUE : Boolean.FALSE;
model.setListenToBorder(listenToBorder);
firePropertyChange(BORDER_LISTENER_PROPERTY, oldValue, newValue);
}
/**
* Returns <code>true</code> if a border listener is set,
* <code>false</code> otherwise.
*
* @return See above.
*/
public boolean isListenToBorder() { return model.isListenToBorder(); }
/** Moves the frame to the front. */
public void moveToFront()
{
if (getParent() != null && getParent() instanceof JLayeredPane) {
// Because move to front typically involves adding and removing
// Components, it will often times result in focus changes. We
// either install focus to the lastFocusOwner, or our desendant
// that has focus. We use the ivar for lastFocusOwner as in
// some cases requestFocus is async and won't have completed from
// the requestFocus call in setSelected so that getFocusOwner
// will return the wrong component.
Component focusOwner = (lastFocusOwner != null) ? lastFocusOwner :
KeyboardFocusManager.getCurrentKeyboardFocusManager().
getFocusOwner();
if (focusOwner != null &&
!SwingUtilities.isDescendingFrom(focusOwner, this))
focusOwner = null;
((JLayeredPane) getParent()).moveToFront(this);
if (focusOwner != null) focusOwner.requestFocus();
}
}
/** Moves the component to the back. */
public void moveToBack()
{
if (getParent() != null && getParent() instanceof JLayeredPane)
((JLayeredPane) getParent()).moveToBack(this);
}
/** Restores the original display. */
public void restoreDisplay() { model.restoreDisplay(uiDelegate); }
/**
* Sets the buttons to add to the <code>TitleBar</code>.
*
* @param decoration Collection of components to add.
*/
public void setDecoration(List decoration)
{
List oldValue = model.getDecoration();
//if (oldValue.size() == 0 && decoration.size() == 0) return;
model.setDecoration(decoration);
firePropertyChange(DECORATION_PROPERTY, oldValue, decoration);
}
/** Indicates no to add buttons to the <code>TitleBar</code>. */
public void noDecoration() { setDecoration(new ArrayList()); }
/**
* Adds a close button to the frame if the passed value
* is <code>true</code>, no close button if <code>false</code>.
*
* @param b Pass <code>true</code> to add a close button,
* <code>false</code> to remove the close button.
*/
public void allowClose(boolean b) { uiDelegate.allowClose(b); }
/** Removes all buttons, added by default, from the tool bar. */
public void clearDefaultButtons() { uiDelegate.clearDefaultButtons(); }
/**
* Modifies the style of the font of the title.
*
* @param style The style to set.
*/
public void setFontTitleStyle(int style)
{
uiDelegate.setFontStyle(style);
}
/**
* Overridden to return the title of the frame.
* @see JPanel#toString()
*/
public String toString() { return getTitle(); }
/**
* Overridden to set the text to display in the tool tip of the
* title bar.
* @see JPanel#setToolTipText(String)
*/
public void setToolTipText(String text)
{
super.setToolTipText(text);
uiDelegate.getTitleBar().setToolTipText(text);
}
}