org.openide.explorer.propertysheet.ButtonPanel Maven / Gradle / Ivy
/*
* 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-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
/*
* ButtonPanel.java
*
* Created on December 15, 2002, 5:45 PM
*/
package org.openide.explorer.propertysheet;
import java.awt.*;
import java.awt.event.FocusListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import javax.swing.*;
/** This class acts as a container for property table cell
* editors that support custom editors, and as a cell
* renderer proxy that will display the custom editor button.
* This ensures that renderers appear identical
* to editors, and that any changes to the appearance of the
* button that launches the custom editor are made, they will
* appear automatically in both renderers and editors.
*
* It implements InplaceEditor to proxy the real inplace editor it contains wraps,
* and desplays the component returned by InplaceEditor.getComponent on the
* inplace editor it is currently wrapping.
*
* @author Tim Boudreau
*/
class ButtonPanel extends javax.swing.JComponent implements InplaceEditor {
public static final Object editorActionKey = "openCustomEditor"; //NOI18N
/** Store this value since we will check it for every paint or layout */
private final boolean log = PropUtils.isLoggable(ButtonPanel.class);
/** The component to be rendered in the left side of the component or the
*full component in the case the custom editor button should not be displayed. */
JComponent comp = null;
private ConditionallyFocusableButton button;
boolean needLayout=true;
/** Creates a new instance of ButtonPanel */
public ButtonPanel() {
createButton();
setOpaque(true);
}
private void createButton() {
button = new ConditionallyFocusableButton();
int buttonWidth = PropUtils.getCustomButtonWidth();
button.setBounds (getWidth() - buttonWidth, 0, buttonWidth, getHeight());
button.setIcon (PropUtils.getCustomButtonIcon());
button.setRolloverIcon(PropUtils.getCustomButtonIcon());
button.setMargin (null);
button.setName ("Custom editor button - editor instance");//NOI18N
button.setText(null);
//undocumented (?) call to hide action text - see JButton line 234
button.putClientProperty ("hideActionText", Boolean.TRUE); //NOI18N
//Don't allow button to receive focus - otherwise it can when it's
//removed
// button.setFocusable(false);
add (button);
//setFocusable(false);
}
/** This handles the problem that the order of removal is that when
* the editor is removed, first the inner component is removed. At
* that point, the focus subsystem will try to give focus to the first
* focusable sibling of the inner component, which is the custom editor
* button, which is still there. However, when the editor is removed,
* focus never returns to the table - it stays on the now-offscreen
* custom editor button.
*
* This class also contains the ability to create an image buffer of itself
* and use it for its lifetime. On XP and Aqua L&Fs, button painting is
* expensive, and a huge amount of a treetable or property sheet's painting
* cycle gets spent scaling the backing bitmap for a button that will
* always be painted exactly the same size.
*/
private class ConditionallyFocusableButton extends JButton {
private AffineTransform at = AffineTransform.getTranslateInstance(0,0);
public ConditionallyFocusableButton() {
}
public boolean isFocusable() {
return ButtonPanel.this.getParent() != null && !clearing;
}
public void paint (Graphics g) {
if (PropUtils.useOptimizedCustomButtonPainting() && !hasFocus()) {
if (log) {
PropUtils.log (ButtonPanel.class, "Blitting custom editor "+
"button backing store for button at " + getBounds() + " in " +
(getParent() == null ? " null parent" : getParent() +
"editor=" + inplace)); //NOI18N
}
((Graphics2D)g).drawRenderedImage(getSnapshot(), at);
} else {
if (log) {
PropUtils.log (ButtonPanel.class, "Painting unoptimized custom editor "+
"button button at " + getBounds() + " in " +
(getParent() == null ? " null parent" : getParent() +
"editor=" + inplace)); //NOI18N
}
super.paint(g);
}
}
private BufferedImage snapshot = null;
public BufferedImage getSnapshot() {
if (snapshot == null) {
snapshot = GraphicsEnvironment.
getLocalGraphicsEnvironment().
getDefaultScreenDevice().getDefaultConfiguration().
createCompatibleImage(getWidth(), getHeight());
if (log) {
PropUtils.log (ButtonPanel.class, "Created " + snapshot +
" custom editor button backing image");
}
if (snapshot.getAlphaRaster() == null) {
//Alpha not supported, could cause corruption (issue
//39280) - use a bufferedImage which will support alpha,
//although less efficient to blit
snapshot = new BufferedImage (getWidth(), getHeight(),
BufferedImage.TYPE_INT_ARGB);
}
Graphics g = snapshot.getGraphics();
Point loc = getLocation();
super.paint (g);
}
return snapshot;
}
}
void setButtonAction (Action a) {
button.setAction (a);
button.setIcon(PropUtils.getCustomButtonIcon());
button.setRolloverIcon(PropUtils.getCustomButtonIcon());
}
public void setOpaque(boolean b) {
if (getInplaceEditor() != null) {
getInplaceEditor().getComponent().setOpaque(true);
}
}
public void setFont(Font f) {
if (comp != null) {
comp.setFont(f);
}
super.setFont(f);
}
public InplaceEditor getInplaceEditor() {
return inplace;
}
public void setCustomButtonBackground(Color c) {
button.setBackground(c);
}
public void setRolloverPoint(Point p) {
if (p != null) {
if (p.x < getWidth() - PropUtils.getCustomButtonWidth()) {
button.getModel().setRollover(false);
if (comp instanceof AbstractButton) {
((AbstractButton) comp).getModel().setRollover(true);
}
} else {
button.getModel().setRollover(true);
if (comp instanceof AbstractButton) {
((AbstractButton) comp).getModel().setRollover(false);
}
}
} else {
button.getModel().setRollover(false);
if (comp instanceof AbstractButton) {
((AbstractButton) comp).getModel().setRollover(false);
}
}
}
public Dimension getPreferredSize() {
Dimension result;
if (comp != null) {
result = new Dimension(comp.getPreferredSize());
result.width += button.getWidth();
result.height = Math.max(result.height, button.getPreferredSize().height);
} else {
result = new Dimension(button.getPreferredSize());
}
return result;
}
/** Overridden to forward the setEnabled call to the contained
* component - the custom editor button should always be
* enabled if present */
public void setEnabled(boolean val) {
super.setEnabled(val);
if (comp != null) {
comp.setEnabled(val);
}
button.setEnabled(true);
}
/** Set the component that will render (or be
* editor for) the property value. The component
* must be set before
* the instance is added to a container (the add will
* happen on addNotify()
)*/
private void setComponent (JComponent c) {
if (c == comp) return;
if ((comp != null) && (comp.getParent() == this)) {
remove (comp);
}
if (log) {
PropUtils.log (ButtonPanel.class, "Button panel setComponent to " +
c);
}
comp = c;
if (comp != null) {
comp.setBackground(getBackground());
comp.setForeground(getForeground());
if (comp.isEnabled() != isEnabled()) {
comp.setEnabled(isEnabled());
}
add (comp);
}
needLayout = true;
}
public void setBackground(Color c) {
super.setBackground(c);
if (comp != null) {
comp.setBackground(c);
Color bttn = PropUtils.getButtonColor();
if (bttn == null) {
button.setBackground(c);
} else {
button.setBackground(bttn);
}
}
}
public void setForeground(Color c) {
super.setForeground(c);
if (comp != null) {
comp.setForeground(c);
if (PropUtils.getButtonColor() == null) {
button.setForeground(c);
}
}
}
public void paint (Graphics g) {
if (isShowing()) {
super.paint(g);
return;
}
if (needLayout) {
doLayout();
}
int width = getWidth();
//We're painting in a PropertyRenderer, no parent present
Graphics cg = g.create(0,0,width-button.getWidth(), getHeight());
try {
if (comp instanceof InplaceEditor) {
comp.paint(cg);
if (comp.getParent() != this) {
add(comp);
}
}
} finally {
cg.dispose();
}
cg = g.create(width-button.getWidth(),0, button.getWidth(),
getHeight());
try {
button.paint(cg);
} finally {
cg.dispose();
}
/** Problem with endless looping in windows look and feel - painting
* causes some fiddling with component hierarchy */
if (getParent() instanceof CellRendererPane) {
RepaintManager.currentManager(this).markCompletelyClean(this);
}
}
/** Overridden to flag that a layout needs to be performed. This is
* needed since the component may be painted without a parent, so
* invalidate will not do anything */
public void reshape (int x, int y, int w, int h) {
super.reshape(x,y,w,h);
needLayout = true;
}
/** Overridden to force focus requests to the contained editor
* component - setting focus to this component directly will
* never be desirable. */
public void requestFocus () {
if (comp != null) {
comp.requestFocus();
}
}
/** Overridden to force focus requests to the contained editor
* component - setting focus to this component directly will
* never be desirable. */
public boolean requestFocusInWindow () {
if (comp != null) {
return comp.requestFocusInWindow();
} else {
return false;
}
}
/** Overridden to proxy adds to the custom editor button and the
* installed component */
public void addFocusListener (FocusListener l) {
if (comp != null) {
button.addFocusListener (l);
comp.addFocusListener (l);
}
}
/** Overridden to proxy removes to the custom editor button and the
* installed component */
public void removeFocusListener (FocusListener l) {
if (comp != null) {
button.removeFocusListener (l);
comp.removeFocusListener (l);
}
}
private InplaceEditor inplace=null;
public void setInplaceEditor (InplaceEditor ed) {
if (inplace == ed) {
if (isAncestorOf(inplace.getComponent())) {
return;
}
}
if (inplace != null) {
setComponent(null);
}
inplace = ed;
setComponent(inplace.getComponent());
needLayout = true;
}
//*********InplaceEditor impl that proxies to the embedded inplace editor*****
public void addActionListener(java.awt.event.ActionListener al) {
inplace.addActionListener(al);
}
boolean clearing = false;
public void clear() {
clearing = true;
try {
inplace.clear();
inplace = null;
setComponent(null);
} finally {
clearing = false;
}
}
/** Get the component currently assigned as the real editor
* embedded in this component. While not strictly necessary,
* this is useful if there are issues with focus bugs stemming
* from specific component types which need to be handled by
* the parent table. */
public JComponent getComponent () {
return this;
}
public void connect(java.beans.PropertyEditor pe, PropertyEnv env) {
inplace.connect(pe, env);
}
public KeyStroke[] getKeyStrokes() {
return inplace.getKeyStrokes();
}
public java.beans.PropertyEditor getPropertyEditor() {
return inplace.getPropertyEditor();
}
public PropertyModel getPropertyModel() {
return inplace.getPropertyModel();
}
public Object getValue() {
return inplace.getValue();
}
public boolean isKnownComponent(Component c) {
// return c == this || c == button || inplace.isKnownComponent(c);
return c == this || inplace.isKnownComponent(c);
}
public void removeActionListener(java.awt.event.ActionListener al) {
inplace.removeActionListener(al);
}
public void reset() {
inplace.reset();
}
public void setPropertyModel(PropertyModel pm) {
inplace.setPropertyModel(pm);
}
public void setValue(Object o) {
inplace.setValue(o);
}
public boolean supportsTextEntry() {
return inplace.supportsTextEntry();
}
public void doLayout() {
if (comp != null) {
comp.setBounds(0,0, getWidth()-PropUtils.getCustomButtonWidth(), getHeight());
comp.doLayout();
}
button.setBounds(getWidth()-PropUtils.getCustomButtonWidth(), 0,
PropUtils.getCustomButtonWidth(), getHeight());
if (log) {
PropUtils.log (ButtonPanel.class, "Laying out button panel. Bounds" +
" are " + getBounds() + ", custom editor button bounds: " +
button.getBounds() + " comp is " + comp); //NOI18N
}
needLayout=false;
}
public Dimension getMinimumSize() {
return getPreferredSize();
}
}