/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*
* Contributors: Michael (mikew, mw, hair) Wever,
* ECS International Pty Ltd.
* mikew@ecsi.com.au, www.ecsi.com.au
*/
package org.openide.awt;
import java.awt.*;
import java.awt.event.*;
import java.util.Iterator;
import java.util.Vector;
import java.util.EventObject;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.JToolBar;
import org.openide.*;
import org.openide.loaders.*;
import org.openide.cookies.InstanceCookie;
import org.openide.util.actions.Presenter;
/**
* Toolbar provides a component which is useful for displaying commonly used
* actions. It can be dragged inside its ToolbarPanel
to
* customize its location.
*
* @author David Peroutka, Libor Kramolis
*/
public class Toolbar extends JToolBar
/*nbif compat
implements MouseInputListener
/*nbend*/
{
/** Constant of grip layout constraint. */
private static final String GRIP = "Grip"; // NOI18N
/** Constant of action layout constraint. */
private static final String ACTION = "Action"; // NOI18N
/** Basic toolbar height. */
public static
/*nbif compat
nbelse*/
final
/*nbend*/
int BASIC_HEIGHT = 34;
/** 5 pixels is tolerance of toolbar height so toolbar can be high (BASIC_HEIGHT + HEIGHT_TOLERANCE)
* but it will be set to BASIC_HEIGHT high. */
static int HEIGHT_TOLERANCE = 5;
/** TOP of toolbar empty border. */
static int TOP = 0;
/** LEFT of toolbar empty border. */
static int LEFT = 3;
/** BOTTOM of toolbar empty border. */
static int BOTTOM = 0;
/** RIGHT of toolbar empty border. */
static int RIGHT = 3;
/** is toolbar floatable */
private boolean floatable;
/** Toolbar DnDListener */
private DnDListener listener;
/** Toolbar mouse listener */
private ToolbarMouseListener mouseListener;
/** display name of the toolbar */
private String displayName;
/** lazy display name loader */
private DisplayNameResolver resolver;
static final long serialVersionUID =5011742660516204764L;
/** Create a new Toolbar which empty name. */
public Toolbar() {
this (""); // NOI18N
}
/** Create a new not floatable Toolbar with programmatic name.
* Display name is set to be the same as name */
public Toolbar(String name) {
this (name, name, false);
}
/** Create a new not floatable Toolbar with specified programmatic name
* and display name */
public Toolbar(String name, String displayName) {
this (name, displayName, false);
}
/** Create a new Toolbar
.
* @param name a String
containing the associated name
* @param f specified if Toolbar is floatable
* Display name of the toolbar is set equal to the name.
*/
public Toolbar(String name, boolean f) {
this (name, name, f);
}
/** Constructs toolbar with lazy display name resolver */
Toolbar(String name, DisplayNameResolver resolver, boolean f) {
super();
initAll(name, f);
this.resolver = resolver;
}
/**
* Create a new Toolbar
.
* @param name a String
containing the associated name
* @param f specified if Toolbar is floatable
*/
public Toolbar(String name, String displayName, boolean f) {
super();
setDisplayName(displayName);
initAll(name, f);
}
private void initAll(String name, boolean f) {
floatable = f;
mouseListener = null;
setName(name);
setFloatable(false);
setBorder(new CompoundBorder(new EtchedBorder(),
new EmptyBorder(TOP, LEFT, BOTTOM, RIGHT)));
setLayout(new InnerLayout(0, 0));
putClientProperty("JToolBar.isRollover", new Boolean(true)); // NOI18N
addGrip();
}
/** Add new Component as ACTION. */
public Component add(Component comp) {
add(ACTION, comp);
return comp;
}
/** Removes all ACTION components. */
public void removeAll() {
super.removeAll();
addGrip();
}
/**
* When Toolbar is floatable, ToolbarGrip is added as Grip as first toolbar component
* modified by Michael Wever, 28/4/01, to use l&f's grip/bump.
*/
void addGrip() {
if (floatable) {
//ToolbarGrip dragarea = new ToolbarGrip();
/** Uses L&F's grip **/
String lAndF = UIManager.getLookAndFeel().getName();
JPanel dragarea = lAndF.equals("Windows") ?
(JPanel)new ToolbarGrip() : (JPanel)new ToolbarBump();
if (mouseListener == null)
mouseListener = new ToolbarMouseListener();
dragarea.addMouseListener(mouseListener);
dragarea.addMouseMotionListener(mouseListener);
add(GRIP, dragarea);
addSeparator(new Dimension(4, 1));
}
}
/** Compute with HEIGHT_TOLERANCE number of rows for specific toolbar height.
* @param height of some toolbar
* @return number of rows
*/
static public int rowCount(int height) {
int rows = 1;
int max_height = (BASIC_HEIGHT + HEIGHT_TOLERANCE);
while (height > max_height) {
rows++;
height -= max_height;
}
return rows;
}
/** Set DnDListener to Toolbar.
* @param DndListener for toolbar
*/
public void setDnDListener(DnDListener l) {
listener = l;
}
/** @return Display name of this toolbar. Display name is localizable,
* on the contrary to the programmatic name */
public String getDisplayName() {
if (resolver != null) {
displayName = resolver.displayName();
resolver = null;
}
return displayName;
}
/** Sets new display name of this toolbar. Display name is localizable,
* on the contrary to the programmatic name */
public void setDisplayName(String displayName) {
//System.out.println("setting display name...");
this.displayName = displayName;
// make sure that lazy resolver will not be involved
resolver = null;
}
/** Fire drag of Toolbar
* @param dx distance of horizontal dragging
* @param dy distance of vertical dragging
* @param type type of toolbar dragging
*/
protected void fireDragToolbar(int dx, int dy, int type) {
if (listener != null)
listener.dragToolbar(new DnDEvent(this, getName(), dx, dy, type));
}
/** Fire drop of Toolbar
* @param dx distance of horizontal dropping
* @param dy distance of vertical dropping
* @param type type of toolbar dropping
*/
protected void fireDropToolbar(int dx, int dy, int type) {
if (listener != null)
listener.dropToolbar(new DnDEvent(this, getName(), dx, dy, type));
}
/** Toolbar mouse listener. */
class ToolbarMouseListener implements MouseInputListener {
/** Is toolbar dragging now. */
private boolean dragging;
/** Start point of dragging. */
private Point startPoint;
public ToolbarMouseListener() {
dragging = false;
startPoint = null;
}
/** Invoked when the mouse has been clicked on a component. */
public void mouseClicked(MouseEvent e) {
}
/** Invoked when the mouse enters a component. */
public void mouseEntered(MouseEvent e) {
}
/** Invoked when the mouse exits a component. */
public void mouseExited(MouseEvent e) {
}
/** Invoked when a mouse button has been pressed on a component. */
public void mousePressed(MouseEvent e) {
startPoint = new Point(e.getX(), e.getY());
}
/** Invoked when a mouse button has been released on a component. */
public void mouseReleased(MouseEvent e) {
if (dragging) {
fireDropToolbar(e.getX() - startPoint.x,
e.getY() - startPoint.y,
DnDEvent.DND_ONE);
dragging = false;
}
}
/** Invoked when a mouse button is pressed on a component and then dragged. */
public void mouseDragged(MouseEvent e) {
int m = e.getModifiers();
int type = DnDEvent.DND_ONE;
if (e.isControlDown())
type = DnDEvent.DND_LINE;
else if (((m & InputEvent.BUTTON2_MASK) != 0) ||
((m & InputEvent.BUTTON3_MASK) != 0))
type = DnDEvent.DND_END;
if (startPoint == null)
startPoint = new Point(e.getX(), e.getY());
fireDragToolbar(e.getX() - startPoint.x,
e.getY() - startPoint.y,
type);
dragging = true;
}
/** Invoked when the mouse button has been moved on a component (with no
* buttons no down). */
public void mouseMoved(MouseEvent e) {
}
} // end of inner class ToolbarMouseListener
/*nbif compat
public void mouseClicked (MouseEvent e) {
if (mouseListener == null) mouseListener = new ToolbarMouseListener ();
mouseListener.mouseClicked (e);
}
public void mouseEntered (MouseEvent e) {
if (mouseListener == null) mouseListener = new ToolbarMouseListener ();
mouseListener.mouseEntered (e);
}
public void mouseExited (MouseEvent e) {
if (mouseListener == null) mouseListener = new ToolbarMouseListener ();
mouseListener.mouseExited (e);
}
public void mousePressed (MouseEvent e) {
if (mouseListener == null) mouseListener = new ToolbarMouseListener ();
mouseListener.mousePressed (e);
}
public void mouseReleased (MouseEvent e) {
if (mouseListener == null) mouseListener = new ToolbarMouseListener ();
mouseListener.mouseReleased (e);
}
public void mouseDragged (MouseEvent e) {
if (mouseListener == null) mouseListener = new ToolbarMouseListener ();
mouseListener.mouseDragged (e);
}
public void mouseMoved (MouseEvent e) {
if (mouseListener == null) mouseListener = new ToolbarMouseListener ();
mouseListener.mouseMoved (e);
}
/*nbend*/
/** Lazy toolbar display name loader. Accessing bundles is expensive
* during startup, so we want to delay it until it is really needed */
private static final class DisplayNameResolver {
private DataFolder folder;
DisplayNameResolver(DataFolder folder) {
this.folder = folder;
}
String displayName() {
//System.out.println("really loading display name");
return folder.getNodeDelegate().getDisplayName();
}
}
/**
* This class can be used to produce a Toolbar
instance from
* the given DataFolder
.
*/
final static class Folder extends FolderInstance {
/** the Toolbar
to work with */
private JToolBar toolbar;
/**
* Creates a new folder on the specified DataFolder
.
* @param folder a DataFolder
to work with
*/
public Folder(DataFolder folder) {
super (folder);
recreate();
}
/**
* Returns a Toolbar
representee of this folder.
* @return a Toolbar
representee of this folder
*/
public final JToolBar getToolbar() {
if (toolbar == null) {
synchronized (this) {
if (toolbar == null) {
toolbar = new Toolbar(
folder.getName(),
new DisplayNameResolver(folder),
true
);
}
}
}
return toolbar;
}
/**
* Full name of the data folder's primary file separated by dots.
* @return the name
*/
public String instanceName() {
return getToolbar().getClass().getName();
}
/**
* Returns the root class of all objects.
* @return Object.class
*/
public Class instanceClass()
throws java.io.IOException, ClassNotFoundException {
return getToolbar().getClass();
}
/** If no instance cookie, tries to create execution action on the
* data object.
*/
protected InstanceCookie acceptDataObject(DataObject dob) {
InstanceCookie ic = super.acceptDataObject(dob);
if (ic == null) {
JButton button = ExecBridge.createButton(dob);
return button != null ? new InstanceSupport.Instance(button) : null;
} else {
return ic;
}
}
/**
* Accepts only cookies that can provide Toolbar
.
* @param cookie an InstanceCookie
to test
* @return true if the cookie can provide accepted instances
*/
protected InstanceCookie acceptCookie(InstanceCookie cookie)
throws java.io.IOException, ClassNotFoundException {
Class c = cookie.instanceClass();
if (Component.class.isAssignableFrom(c)) {
return cookie;
}
if (Presenter.Toolbar.class.isAssignableFrom(c)) {
return cookie;
}
return null;
}
/**
* Returns a Toolbar.Folder
cookie for the specified
* DataFolder
.
* @param df a DataFolder
to create the cookie for
* @return a Toolbar.Folder
for the specified folder
*/
protected InstanceCookie acceptFolder(DataFolder df) {
return null; // PENDING new Toolbar.Folder(df);
}
/**
* Updates the Toolbar
represented by this folder.
*
* @param cookies array of instance cookies for the folder
* @return the updated ToolbarPool
representee
*/
protected Object createInstance(final InstanceCookie[] cookies)
throws java.io.IOException, ClassNotFoundException {
// refresh the toolbar's content
org.openide.util.Mutex.EVENT.readAccess(new Runnable() {
public void run() {
getToolbar().removeAll();
for (int i = 0; i < cookies.length; i++) {
try {
Object obj = cookies[i].instanceCreate();
if (obj instanceof Presenter.Toolbar) {
obj = ((Presenter.Toolbar)obj).getToolbarPresenter();
}
if (obj instanceof Component) {
Component comp = (Component)obj;
getToolbar().add(comp);
}
} catch (java.io.IOException ex) {
TopManager.getDefault().getErrorManager().notify(ErrorManager.INFORMATIONAL, ex);
} catch (ClassNotFoundException ex) {
TopManager.getDefault().getErrorManager().notify(ErrorManager.INFORMATIONAL, ex);
}
}
// invalidate the toolbar and its parent
toolbar.invalidate();
java.awt.Container parent = toolbar.getParent();
if (parent != null) {
parent.validate();
parent.repaint();
}
}
});
return getToolbar();
}
} // end of inner class Folder
/** Grip for floatable toolbar */
private class ToolbarGrip extends JPanel {
/** Horizontal gaps. */
static final int HGAP = 1;
/** Vertical gaps. */
static final int VGAP = 1;
/** Step between two grip elements. */
static final int STEP = 1;
/** Width of grip element. */
static final int WIDTH = 2;
/** Number of grip elements. */
int columns;
/** Minimum size. */
Dimension dim;
static final long serialVersionUID =-8819972936203315276L;
/** Create new ToolbarGrip for default number of grip elements. */
public ToolbarGrip() {
this (2);
}
/** Create new ToolbarGrip for specific number of grip elements.
* @param col number of grip elements
*/
public ToolbarGrip(int col) {
super ();
columns = col;
int width = (col - 1) * STEP + col * WIDTH + 2 * HGAP;
dim = new Dimension(width, width);
this.setBorder(new EmptyBorder(VGAP, HGAP, VGAP, HGAP));
this.setToolTipText(Toolbar.this.getName());
}
/** Paint grip to specific Graphics. */
public void paint(Graphics g) {
Dimension size = this.getSize();
int top = VGAP;
int bottom = size.height - 1 - VGAP;
int height = bottom - top;
g.setColor( this.getBackground() );
for (int i = 0, x = HGAP; i < columns; i++, x += WIDTH + STEP) {
g.draw3DRect(x, top, WIDTH, height, true); // grip element is 3D rectangle now
}
}
/** @return minimum size */
public Dimension getMinimumSize() {
return dim;
}
/** @return preferred size */
public Dimension getPreferredSize() {
return this.getMinimumSize();
}
} // end of inner class ToolbarGrip
/** Bumps for floatable toolbar */
private class ToolbarBump extends JPanel {
/** Top gap. */
static final int TOPGAP = 2;
/** Bottom gap. */
static final int BOTGAP = 0;
/** Width of bump element. */
static final int WIDTH = 6;
/** Minimum size. */
Dimension dim;
static final long serialVersionUID =-8819972936203315277L;
/** Create new ToolbarBump. */
public ToolbarBump() {
super ();
int width = WIDTH;
dim = new Dimension(width, width);
this.setToolTipText(Toolbar.this.getName());
}
/** Paint bumps to specific Graphics. */
public void paint(Graphics g) {
Dimension size = this.getSize();
int height = size.height - BOTGAP;
g.setColor(this.getBackground());
for (int x = 0; x+1 < size.width; x+=4) {
for (int y = TOPGAP; y+1 < height; y+=4) {
g.setColor(this.getBackground().brighter());
g.drawLine(x, y, x, y);
if (x+5 < size.width && y+5 < height)
g.drawLine(x+2, y+2, x+2, y+2);
g.setColor(this.getBackground().darker().darker());
g.drawLine(x+1, y+1, x+1, y+1);
if (x+5 < size.width && y+5 < height)
g.drawLine(x+3, y+3, x+3, y+3);
}
}
}
/** @return minimum size */
public Dimension getMinimumSize() {
return dim;
}
/** @return preferred size */
public Dimension getPreferredSize() {
return this.getMinimumSize();
}
} // end of inner class ToolbarBump
/** Toolbar layout manager */
private class InnerLayout implements LayoutManager {
/** Grip component */
private Component grip;
/** Vector of Components */
private Vector actions;
/** horizontal gap */
private int hgap;
/** vertical gap */
private int vgap;
/**
* Constructs a new InnerLayout.
*/
public InnerLayout() {
this (5, 5);
}
/**
* Constructs a new InnerLayout with the specified gap values.
* @param hgap the horizontal gap variable
* @param vgap the vertical gap variable
*/
public InnerLayout(int hgap, int vgap) {
this.hgap = hgap;
this.vgap = vgap;
actions = new Vector();
}
/**
* Adds the specified component with the specified name to the layout.
*/
public void addLayoutComponent(String name, Component comp) {
synchronized (comp.getTreeLock()) {
if (GRIP.equals(name)) {
grip = comp;
} else
if (ACTION.equals(name)) {
actions.addElement(comp);
} else
throw new IllegalArgumentException
("cannot add to layout: unknown constraint: " + name); // NOI18N
}
}
/**
* Adds the specified component to the layout, using the specified
* constraint object.
*/
public void addLayoutComponent(Component comp, Object constraints) {
throw new IllegalArgumentException();
}
/**
* Removes the specified component from the layout.
*/
public void removeLayoutComponent(Component comp) {
synchronized (comp.getTreeLock()) {
if (grip == comp)
grip = null;
else
actions.removeElement(comp);
}
}
/**
* Calculates the preferred size dimensions for the specified panal given
* the components in the specified parent container.
*/
public Dimension preferredLayoutSize(Container target) {
synchronized (target.getTreeLock()) {
Dimension dim = new Dimension(0, 0);
int size = actions.size();
if ((grip != null) && grip.isVisible()) {
Dimension d = grip.getPreferredSize();
dim.width += d.width;
dim.width += hgap;
}
for (int i = 0; i < size; i++) {
Component comp = (Component)actions.elementAt(i);
if (comp.isVisible()) {
Dimension d = comp.getPreferredSize();
dim.width += d.width;
dim.height = Math.max(dim.height, d.height);
dim.width += hgap;
}
}
Insets insets = target.getInsets();
dim.width += insets.left + insets.right;
dim.height += insets.top + insets.bottom;
return dim;
}
}
/**
* Calculates the minimum size dimensions for the specified panal given
* the components in the specified parent container.
*/
public Dimension minimumLayoutSize(Container target) {
return preferredLayoutSize(target);
}
/**
* Calculates the maximum size dimensions for the specified panal given
* the components in the specified parent container.
*/
public Dimension maximumLayoutSize(Container target) {
synchronized (target.getTreeLock()) {
Dimension dim = new Dimension(0, 0);
int size = actions.size();
if ((grip != null) && grip.isVisible()) {
Dimension d = grip.getPreferredSize();
dim.width += d.width;
dim.width += hgap;
}
Component last = null;
for (int i = 0; i < size; i++) {
Component comp = (Component)actions.elementAt(i);
if (comp.isVisible()) {
Dimension d = comp.getPreferredSize();
dim.width += d.width;
dim.height = Math.max(dim.height, d.height);
dim.width += hgap;
last = comp;
}
}
if (last != null) {
Dimension prefSize = last.getPreferredSize();
Dimension maxSize = last.getMaximumSize();
if (prefSize != maxSize) {
dim.width = dim.width - prefSize.width + maxSize.width;
dim.height = Math.max(dim.height, maxSize.height);
}
}
Insets insets = target.getInsets();
dim.width += insets.left + insets.right;
dim.height += insets.top + insets.bottom;
return dim;
}
}
/**
* Lays out the container in the specified panel.
*/
public void layoutContainer(Container target) {
synchronized (target.getTreeLock()) {
Insets insets = target.getInsets();
Dimension dim = target.getSize();
int fullHeight = dim.height;
int bottom = dim.height - insets.bottom - 1;
int top = 0 + insets.top;
int left = insets.left;
int maxPosition = dim.width - (insets.left + insets.right) - hgap;
int right = dim.width - insets.right;
maxPosition = right;
int height = bottom - top;
int size = actions.size();
int w, h;
if ((grip != null) && grip.isVisible()) {
Dimension d = grip.getPreferredSize();
grip.setBounds(left, top + vgap, d.width, bottom - top - 2*vgap);
left += d.width;
left += hgap;
}
for (int i = 0; i < size; i++) {
left += hgap;
Component comp = (Component)actions.elementAt(i);
Dimension d;
Dimension minSize = comp.getMinimumSize();
if (i == size - 1)
d = comp.getMaximumSize();
else
d = comp.getPreferredSize();
w = d.width;
h = Math.min(height, d.height);
if ((left < maxPosition) &&
(left + w > maxPosition)) {
if (maxPosition - left >= minSize.width)
w = maxPosition - left;
else
w = minSize.width;
}
comp.setBounds(left, (fullHeight - d.height)/2, w, h);
left += d.width;
}
}
}
/**
* Returns the alignment along the x axis.
*/
public float getLayoutAlignmentX(Container target) {
return 0;
}
/**
* Returns the alignment along the y axis.
*/
public float getLayoutAlignmentY(Container target) {
return (float)0.5;
}
/**
* Invalidates the layout, indicating that if the layout manager has cached
* information it should ne discarded.
*/
public void invalidateLayout(Container target) {
// check target components with local vars (grip, actions)
}
} // end of class InnerLayout
/** DnDListener is Drag and Drop listener for Toolbar motion events. */
public interface DnDListener extends java.util.EventListener {
/** Invoced when toolbar is dragged. */
public void dragToolbar(DnDEvent e);
/** Invoced when toolbar is dropped. */
public void dropToolbar(DnDEvent e);
} // end of interface DnDListener
/** DnDEvent is Toolbar's drag and drop event. */
public static class DnDEvent extends EventObject {
/** Type of DnDEvent. Dragging with only one Toolbar. */
public static final int DND_ONE = 1;
/** Type of DnDEvent. Only horizontal dragging with Toolbar and it's followers. */
public static final int DND_END = 2;
/** Type of DnDEvent. Only vertical dragging with whole lines. */
public static final int DND_LINE = 3;
/** Name of toolbar where event occured. */
/*nbif compat
public
nbelse*/
private
/*nbend*/
String name;
/** distance of horizontal dragging */
/*nbif compat
public
nbelse*/
private
/*nbend*/
int dx;
/** distance of vertical dragging */
/*nbif compat
public
nbelse*/
private
/*nbend*/
int dy;
/** Type of event. */
/*nbif compat
public
nbelse*/
private
/*nbend*/
int type;
static final long serialVersionUID =4389530973297716699L;
public DnDEvent(Toolbar toolbar, String name, int dx, int dy, int type) {
super (toolbar);
this.name = name;
this.dx = dx;
this.dy = dy;
this.type = type;
}
/** @return name of toolbar where event occured. */
public String getName() {
return name;
}
/** @return distance of horizontal dragging */
public int getDX() {
return dx;
}
/** @return distance of vertical dragging */
public int getDY() {
return dy;
}
/** @return type of event. */
public int getType() {
return type;
}
} // end of class DnDEvent
} // end of class Toolbar