/* * 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. */ package org.openide.awt; import java.awt.*; import java.awt.event.*; import java.util.EventObject; import javax.swing.*; import javax.swing.event.*; import javax.swing.border.*; import org.openide.*; import org.openide.loaders.*; import org.openide.cookies.InstanceCookie; import org.openide.util.actions.Presenter; import org.openide.util.Task; /** * Toolbar provides a component which is useful for displaying commonly used * actions. It can be dragged inside its <code>ToolbarPanel</code> to * customize its location. * * @author David Peroutka, Libor Kramolis */ public class Toolbar extends JToolBar /*implemented by patchsuperclass MouseInputListener*/ { /** Basic toolbar height. */ public static final 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 = 2; /** LEFT of toolbar empty border. */ static int LEFT = 3; /** BOTTOM of toolbar empty border. */ static int BOTTOM = 2; /** RIGHT of toolbar empty border. */ static int RIGHT = 3; /** Residual size of the toolbar when dragged far right */ static int RESIDUAL_WIDTH = 16; /** 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; /** Used for lazy creation of Folder and DisplayName */ private DataFolder backingFolder; /* FolderInstance that will track all the changes in backingFolder */ private Folder processor; static final long serialVersionUID =5011742660516204764L; /** Create a new Toolbar with 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 <code>Toolbar</code>. * @param name a <code>String</code> 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); } Toolbar(DataFolder folder, boolean f) { super(); backingFolder = folder; initAll(folder.getName(), f); } /** Start tracking content of the underlaying folder if not doing so yet */ private void doInitialize() { if(processor == null && isVisible()) { processor = new Folder(); // It will start tracking immediatelly } } public void addNotify() { super.addNotify(); doInitialize(); } public void setVisible(boolean b) { super.setVisible(b); doInitialize(); } /** * Create a new <code>Toolbar</code>. * @param name a <code>String</code> 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); Border b = UIManager.getBorder ("ToolBar.border"); //NOI18N //(TDB) hack to avoid no borders on toolbars //Can be removed once theme file ships with NetBeans, so that //NB will respect toolbar.border from other look and feels. if ((b==null) || (b instanceof javax.swing.plaf.metal.MetalBorders.ToolBarBorder)) b=BorderFactory.createEtchedBorder (EtchedBorder.LOWERED); setBorder (new CompoundBorder ( b, new EmptyBorder (TOP, LEFT, BOTTOM, RIGHT)) ); putClientProperty("JToolBar.isRollover", Boolean.TRUE); // NOI18N addGrip(); getAccessibleContext().setAccessibleName(displayName == null ? getName() : displayName); getAccessibleContext().setAccessibleDescription(getName()); } /** Removes all ACTION components. */ public void removeAll () { super.removeAll(); addGrip(); } /** * When Toolbar is floatable, ToolbarBump is added as Grip as first toolbar component * modified by Michael Wever, to use l&f's grip/bump. */ void addGrip () { if (floatable) { /** 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 (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) { return 1 + height / (BASIC_HEIGHT + HEIGHT_TOLERANCE); } /** Set DnDListener to Toolbar. * @param l 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 (displayName == null) { if (!backingFolder.isValid()) { // #17020 return backingFolder.getName(); } return backingFolder.getNodeDelegate ().getDisplayName (); } 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) { this.displayName = displayName; } /** 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)); } synchronized final MouseInputListener mouseDelegate () { if (mouseListener == null) mouseListener = new ToolbarMouseListener (); return mouseListener; } /** Toolbar mouse listener. */ class ToolbarMouseListener extends MouseInputAdapter { /** Is toolbar dragging now. */ private boolean dragging = false; /** Start point of dragging. */ private Point startPoint = null; /** Invoked when a mouse button has been pressed on a component. */ public void mousePressed (MouseEvent e) { startPoint = e.getPoint(); } /** Invoked when a mouse button has been released on a component. */ public void mouseReleased (MouseEvent e) { if (dragging) { int dx = getX() + e.getX() - startPoint.x > getParent().getWidth() - RESIDUAL_WIDTH ? 0 : e.getX() - startPoint.x; fireDropToolbar (dx, 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; int dx; 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()); } if ( getX() + e.getX() + startPoint.x > getParent().getWidth() - RESIDUAL_WIDTH ) { if ( getX() >= getParent().getWidth() - RESIDUAL_WIDTH ) { dx = 0; } else { dx = getParent().getWidth() - RESIDUAL_WIDTH - getX(); } } else { dx = e.getX() - startPoint.x; } fireDragToolbar ( dx, e.getY() - startPoint.y, type); dragging = true; } } // end of inner class ToolbarMouseListener /** * This class can be used to produce a <code>Toolbar</code> instance from * the given <code>DataFolder</code>. */ final class Folder extends FolderInstance { /** * Creates a new folder on the specified <code>DataFolder</code>. * @param folder a <code>DataFolder</code> to work with */ public Folder () { super (backingFolder); recreate (); } /** * Full name of the data folder's primary file separated by dots. * @return the name */ public String instanceName () { return Toolbar.this.getClass().getName(); } /** * Returns the root class of all objects. * @return Object.class */ public Class instanceClass () throws java.io.IOException, ClassNotFoundException { return Toolbar.this.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 <code>Toolbar</code>. * @param cookie an <code>InstanceCookie</code> to test * @return true if the cookie can provide accepted instances */ protected InstanceCookie acceptCookie (InstanceCookie cookie) throws java.io.IOException, ClassNotFoundException { boolean is; if (cookie instanceof InstanceCookie.Of) { InstanceCookie.Of of = (InstanceCookie.Of)cookie; is = of.instanceOf (Component.class) || of.instanceOf (Presenter.Toolbar.class) || of.instanceOf (Action.class); } else { Class c = cookie.instanceClass(); is = Component.class.isAssignableFrom(c) || Presenter.Toolbar.class.isAssignableFrom(c) || Action.class.isAssignableFrom (c); } return is ? cookie : null; } /** * Returns a <code>Toolbar.Folder</code> cookie for the specified * <code>DataFolder</code>. * @param df a <code>DataFolder</code> to create the cookie for * @return a <code>Toolbar.Folder</code> for the specified folder */ protected InstanceCookie acceptFolder(DataFolder df) { return null; // PENDING new Toolbar.Folder(df); } /** * Updates the <code>Toolbar</code> represented by this folder. * * @param cookies array of instance cookies for the folder * @return the updated <code>ToolbarPool</code> representee */ protected Object createInstance(final InstanceCookie[] cookies) throws java.io.IOException, ClassNotFoundException { // refresh the toolbar's content Toolbar.this.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(); // go on to get thru next condition } if (obj instanceof Component) { // remove border and grip if requested. "Fixed" toolbar // item has to live alone in toolbar now if ((obj instanceof JComponent) && "Fixed".equals(((JComponent)obj).getClientProperty("Toolbar"))) { // NOI18N floatable = false; Toolbar.this.removeAll(); setBorder(null); } Toolbar.this.add ((Component)obj); continue; } if (obj instanceof Action) { Action a = (Action)obj; JButton b = new JButton (); Actions.connect (b, a); Toolbar.this.add (b); continue; } } catch (java.io.IOException ex) { ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex); } catch (ClassNotFoundException ex) { ErrorManager.getDefault ().notify (ErrorManager.INFORMATIONAL, ex); } } // invalidate the toolbar, trigger proper relayout // toolbars can affect layout of whole frame, we need to // force repaint of frame (unfortunately) Toolbar.this.invalidate (); Container parent = Toolbar.this.getParent (); while ((parent != null) && !(parent instanceof Frame)) { parent = parent.getParent(); } if (parent != null) { parent.validate(); parent.repaint(); } return Toolbar.this; } /** Recreate the instance in AWT thread. */ protected Task postCreationTask (Runnable run) { return new AWTTask (run); } } // end of inner class Folder /** Bumps for floatable toolbar */ private final 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; /** Maximum size. */ Dimension max; static final long serialVersionUID =-8819972936203315277L; /** Create new ToolbarBump. */ public ToolbarBump () { super (); int width = WIDTH; dim = new Dimension (width, width); max = new Dimension (width, Integer.MAX_VALUE); this.setToolTipText (Toolbar.this.getDisplayName()); } /** 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 (); } public Dimension getMaximumSize () { return max; } } // end of inner class ToolbarBump /** Grip for floatable toolbar, used for Windows L&F */ private final 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; /** Maximum size. */ Dimension max; 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); max = new Dimension (width, Integer.MAX_VALUE); 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(); } public Dimension getMaximumSize () { return max; } } // end of inner class ToolbarGrip /** 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. */ private String name; /** distance of horizontal dragging */ private int dx; /** distance of vertical dragging */ private int dy; /** Type of event. */ private 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