/*
* 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-2001 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.openide.explorer.propertysheet;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.util.Vector;
import javax.swing.border.EmptyBorder;
import javax.swing.FocusManager;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.UIManager;
import org.openide.awt.MouseUtils;
import org.openide.util.Utilities;
/**
* A lightweight component creating a labelled button.
* Can be "plastic", which means that when a mouse
* enters the button, the area of the button is stubbed.
* <code>SheetButton</code> can contain one inner component.
*
* @author Jan Jancura
*/
class SheetButton extends JPanel {
/** generated Serialized Version UID */
static final long serialVersionUID = -5681433767155127558L;
/** Action command idicating the right mouse button was clicked. */
static final String RIGHT_MOUSE_COMMAND = "rightMouseActionCommand"; // NOI18N
// variables .................................................................
/** There are all listeners stored. */
private transient Vector listeners = new Vector (1,5);
/** There are action command string stored. Default value is "click". */
private String actionCommand = "click"; // NOI18N
/** Label of this button. */
private String label;
/** Preferred size of this button. */
private Dimension preferredSize;
/** State of button. */
private boolean pressed;
/** True if button can receive focus. */
private boolean focusTransferable;
/** True is is in "plastic" mode. */
private boolean isPlastic;
/** Listens on focus/key/mouse events. */
private IL innerListener;
/** <code>SheetButton</code> can contain one inner component. */
private JComponent innerComponent;
/** Is true if mouseEntered and mouseExited actions should be propagated. */
private boolean plasticNotify;
/** Active foreground color. */
private Color foreground;
/** Inactive foreground color. */
private Color inForeground;
/** Flag indicating invalidity. Invalid mark. */
private boolean invalidMark;
/** Flag indicating modified status */
private boolean modifiedMark;
/** Flag indicating that button is used as renderer. */
private boolean flat;
// init .................................................................
/**
* Construct a button with empty label.
*/
public SheetButton() {
this("", false, false); // NOI18N
}
/**
* Construct a button with label and standard appearance (not plastic).
*
* @param aLabel the label
*/
public SheetButton (String aLabel) {
this (aLabel, false, false);
}
/**
* Construct a button with label.
*
* @param aLabel the label
* @param boolean <code>true</code> if should use plastic appearance
*/
public SheetButton (String aLabel, boolean isPlasticButton) {
this (aLabel, isPlasticButton, false);
}
/**
* Construct a button with label and possibly a plastic listener.
* @param label the label
* @param isPlasticButton <code>true</code> if should be plastic
* @param plasticActionNotify <code>true</code> if mouse enter/exit events should be propagated
*/
public SheetButton (String aLabel, boolean isPlasticButton, boolean plasticActionNotify) {
setDoubleBuffered (false);
setOpaque (true);
label = aLabel == null ? "" : aLabel; // NOI18N
isPlastic = isPlasticButton;
plasticNotify = plasticActionNotify;
innerListener = new IL ();
addMouseListener (innerListener);
addKeyListener (innerListener);
foreground = UIManager.getColor ("controlText");
inForeground = UIManager.getColor ("Button.disabledText");
}
// variables .................................................................
/** Identifies whether or not this component can receive the focus.
* Getter for <code>focusTransferble</code> property.
* Overrides superclass method.
* @see #focusTransferable */
public boolean isFocusTraversable () {
return focusTransferable;
}
/**
* Sets whether this component can receive the focus.
* Setter of <code>focusTransferable</code> property.
* @param ft whether to be able receive the focus
* @see #isFocusTraversable
*/
public void setFocusTraversable(boolean ft) {
if (isFocusTraversable () == ft) {
return;
}
focusTransferable = ft;
if (ft) {
addFocusListener (innerListener);
} else {
removeFocusListener (innerListener);
}
}
/**
* Sets tooltip test.
*/
public void setToolTipText (String str) {
// bugfix #26252, mark invalid value in a tool tip
if (invalidMark)
str = org.openide.util.NbBundle.getMessage (SheetButton.class,
"CTL_InvalidValue", str); // NOI18N
if (innerComponent != null) innerComponent.setToolTipText (str);
super.setToolTipText (str);
}
/**
* Gets preferred size. Overrides superclass method.
* @return Standart method returned preferredSize (depends on font size only).
*/
public Dimension getPreferredSize () {
if(preferredSize == null) {
updatePreferredSize ();
}
return preferredSize == null ? new Dimension (10,10) : preferredSize;
}
/**
* Sets the font of this component. Overrides superclass method. Adds updating
* of preferred size.
* @param aFont The font to become this component's font.
*/
public void setFont (Font aFont) {
super.setFont (aFont);
updatePreferredSize ();
}
/**
* Getter for <code>foreground</code> property.
* @return the color
*/
public Color getActiveForeground () {
return foreground;
}
/**
* Setter for <code>foreground</code> property.
* @param color the color to set
*/
public void setActiveForeground (Color color) {
foreground = color;
repaint();
}
/**
* Getter for <code>inForeColor</code> property.
* @return the color of inactive property
*/
public Color getInactiveForeground () {
return inForeground;
}
/**
* Sets the inactive foreground color.
* @param color the color
*/
public void setInactiveForeground (Color color) {
inForeground = color;
repaint();
}
/**
* Gets the label of this button.
* @return the button's label.
*/
public String getLabel () {
return label;
}
/**
* Sets the button's label to be the specified string.
* @param label the new label.
*/
public void setLabel (String aLabel) {
label = aLabel;
if (isShowing()) {
updatePreferredSize ();
repaint ();
}
}
/**
* Set whether button should appear stubbed.
* @param aPressed <code>true</code> if so
*/
public void setPressed (boolean aPressed) {
if (pressed == aPressed) return;
pressed = aPressed;
if (pressed)
setBorder (new EmptyBorder (2, 2, 0, 0));
else
setBorder (new EmptyBorder (1, 1, 1, 1));
repaint ();
}
/**
* Test whether button is pressed.
* @return <code>true</code> if so
*/
public boolean isPressed () {
return pressed;
}
/**
* Set whether button is plastic.
* @param plastic <code>true</code> if so
*/
public void setPlastic (boolean plastic) {
this.isPlastic = plastic;
repaint ();
}
/**
* Test whether button is plastic.
* @return <code>true</code> if so
*/
public boolean isPlastic () {
return isPlastic;
}
/**
* Set the action command.
* @param command the new command, default is set to "click"
*/
public void setActionCommand (String command) {
actionCommand = command;
}
/**
* Attaches component for this button.
*/
public Component add (Component innerComponent) {
setLayout (new BorderLayout ());
setBorder (new EmptyBorder (1, 1, 1, 1));
add (innerComponent, BorderLayout.CENTER);
removeMouseListener (innerListener);
innerComponent.addMouseListener (innerListener);
return this.innerComponent = (JComponent) innerComponent;
}
/** Sets invalid. */
void setInvalidMark(boolean invalid) {
invalidMark = invalid;
repaint();
}
void setModifiedMark(boolean modified) {
modifiedMark = modified;
repaint();
}
/** Sets flat flag. */
void setFlat(boolean f) {
flat = f;
}
/** Gets invalid image. */
private Image getInvalidImage() {
return Utilities.loadImage("org/openide/resources/propertysheet/invalid.gif"); //NOI18N
}
/** Gets modified image. */
private Image getModifiedImage() {
return Utilities.loadImage("org/openide/resources/propertysheet/modified.gif"); //NOI18N
}
/**
* Recalculates preferred size.
*/
private void updatePreferredSize () {
Graphics g = getGraphics ();
if (g == null) return;
Font font = null;
if ((font = getFont ()) == null) return;
FontMetrics fontMetrics = g.getFontMetrics (font);
preferredSize = new Dimension (
fontMetrics.stringWidth (label) + 10,
fontMetrics.getHeight () + 6
);
}
/**
* Standart methods painting SheetButton. Overrides superclass method.
*/
public void paint (Graphics g) {
super.paint (g);
Dimension size = getSize ();
Color color = g.getColor ();
Font theFont = g.getFont ();
int xShift = 0;
if (invalidMark) {
xShift += getInvalidImage().getWidth(null) + 2;
}
if (modifiedMark) {
xShift += getModifiedImage().getWidth(null) + 2;
}
g.setFont (getFont ());
FontMetrics fontMetrics = g.getFontMetrics ();
if (flat || pressed) {
if (innerComponent == null) {
g.setColor (isEnabled () ? getActiveForeground () : getInactiveForeground ());
g.drawString (
label,
6 + xShift,
(size.height - fontMetrics.getHeight ()) / 2 + 1 + fontMetrics.getMaxAscent ()
);
if (invalidMark) {
g.drawImage(
getInvalidImage(), 5,
(size.height - getInvalidImage ().getHeight (null)) / 2 + 1, null);
}
if (modifiedMark) {
g.drawImage(
getModifiedImage(), invalidMark ? getInvalidImage().getWidth(null) + 7 : 5,
(size.height - getModifiedImage ().getHeight (null)) / 2 + 1, null);
}
}
} else {
g.setColor (UIManager.getColor ("controlLtHighlight"));
g.drawLine (0, 0, size.width - 1, 0);
g.drawLine (0, 0, 0, size.height - 1);
g.setColor (UIManager.getColor("controlDkShadow"));
g.drawLine (size.width - 1, 0, size.width - 1, size.height - 1);
g.drawLine (0, size.height - 1, size.width - 1, size.height - 1);
if (innerComponent == null) {
g.setColor (isEnabled () ? getActiveForeground () : getInactiveForeground ());
g.drawString (
label,
5 + xShift,
(size.height - fontMetrics.getHeight ()) / 2 + fontMetrics.getMaxAscent ()
);
if (invalidMark) {
g.drawImage(
getInvalidImage(), 4,
(size.height - getInvalidImage().getHeight(null)) / 2, null);
}
if (modifiedMark) {
g.drawImage(
getModifiedImage(), invalidMark ? getInvalidImage().getWidth(null) + 6 : 4,
(size.height - getModifiedImage().getHeight(null)) / 2, null);
}
}
}
if(hasFocus()) {
g.setColor (UIManager.getColor("Button.focus")); // NOI18N
g.drawRect(2, 1, size.width - 5, size.height - 4);
}
g.setFont (theFont);
g.setColor (color);
}
/**
* Returns string representation of this class.
* @return <code>String</code> Representation of this class.
*/
public String toString () {
return getClass ().getName () + "[ \"" + label + "\" ]"; // NOI18N
}
// SheetButtonListener support ......................................................
/**
* Adds <code>SheetButtonListener</code>.
*/
void addSheetButtonListener (SheetButtonListener sheetButtonListener) {
if (!listeners.contains(sheetButtonListener)) {
listeners.addElement (sheetButtonListener);
}
}
/**
* Removes <code>SheetButtonListener</code>.
*/
void removeSheetButtonListener (SheetButtonListener sheetButtonListener) {
listeners.removeElement (sheetButtonListener);
}
/** Notifies listeners button was clicked. */
public void notifySheetButtonListenersAboutClick (ActionEvent e) {
Vector l = (Vector) listeners.clone ();
int i, k = l.size ();
for (i = 0; i < k; i++)
((SheetButtonListener) l.elementAt (i)).sheetButtonClicked (e);
}
/** Notifies listeners button was entered. */
public void notifySheetButtonListenersAboutEntered (ActionEvent e) {
Vector l = (Vector) listeners.clone ();
int i, k = l.size ();
for (i = 0; i < k; i++)
((SheetButtonListener) l.elementAt (i)).sheetButtonEntered (e);
}
/** Notifies listeners buttoin was exited. */
public void notifySheetButtonListenersAboutExited (ActionEvent e) {
Vector l = (Vector) listeners.clone ();
int i, k = l.size ();
for (i = 0; i < k; i++)
((SheetButtonListener) l.elementAt (i)).sheetButtonExited (e);
}
// innerclasses ..........................................................................
/** Mouse, key and focus listener. */
private class IL extends MouseMotionAdapter implements
MouseListener, KeyListener, FocusListener {
IL() {}
/** Dummy implementation of <code>MouseListener</code> interface method. */
public void mouseClicked (MouseEvent e) {}
/** Implements <code>MouseListener</code> interface method. */
public void mousePressed (MouseEvent e) {
if (!isPlastic) {
pressed = true;
setBorder (new EmptyBorder (2, 2, 0, 0));
notifySheetButtonListenersAboutEntered (
new ActionEvent (
SheetButton.this,
ActionEvent.ACTION_FIRST,
actionCommand
)
);
if (innerComponent != null) {
// setBorder (new EmptyBorder (2, 2, 0, 0));
invalidate ();
if (getParent() != null) {
getParent ().validate ();
}
innerComponent.addMouseMotionListener (innerListener);
} else
addMouseMotionListener (innerListener);
repaint ();
}
}
/** Implements <code>MouseListener</code> interface method. */
public void mouseReleased (MouseEvent e) {
boolean isRightClick = MouseUtils.isRightMouseButton(e);
if (innerComponent != null) {
innerComponent.removeMouseMotionListener (innerListener);
// setBorder (new EmptyBorder (1, 1, 1, 1));
invalidate ();
if (getParent() != null) {
getParent ().validate ();
}
} else
removeMouseMotionListener (innerListener);
repaint ();
if(isEnabled ()) {
notifySheetButtonListenersAboutClick (
new ActionEvent (
SheetButton.this,
((e.getClickCount () % 2) == 1) ?
(ActionEvent.ACTION_FIRST + 1) :
ActionEvent.ACTION_FIRST + 2,
isRightClick ? RIGHT_MOUSE_COMMAND : actionCommand
)
);
notifySheetButtonListenersAboutExited (
new ActionEvent (
SheetButton.this,
ActionEvent.ACTION_FIRST,
actionCommand
)
);
}
pressed = false;
setBorder (new EmptyBorder (1, 1, 1, 1));
}
/** Implements <code>MouseListener</code> interface method. */
public void mouseEntered (MouseEvent e) {
if (isPlastic) {
pressed = true;
setBorder (new EmptyBorder (2, 2, 0, 0));
repaint ();
if (plasticNotify)
notifySheetButtonListenersAboutEntered (
new ActionEvent (
SheetButton.this,
ActionEvent.ACTION_FIRST,
actionCommand
)
);
}
}
/** Implements <code>MouseExited</code> interface method. */
public void mouseExited (MouseEvent e) {
if (isPlastic) {
pressed = false;
setBorder (new EmptyBorder (1, 1, 1, 1));
repaint ();
if (plasticNotify)
notifySheetButtonListenersAboutExited (
new ActionEvent (
SheetButton.this,
ActionEvent.ACTION_FIRST,
actionCommand
)
);
}
}
/** Overrides superclass method. */
public void mouseDragged (MouseEvent e) {
if(new Rectangle(SheetButton.this.getSize())
.contains(e.getPoint()) == pressed) {
return;
}
if (pressed) {
notifySheetButtonListenersAboutExited (
new ActionEvent (
SheetButton.this,
ActionEvent.ACTION_FIRST,
actionCommand
));
} else {
notifySheetButtonListenersAboutEntered (
new ActionEvent (
SheetButton.this,
ActionEvent.ACTION_FIRST,
actionCommand
));
}
pressed = !pressed;
if (pressed) {
setBorder (new EmptyBorder (2, 2, 0, 0));
} else {
setBorder (new EmptyBorder (1, 1, 1, 1));
}
if (innerComponent != null) {
invalidate ();
if (getParent() != null) {
getParent ().validate ();
}
}
repaint ();
}
/** Implements <code>KeyListener</code> interface method. */
public void keyTyped (KeyEvent e) {
}
/** Implements <code>KeyListener</code> interface method. */
public void keyPressed (KeyEvent e) {
if (e.isControlDown())
return;
if ((e.getKeyCode() == KeyEvent.VK_SPACE) ||
(e.getKeyCode() == KeyEvent.VK_ENTER)) {
notifySheetButtonListenersAboutClick (
new ActionEvent (
SheetButton.this,
ActionEvent.ACTION_FIRST + 1,
actionCommand
)
);
}
if(e.getKeyCode () == KeyEvent.VK_DOWN) {
FocusManager fm = FocusManager.getCurrentManager ();
fm.focusNextComponent(SheetButton.this);
e.consume();
} else {
if(e.getKeyCode () == KeyEvent.VK_UP) {
FocusManager fm = FocusManager.getCurrentManager ();
fm.focusPreviousComponent(SheetButton.this);
e.consume();
}
}
}
/** Dummy implementation of <code>KeyListener</code> interface method. */
public void keyReleased (KeyEvent e) {
}
/** Implements <code>FocusListener</code> interface method. */
public void focusGained (FocusEvent fe) {
notifySheetButtonListenersAboutEntered (
new ActionEvent (
SheetButton.this,
ActionEvent.ACTION_FIRST + 1,
actionCommand
)
);
Component c = SheetButton.this;
Rectangle r = SheetButton.this.getBounds();
while ((c != null) && (!(c instanceof javax.swing.JViewport))) {
c = c.getParent();
if (c != null) {
r.x += c.getX();
r.y += c.getY();
}
}
if (c != null) {
javax.swing.JViewport jvp = (javax.swing.JViewport)c;
jvp.scrollRectToVisible(r);
}
repaint ();
}
/** Implements <code>FocusListener</code> interface method. */
public void focusLost (FocusEvent fe) {
if(isPlastic) {
pressed = false;
setBorder(new EmptyBorder (1, 1, 1, 1));
}
notifySheetButtonListenersAboutExited (
new ActionEvent (
SheetButton.this,
ActionEvent.ACTION_FIRST + 1,
actionCommand
)
);
repaint ();
}
} // End of class IL.
public javax.accessibility.AccessibleContext getAccessibleContext() {
if (accessibleContext == null) {
accessibleContext = new AccessibleSheetButton();
}
return accessibleContext;
}
class AccessibleSheetButton extends AccessibleJComponent {
public javax.accessibility.AccessibleRole getAccessibleRole() {
return javax.accessibility.AccessibleRole.PUSH_BUTTON;
}
public String getAccessibleName() {
String name = super.getAccessibleName();
if (name == null && innerComponent != null) {
name = innerComponent.getAccessibleContext().getAccessibleName();
}
if (name == null && label != null) {
name = label;
}
return org.openide.util.NbBundle.getMessage(SheetButton.class,
"ACS_SheetButton", new Integer(invalidMark ? 0 : 1), name); // NOI18N
}
public String getAccessibleDescription() {
String name = super.getAccessibleDescription();
if (name == null && innerComponent != null) {
name = innerComponent.getAccessibleContext().getAccessibleDescription();
}
if (name == null && label != null) {
name = label;
}
return org.openide.util.NbBundle.getMessage(SheetButton.class,
"ACS_SheetButton", new Integer(invalidMark ? 0 : 1), name); // NOI18N
}
}
}