/* * 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