org.openide.explorer.propertysheet.PropUtils 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-2004 Sun
* Microsystems, Inc. All Rights Reserved.
*/
/*
* PropUtils.java
*
* Created on January 4, 2003, 7:31 PM
*/
package org.openide.explorer.propertysheet;
import java.awt.event.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.beans.*;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.*;
import java.util.*;
import javax.swing.*;
import javax.swing.plaf.metal.*;
import java.text.MessageFormat;
import javax.swing.border.Border;
import javax.swing.plaf.SplitPaneUI;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import org.netbeans.modules.openide.explorer.PsSettings;
import org.openide.nodes.Node.*;
import org.openide.*;
import org.openide.util.*;
import org.openide.nodes.*;
/** A few utility methods useful to implementors of Inplace Editors.
* @author Tim Boudreau
*/
final class PropUtils {
/**If true, hides custom editor buttons unless in editing mode.
* Auto popup of combo boxes is suppressed in this mode */
static boolean noCustomButtons =
Boolean.getBoolean("netbeans.ps.noCustomButtons"); //NOI18N
/**If true, radio button boolean editor will always be used */
static boolean forceRadioButtons = //non final for unit tests
// !Boolean.getBoolean ("netbeans.ps.useCheckbox");
Boolean.getBoolean("netbeans.ps.forceRadioButtons");
/**If true, caption on the checkbox boolean editor will be suppressed */
static final boolean noCheckboxCaption =
!Boolean.getBoolean ("netbeans.ps.checkboxCaption"); //NOI18N
/** Flag which, when true, property set expansion handles will not be
* shown when the node only has one property set. Leaving as an option
* since there is still disagreement about the right way this should
* work, and I don't want to repeatedly reimplement it */
static final boolean hideSingleExpansion = Boolean.getBoolean (
"netbeans.ps.hideSingleExpansion"); //NOI18N
/**If true, the left margin for expandable sets will be suppressed.
* Mainly desirable for small screens. */
static final boolean neverMargin = Boolean.getBoolean(
"netbeans.ps.neverMargin"); //NOI18N
/** If true, user will have to press enter to write the value,
* otherwise (the default), if a cell loses focus, its value
* gets written. */
static final boolean psCommitOnFocusLoss = !Boolean.getBoolean(
"netbeans.ps.NoCommitOnFocusLoss");
/** UIManager key for alternate color for table - if present, color will
* alternate between the standard background and this color */
private static final String KEY_ALTBG = "Tree.altbackground"; //NOI18N
/** UIManager key for the background color for expandable sets. If not
* set, the color will be derived from the default tree background
* color */
private static final String KEY_SETBG = "PropSheet.setBackground"; //NOI18N
/** UIManager key for background color of expandable sets when selected. If not
* set, the color will be derived from the default tree selection background
* color */
private static final String KEY_SELSETBG = "PropSheet.selectedSetBackground"; //NOI18N
/** UIManager key for foreground color of expandable sets. If not
* set, the color will be derived from the default tree selection background
* color */
private static final String KEY_SETFG = "PropSheet.setForeground";//NOI18N
/** UIManager key for foreground color of expandable sets when selected. If not
* set, the color will be derived from the default tree selection background
* color */
private static final String KEY_SELSETFG = "PropSheet.selectedSetForeground"; //NOI18N
/** UIManager key for integer icon margin, amount of space to add beside
* the expandable set icon to make up the margin */
private static final String KEY_ICONMARGIN = "netbeans.ps.iconmargin"; //NOI18N
/** UIManager key for fixed row height for rows in property sheet. If not
* explicitly set, row height will be derived from the target font */
static final String KEY_ROWHEIGHT = "netbeans.ps.rowheight"; //NOI18N
/** Preferences key for the show description area property */
private static final String PREF_KEY_SHOWDESCRIPTION="showDescriptionArea"; //NOI18N
/** Preferences key for the storage of closed set names */
private static final String PREF_KEY_CLOSEDSETNAMES="closedSetNames"; //NOI18N
/** Preferences key for the storage of sort order */
private static final String PREF_KEY_SORTORDER="sortOrder"; //NOI18N
/** A scratch image for use when we need a Graphics object for calculating
* size with no parent */
static BufferedImage scratch = null;
/** Disabled foreground color */
static Color disFg=null;
/** Factor by which default font is larger/smaller than 12 point, used for
* calculating preferred sizes and compensating for larger font size */
static float fsfactor=-1f;
/** Minimum width for a property panel */
static int minW=-1;
/** Minimum height for a property panel */
static int minH=-1;
/** TextField foreground color for property panel */
private static Color tfFg=null;
/** TextField background color for property panel */
private static Color tfBg=null;
/** Flag for presence of an alternative background color (alternating
* "zebra" style painting in property sheet) */
static Boolean noAltBg=null;
/** Icon used by the custom editor button */
private static Icon bpIcon=null;
/** Field to hold the width of the margin. This is used for painting, so the
* grid is not displayed in the margin, and for figuring out if a mouse event
* occured in the margin (toggle expanded on a single click) or not. */
static int marginWidth=-1;
/** Field to hold additional space between spinner icons and set text */
private static int iconMargin = -1;
/** Color for selected property set expanders - should be
* darker than the selection color for regular properties,
* to differentiate and be consistent with their unselected color */
static Color selectedSetRendererColor = null;
/** Color for property set expanders */
static Color setRendererColor=null;
/** Cached icon used for collapsed set renderers */
static Icon collapsedIcon = null;
/** Cached icon used for expanded set renderers */
static Icon expandedIcon = null;
/** Cached height of for the icon */
static int spinnerHeight = -1;
/** UIManager or derived control color */
static Color controlColor = null;
/** UIManager or derived shadow color */
static Color shadowColor = null;
/** Alternative background color */
static Color altBg = null;
/** Tab name for basic properties */
private static String bptn=null;
/** Comparator used by property sheet */
private static Comparator comp = null;
/** Left hand margin for properties in the right column of the sheet */
private static int textMargin = -1;
/** Foreground color for expando sets */
private static Color setForegroundColor=null;
/** Foreground color for expando sets when selected */
private static Color selectedSetForegroundColor=null;
/** Painting the custom editor button is the most expensive thing
* we do on XP and Aqua; if this flag is set, the button panel
* will build a bitmap and blit it (this isn't appropriate for
* Metal, where the background color of the button changes if it
* is selected */
private static Boolean useOptimizedCustomButtonPainting = null;
/** Private constructor to hide from API */
private PropUtils() {
//do nothing
}
/** If true, ButtonPanel will build a bitmap of the custom editor
* button to use when painting - huge amounts of painting time in
* XP and Aqua are used scaling the L&F's background button
* bitmap (and the custom editor button is always a fixed size),
* so this yields
* better performance when a large number of custom editor buttons
* are displayed. */
static boolean useOptimizedCustomButtonPainting() {
if (useOptimizedCustomButtonPainting == null) {
if ("com.sun.java.swing.plaf.WindowsLookAndFeel".equals(
UIManager.getLookAndFeel())) { //NOI18N
useOptimizedCustomButtonPainting = isXPTheme() ?
Boolean.TRUE : Boolean.FALSE;
} else if ("Aqua".equals (UIManager.getLookAndFeel().getID())) { //NOI18N
useOptimizedCustomButtonPainting = Boolean.TRUE;
} else {
useOptimizedCustomButtonPainting = Boolean.FALSE;
}
}
return useOptimizedCustomButtonPainting.booleanValue();
}
static void log(Class clazz, String msg, boolean dumpstack) {
log (clazz, msg);
if (dumpstack) {
dumpStack(clazz);
}
}
//logging code borrowed from winsys
static void log(Class clazz, String msg) {
if (isLoggable (clazz)){
ErrorManager.getDefault().getInstance(clazz.getName()).log(msg);
}
}
static void log (Class clazz, FocusEvent fe) {
if (isLoggable(clazz)) {
StringBuffer sb = new StringBuffer (30);
focusEventToString (fe, sb);
log (clazz, sb.toString());
}
}
static boolean isLoggable(Class clazz) {
if (System.getProperty(clazz.getName()) == null) {
return false;
}
boolean result = ErrorManager.getDefault().getInstance(
clazz.getName()).isLoggable(ErrorManager.INFORMATIONAL);
return result;
}
static void logFocusOwner(Class clazz, String where) {
if (isLoggable (clazz)) {
StringBuffer sb = new StringBuffer (where);
sb.append (" focus owner: "); //NOI18N
Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
compToString (owner, sb);
}
}
static void focusEventToString (FocusEvent fe, final StringBuffer sb) {
Component target = (Component) fe.getSource();
Component opposite = (Component) fe.getOppositeComponent();
sb.append (" focus "); //NOI18N
sb.append (fe.getID() == FocusEvent.FOCUS_GAINED ? " gained by " : " lost by "); //NOI18N
compToString (target, sb);
sb.append (fe.getID() == FocusEvent.FOCUS_GAINED ? " from " : " to "); //NOI18N
compToString (opposite, sb);
sb.append (" isTemporary: "); //NOI18N
sb.append (fe.isTemporary());
}
static void compToString (Component c, final StringBuffer sb) {
if (c == null) {
sb.append (" null "); //NOI18N
return;
}
String name = c.getName();
Class clazz = c.getClass();
String classname = clazz.getName();
int i = classname.lastIndexOf ('.');
if (i != -1 && i != classname.length() -1) {
classname = classname.substring (i + 1);
}
if (name != null) {
sb.append ("\""); //NOI18N
sb.append (name);
sb.append ("\" ("); //NOI18N
sb.append (classname);
sb.append (") "); //NOI18N
} else {
sb.append (' '); //NOI18N
sb.append (classname);
sb.append (' '); //NOI18N
}
if (!c.isVisible()) {
sb.append (" [NOT VISIBLE] "); //NOI18N
}
if (!c.isDisplayable()) {
sb.append (" [HAS NO PARENT COMPONENT] "); //NOI18N
}
}
public static void dumpStack(Class clazz) {
// log(Class,String) only has an effect if INFORMATIONAL logging enabled on that prefix
if(ErrorManager.getDefault().getInstance(clazz.getName())
.isLoggable(ErrorManager.INFORMATIONAL)) {
StringWriter sw = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(sw));
log(clazz, sw.getBuffer().toString());
}
}
/** Get the color for the custom editor button if specified by the theme or
* look and feel, or null if the defaults should simply be used */
static Color getButtonColor() {
return UIManager.getColor("netbeans.ps.buttonColor"); //NOI18N
}
static boolean isAqua = "Aqua".equals(UIManager.getLookAndFeel().getID()); //NOI18N
/** Get the width required by the custom editor button (this varies with
* font size). */
static int getCustomButtonWidth() {
Icon ic = getCustomButtonIcon();
return ic.getIconWidth() + (isAqua ? 5 : 3);
}
/** Check the myriad ways in which a property may be non-editable */
static boolean checkEnabled (Component c, PropertyEditor editor, PropertyEnv env){
if (editor instanceof NoPropertyEditorEditor) {
return false;
}
if (env != null) {
if (!env.isEditable()) {
return false;
}
}
return true;
}
private static Graphics scratchGraphics = null;
/** Get a scratch graphics object which can be used to calculate string
* widths offscreen */
static Graphics getScratchGraphics(Component c) {
if (scratchGraphics == null) {
scratchGraphics = new BufferedImage (1, 1, BufferedImage.TYPE_INT_RGB).getGraphics();
}
return scratchGraphics;
}
/** Get the color that should be used for text when an error or exception
* is encountered in displaying a value. Either the look and feel or
* theme can supply a color via the UIDefaults key nb.errorColor or
* a default (currently Color.RED) will be used */
static Color getErrorColor() {
//allow theme.xml to override error color
Color result=UIManager.getColor("nb.errorColor"); //NOI18N
if (result == null) {
result = Color.RED;
}
return result;
}
/** Get the foreground color for text on disabled components */
static Color getDisabledForeground () {
if (disFg == null) {
disFg=UIManager.getColor ("textInactiveText"); //NOI18N
if (disFg == null) {
disFg = Color.GRAY;
}
}
return disFg;
}
/** Get a factor of the difference between the default font size NetBeans
* uses, and the actual font size which may be different if the -fontsize
* argument was used on startup. */
static float getFontSizeFactor() {
if (fsfactor == -1) {
Font f = UIManager.getFont("controlFont"); //NOI18N
if (f == null) {
JLabel jl = new JLabel();
f = jl.getFont();
}
int baseSize = 12; //default font size
fsfactor = baseSize / f.getSize();
}
return fsfactor;
}
/** Minimum width for an instance of PropPanel, based on the default
* font size */
static int getMinimumPropPanelWidth() {
if (minW==-1) {
int base = 50;
minW = Math.round(base * getFontSizeFactor());
}
return minW;
}
/** Minimum height for an instance of PropPanel based on the default
* font size */
static int getMinimumPropPanelHeight() {
if (minH==-1) {
int base = 18;
minH = Math.round(base * getFontSizeFactor());
}
return minH;
}
/** Minimum size for an instance of PropPanel based on the default
* font size */
static Dimension getMinimumPanelSize() {
return new Dimension(getMinimumPropPanelWidth(),
getMinimumPropPanelHeight());
}
/** Update a property model with value provided by a property editor,
* showing a dialog if the value is invalid */
static boolean updateProp (PropertyModel mdl, PropertyEditor ed,
String title) {
// System.err.println("UPDATEPROP(model=" + mdl + ", editor=" + ed + ", title=" + title);
Object newValue = ed.getValue();
Object o = noDlgUpdateProp(mdl, ed);
// System.err.println(" result of noDlgUpdatePRop:" + o);
if (o instanceof Exception) {
processThrowable ((Exception) o, title, newValue);
}
boolean result = o instanceof Boolean ? ((Boolean) o).booleanValue() : false;
// System.err.println("RETURNING " + result);
return result;
}
/** Try to update a property model with the value held by a property editor.
* If any exceptions are thrown, return them - the invoker can choose to
* process them as it wishes, but it is preferable to return them unaltered
* rather than annotate using ErrorManager and force the client to fish
* through the annotations to find the exception of interest */
static Object noDlgUpdateProp(PropertyModel mdl, PropertyEditor ed) {
// System.err.println(" NO_DLG_UPDATE_PROP - editor value: " + ed.getValue() + " editor=" + ed + " model=" + mdl);
Object newValue = ed.getValue();
Object result = Boolean.FALSE;
try {
try {
Object oldValue = mdl.getValue();
// System.err.println("Model old value is " + oldValue + " newValue is " + newValue);
// test if newValue is not equal to oldValue
if ((newValue != null && !newValue.equals(oldValue)) ||
(newValue == null && oldValue != null)) {
mdl.setValue(newValue);
result = Boolean.TRUE;
} else {
// System.err.println(" New value equals old value - not trying to update");
}
} catch (ProxyNode.DifferentValuesException dve) {
//issue 34982, failure setting value of JSP breakpoint,
//and probably other cases as well
mdl.setValue(newValue);
result = Boolean.TRUE;
}
} catch (Exception e) {
result = e;
}
// System.err.println("NoDlgUpdateProp returning " + result);
return result;
}
/** Try to update a property editor with a value object (presumably)
* provided by an InplaceEditor. If exceptions are thrown, returns them
* for the caller to do with as it wishes (for example a panel that
* commits multiple values from several editors at once will probably want to
* aggregate them into a single error message) */
static Exception updatePropertyEditor (PropertyEditor ed, Object value) {
// System.err.println("UpdatePropertyEditor " + ed + " to " + value );
Exception result = null;
try {
if (value instanceof String) {
ed.setAsText ((String) value);
} else {
ed.setValue (value);
}
} catch (Exception e) {
result =e;
}
// System.err.println("returning " + result);
return result;
}
/** Update a property using the model and value cached by an inplace editor */
static boolean updateProp (InplaceEditor ine) {
// System.err.println("UPDATEPROP(inplaceEditor)");
Component c = ine.getComponent();
Cursor oldCursor = c.getCursor();
try {
c.setCursor (Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
Object o = ine.getValue();
Exception e = updatePropertyEditor(ine.getPropertyEditor(), o);
// System.err.println("UPDATE PROPERTY EDITOR RETURNED " + e);
String newValString = o == null ? NbBundle.getMessage (
PropUtils.class, "NULL") : o.toString(); //NOI18N
if (e != null) {
PropertyModel pm = ine.getPropertyModel();
String propName;
if (pm instanceof NodePropertyModel) {
Node.Property p = ((NodePropertyModel) pm).getProperty();
propName = p.getDisplayName();
} else if (pm instanceof DefaultPropertyModel) {
propName = ((DefaultPropertyModel) pm).propertyName;
} else {
//who knows what it is...
propName = NbBundle.getMessage (PropUtils.class,
"MSG_unknown_property_name"); //NOI18N
}
processThrowable (e, propName, newValString);
}
boolean result = e == null ? PropUtils.updateProp (ine.getPropertyModel(),
ine.getPropertyEditor(), newValString) : false;
// System.err.println("Returning " + result);
return result;
} finally {
c.setCursor (oldCursor);
}
}
/** Processes Throwable
thrown from setAsText
* or setValue
call on editor
. Helper method. */
private static void processThrowable(Throwable throwable, String title, Object newValue) {
//Copied from old PropertyPanel impl
if(throwable instanceof ThreadDeath) {
throw (ThreadDeath)throwable;
}
ErrorManager em = ErrorManager.getDefault();
ErrorManager.Annotation[] anns = em.findAnnotations(throwable);
if (((anns == null) || (anns.length==0)) && (throwable.getLocalizedMessage() != throwable.getMessage())) { //XXX See issue 34569
String msg = MessageFormat.format(NbBundle.getMessage(PropUtils.class,
"FMT_ErrorSettingProperty"), new Object[] {newValue, title}); //NOI18N
em.annotate(throwable, ErrorManager.USER, msg, throwable.getLocalizedMessage(), throwable, new java.util.Date());
} else if (throwable instanceof NumberFormatException) {
//Handle NFE's from the core sun.beans property editors w/o raising stack traces
em.annotate(throwable, ErrorManager.USER, throwable.getMessage(),
MessageFormat.format(NbBundle.getMessage (
PropUtils.class, "FMT_BAD_NUMBER_FORMAT"), //NOI18N
new Object[]{newValue}),
null, null);
}
em.notify(throwable);
}
/** Fetches a localized message for an exception that may be displayed to
* the user. If no localized message can be found in the annotations,
* it will provide a generic one.
* @see org.openide.explorer.propertysheet.PropertyDisplayer.Editable.isModifiedValueLegal */
static synchronized String findLocalizedMessage(Throwable throwable, Object newValue, String title) {
try {
if (throwable == null) {
//need to catch this - for mysterious reasons, calling
//getLocalizedMessage on a null throwable hangs/or results in an
//endless loop - thread will stop doing anything unrecoverably
return null;
}
ErrorManager em = ErrorManager.getDefault();
if (throwable.getLocalizedMessage() != throwable.getMessage()) {
return throwable.getLocalizedMessage();
}
ErrorManager.Annotation[] anns = em.findAnnotations(throwable);
if (throwable instanceof NumberFormatException) {
//Handle NFE's from the core sun.beans property editors w/o raising stack traces
return MessageFormat.format(NbBundle.getMessage (
PropUtils.class, "FMT_BAD_NUMBER_FORMAT"), //NOI18N
new Object[]{newValue});
} else if (/* #34596 */ anns != null) {
for (int i=0; i < anns.length; i++) {
if (anns[i].getLocalizedMessage() != null && anns[i].getSeverity() == ErrorManager.USER) {
return anns[i].getLocalizedMessage();
}
}
}
//No localized message could be found, log the exception
ErrorManager.getDefault().annotate(throwable, ErrorManager.WARNING,
null, null, null, null);
//punt
return MessageFormat.format(NbBundle.getMessage(PropUtils.class,
"FMT_CannotUpdateProperty"), new Object[] {newValue, title}); //NOI18N
} catch (Exception e) {
//We ABSOLUTELY cannot let this method throw exceptions or it will
//quietly endlessly
ErrorManager.getDefault().notify(e);
return null;
}
}
/** Utility method to fetch a comparator for properties
* based on sorting mode defined in PropertySheet. */
static Comparator getComparator (int sortingMode) {
switch (sortingMode) {
case PropertySheet.UNSORTED:
return null;
case PropertySheet.SORTED_BY_NAMES:
return SORTER_NAME;
case PropertySheet.SORTED_BY_TYPES:
return SORTER_TYPE;
default:
throw new IllegalArgumentException (
"Unknown sorting mode: " + Integer.toString(sortingMode)); //NOI18N
}
}
private static final String CAN_COMPARE_STRING = "Can compare only Node.Property instances.";
//Comparators copied from original propertysheet implementation
/** Comparator which compares types */
private final static Comparator SORTER_TYPE = new Comparator () {
public int compare (Object l, Object r) {
if (! (l instanceof Node.Property)) {
throw new IllegalArgumentException(CAN_COMPARE_STRING); // NOI18N
}
if (! (r instanceof Node.Property)) {
throw new IllegalArgumentException(CAN_COMPARE_STRING); // NOI18N
}
Class t1 = ((Node.Property)l).getValueType();
Class t2 = ((Node.Property)r).getValueType();
String s1 = t1 != null ? t1.getName() : ""; //NOI18N
String s2 = t2 != null ? t2.getName() : ""; //NOI18N
int s = s1.compareToIgnoreCase (s2);
if (s != 0) return s;
s1 = ((Node.Property)l).getDisplayName();
s2 = ((Node.Property)r).getDisplayName();
return s1.compareToIgnoreCase(s2);
}
public String toString() {
return "Type comparator";//NOI18N
}
};
/** Comparator which compares PropertyDetils names */
private final static Comparator SORTER_NAME = new Comparator () {
public int compare (Object l, Object r) {
if (! (l instanceof Node.Property)) {
throw new IllegalArgumentException(CAN_COMPARE_STRING);
}
if (! (r instanceof Node.Property)) {
throw new IllegalArgumentException(CAN_COMPARE_STRING);
}
String s1 = ((Node.Property)l).getDisplayName();
String s2 = ((Node.Property)r).getDisplayName();
return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
}
public String toString() {
return "Name comparator";//NOI18N
}
};
private static java.util.List missing = null;
/** Property editor for properties which belong to more than one property, but have
* different values. */
static final class DifferentValuesEditor implements PropertyEditor {
private PropertyEditor ed;
private boolean notSet = true;
public DifferentValuesEditor(PropertyEditor ed) {
this.ed = ed;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
ed.addPropertyChangeListener(listener);
}
public String getAsText() {
String result;
if (notSet) {
result = NbBundle.getMessage(PropUtils.class,
"CTL_Different_Values"); //NOI18N
} else {
result = ed.getAsText();
}
return result;
}
public java.awt.Component getCustomEditor() {
return null;//ed.getCustomEditor();
}
public String getJavaInitializationString() {
return ed.getJavaInitializationString();
}
public String[] getTags() {
return ed.getTags();
}
public Object getValue() {
Object result;
if (notSet) {
result = null;
} else {
result = ed.getValue();
}
return result;
}
public boolean isPaintable() {
return notSet ? false : ed.isPaintable();
}
public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {
//issue 33341 - don't allow editors to paint if value not set
if (isPaintable()) {
ed.paintValue(gfx, box);
}
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
ed.removePropertyChangeListener(listener);
}
public void setAsText(String text) throws java.lang.IllegalArgumentException {
ed.setAsText(text);
notSet = false;
}
public void setValue(Object value) {
ed.setValue(value);
notSet=false;
}
public boolean supportsCustomEditor() {
return false;
}
}
/** Dummy property editor for properties which have no real editor.
* The property sheet does not handle null property editors; this editor
* stands in, and returns "No property editor" from getAsText() */
static final class NoPropertyEditorEditor implements PropertyEditor {
public void addPropertyChangeListener(PropertyChangeListener listener) {
//no-op
}
public String getAsText() {
return NbBundle.getMessage(PropertySheet.class,
"CTL_NoPropertyEditor"); //NOI18N
}
public java.awt.Component getCustomEditor() {
return null;
}
public String getJavaInitializationString() {
return "";
}
public String[] getTags() {
return null;
}
public Object getValue() {
return getAsText();
}
public boolean isPaintable() {
return false;
}
public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) {
//no-op
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
//no-op
}
public void setAsText(String text) throws java.lang.IllegalArgumentException {
//no-op
}
public void setValue(Object value) {
//no-op
}
public boolean supportsCustomEditor() {
return false;
}
}
/** Create a ComboBoxUI that does not display borders on the combo box.
* Since the property sheet is rendered as a table, this looks better.
* This UI also delegates closing of its popup to the property sheet, which
* has better knowledge of when this is appropriate, in the case of focus
* changes. Inplace editors which employ a combo box should use the UI
* returned by this method, rather than the default for the look and feel.
* Thus the appearance will be consistent with the rest of the property
* sheet. */
public static javax.swing.plaf.ComboBoxUI createComboUI (JComboBox box, boolean tableUI) {
return new CleanComboUI(tableUI);
}
/** A convenience map for missing property editor classes, so users
* are only notified once, not every time a table cell is drawn.
*/
private static java.util.List getMissing() {
if (missing == null) {
missing = new ArrayList();
}
return missing;
}
/** Gets a property editor appropriate to the class. First
* checks property editors registered via XML layers, then
* falls back to java.beans.PropertyEditorManager
.
*/
static PropertyEditor getPropertyEditor(Class c) {
PropertyEditor result = PropertyEditorManager.findEditor(c);
if (result == null) {
result = new NoPropertyEditorEditor();
}
return result;
}
/** Gets a property editor appropriate to the property.
* This method centralizes all code for fetching property editors
* used by the property sheet, such that future alternative
* registration systems for property editors may be easily
* implemented.
* Note: This method will return a property
* editor with the value of the property already assigned. Client
* code need not set the value in the property editor unless it
* should be changed, as this can result in unnecessary event
* firing.
*/
static PropertyEditor getPropertyEditor(Property p) {
PropertyEditor result = p.getPropertyEditor();
//XXX Next few lines replicate a hack in the original property sheet.
//Correct solution is to move IndexedPropertyEditor & custom editor
//to the Nodes package and just return an instance from
//IndexedProperty.getPropertyEditor. Appears to be here due to some
//kind of dependancy avoidance.
if (p instanceof Node.IndexedProperty && result == null) {
result = new IndexedPropertyEditor();
// indexed property editor does not want to fire immediately
p.setValue(PropertyEnv.PROP_CHANGE_IMMEDIATE, Boolean.FALSE);
}
if (result == null) {
result = getPropertyEditor(p.getValueType()); //XXX is this agood idea?
}
//handle a type with no registered property editor here
if (result == null) {
java.util.List missing = getMissing();
String type = p.getValueType().getName();
if (!(missing.contains(type))) {
ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL,
"No property editor registered for type " + type); //NOI18N
missing.add(type);
}
result = new NoPropertyEditorEditor();
} else if (p.canRead()) {
try {
try {
try {
if ((p.getValueType() == Boolean.class || p.getValueType() == Boolean.TYPE) && p.getValue() == null) {
//Allows Module folder nodes that use null to indicate indeterminate state to work
// System.err.println("Property is " + p.getClass().getName() + " - " + p + " value is " + p.getValue());
result = new Boolean3WayEditor();
}
updateEdFromProp(p, result, p.getDisplayName());
} catch (ProxyNode.DifferentValuesException dve) {
if (p.getValueType() == Boolean.class ||
p.getValueType() == Boolean.TYPE) {
result = new Boolean3WayEditor();
} else {
result = new DifferentValuesEditor(result);
}
}
} catch (IllegalAccessException iae) {
IllegalStateException ise = new IllegalStateException(
"Error getting property value"); //NOI18N
ErrorManager.getDefault().annotate(ise, iae);
throw ise;
}
} catch (InvocationTargetException ite) {
IllegalStateException ise = new IllegalStateException(
"Error getting property value"); //NOI18N
ErrorManager.getDefault().annotate(ise, ite);
throw ise;
}
}
return result;
}
/** Update a property editor from a Node.Property, such that the
* value in the property editor will reflect the value of the property */
static void updateEdFromProp(Property p, PropertyEditor ed,
String title) throws
ProxyNode.DifferentValuesException,
IllegalAccessException,
InvocationTargetException {
Object newValue = p.getValue();
//IZ 44152, monstrous length strings from the debugger - skip the
//equality test and blindly assign
if (newValue instanceof String && ((String) newValue).length() > 2048) {
ed.setValue(newValue);
return;
}
Object oldValue = ed.getValue();
if (newValue==null && oldValue==null) return;
// test if newValue is not equal to oldValue
if ((newValue != null && !newValue.equals(oldValue)) ||
(newValue == null && oldValue != null)) {
//
// #5050567 Kind of mysterious livelock experienced here.
// When updating arrays.
// The question is whether the setter of the bean has to check if the value
// is equal and not fire property change.. or is if the responsibility
// is also here.
if(oldValue instanceof Object[] && newValue instanceof Object[]
&& Arrays.equals((Object[])oldValue, (Object[])newValue)) {
return;
}
//
ed.setValue(newValue);
}
}
/** Get the basic color for controls in the look and feel, or a reasonable
* default if the look and feel does not supply a value from
* UIManager.getColor("control") */
static Color getControlColor () {
if (controlColor == null) {
deriveColorsAndMargin();
}
return controlColor;
}
/** Get the shadow color specified by the look and feel, or a reasonable
* default if none was specified */
static Color getShadowColor () {
if (shadowColor == null) {
deriveColorsAndMargin();
}
return shadowColor;
}
/** Get the alternate background color, if any. If non-null, the table will
* show every other row using the color specified here */
static Color getAltBg() {
if (altBg == null) {
deriveColorsAndMargin();
}
return altBg;
}
/** Determine if an alternate background color has been specified in the
* look and feel or theme. If one is supplied, then the property sheet
* table will not show a grid, but instead, every other line will have
* a different background color. This method simply avoids repeated
* attempts to look up the alternate background color if none was specified */
static boolean noAltBg() {
if (noAltBg == null) {
noAltBg =
UIManager.getColor(KEY_ALTBG) == null ? //NOI18N
Boolean.TRUE : Boolean.FALSE;
}
return noAltBg.booleanValue();
}
/** Get the forground color for text editable property editors in edit
* mode. This is required because the tree selection color is typically
* the same color as the tree selection color. So if a string editor is
* given the background color of a selected row in the table, it is
* impossible to tell when some text in the field has been selected,
* because they will be the same color. */
static Color getTextFieldBackground() {
if (tfBg == null) {
tfBg = UIManager.getColor("TextField.background"); //NOI18N
if (tfBg == null) {
tfBg = UIManager.getColor("text"); //NOI18N
}
if (tfBg == null) {
tfBg = Color.WHITE;
}
}
return tfBg;
}
/** Get the forground color for text editable property editors in edit
* mode. This is required because the tree selection color is typically
* the same color as the tree selection color. */
static Color getTextFieldForeground() {
if (tfFg == null) {
tfFg = UIManager.getColor("TextField.foreground"); //NOI18N
if (tfFg == null) {
tfFg = UIManager.getColor("textText"); //NOI18N
}
if (tfFg == null) {
tfFg = Color.BLACK;
}
}
return tfFg;
}
/** Initialize the various colors we will be using. */
private static void deriveColorsAndMargin() {
controlColor = UIManager.getColor ("control"); //NOI18N
if (controlColor == null) controlColor = Color.LIGHT_GRAY;
int red, green, blue;
boolean windows =
"com.sun.java.swing.plaf.windows.WindowsLookAndFeel".equals(
UIManager.getLookAndFeel().getClass().getName());
boolean aqua = "Aqua".equals(UIManager.getLookAndFeel().getID());
setRendererColor = UIManager.getColor(KEY_SETBG); //NOI18N
selectedSetRendererColor = UIManager.getColor (
KEY_SELSETBG); //NOI18N
if (setRendererColor == null) {
if (aqua) {
setRendererColor = new Color(225,235,240);
} else {
if (setRendererColor == null) {
red = adjustColorComponent (controlColor.getRed(), -25, -25);
green = adjustColorComponent (controlColor.getGreen(), -25, -25);
blue = adjustColorComponent (controlColor.getBlue(), -25, -25);
setRendererColor = new Color (red, green, blue);
}
}
}
if (aqua) {
selectedSetRendererColor = UIManager.getColor("Table.selectionBackground");
}
if (selectedSetRendererColor == null) {
Color col = windows ? UIManager.getColor("Table.selectionBackground")
: UIManager.getColor ("activeCaptionBorder"); //NOI18N
if (col == null) col = Color.BLUE;
red = adjustColorComponent (col.getRed(), -25, -25);
green = adjustColorComponent (col.getGreen(), -25, -25);
blue = adjustColorComponent (col.getBlue(), -25, -25);
selectedSetRendererColor = new Color (red, green, blue);
}
shadowColor = UIManager.getColor ("controlShadow"); //NOI18N
if (shadowColor == null) shadowColor = Color.GRAY;
setForegroundColor = UIManager.getColor(KEY_SETFG);
if (setForegroundColor == null) {
setForegroundColor = UIManager.getColor("Table.foreground"); //NOI18N
if (setForegroundColor == null) {
setForegroundColor = UIManager.getColor("textText");
if (setForegroundColor == null) {
setForegroundColor = Color.BLACK;
}
}
}
selectedSetForegroundColor = UIManager.getColor(KEY_SELSETFG);
if (selectedSetForegroundColor == null) {
selectedSetForegroundColor = UIManager.getColor(
"Table.selectionForeground"); //NOI18N
if (selectedSetForegroundColor == null) {
selectedSetForegroundColor = Color.WHITE;
}
}
altBg = UIManager.getColor (KEY_ALTBG); //NOI18N
if (altBg == null) {
altBg = UIManager.getColor ("Tree.background"); //NOI18N
if (altBg == null) {
altBg = Color.WHITE;
}
noAltBg=Boolean.TRUE;
} else {
noAltBg=Boolean.FALSE;
}
collapsedIcon = UIManager.getIcon ("Tree.collapsedIcon"); //NOI18N
if (collapsedIcon == null) {
collapsedIcon = new Icon () {
public int getIconHeight () {
return 7;
}
public int getIconWidth() {
return 7;
}
public void paintIcon (Component c, Graphics g, int x, int y) {
//do nothing
}
};
}
expandedIcon = UIManager.getIcon ("Tree.expandedIcon"); //NOI18N
if (expandedIcon == null) {
//Hack for jdk 1.4.2 beta GTK L&F
expandedIcon = collapsedIcon;
}
//GTK absurdly provides a 0 width icons which will figure out its size
//the first time it paints
if (expandedIcon != null && expandedIcon.getIconHeight() <= 0 || expandedIcon.getIconHeight() <= 0) {
expandedIcon = new WrapperSizeIcon (expandedIcon);
}
if (collapsedIcon != null && collapsedIcon.getIconHeight() <= 0 || collapsedIcon.getIconHeight() <= 0) {
collapsedIcon = new WrapperSizeIcon (collapsedIcon);
}
if (collapsedIcon != null) {
marginWidth = Math.max (14, collapsedIcon.getIconWidth() - 2);
} else {
//on the off chance a L&F doesn't provide this
marginWidth = 13;
}
// System.err.println("Collapsed icon height " + (collapsedIcon == null ? " null " : Integer.toString (collapsedIcon.getIconHeight())));
Integer i = (Integer) UIManager.get (KEY_ICONMARGIN); //NOI18N
if (i != null) {
iconMargin = i.intValue();
} else {
if ("com.sun.java.swing.plaf.windows.WindowsLookAndFeel".equals(
UIManager.getLookAndFeel().getClass().getName())) {
iconMargin = 4;
} else {
iconMargin = 0;
}
}
i = (Integer) UIManager.get (KEY_ROWHEIGHT); //NOI18N
if (i != null) {
spinnerHeight = i.intValue();
} else {
spinnerHeight = expandedIcon.getIconHeight();
}
}
/** Get the icon displayed by an expanded set. Typically this is just the
* same icon the look and feel supplies for trees */
static Icon getExpandedIcon () {
if (expandedIcon == null) {
deriveColorsAndMargin();
}
return expandedIcon;
}
/** Get the icon displayed by a collapsed set. Typically this is just the
* icon the look and feel supplies for trees */
static Icon getCollapsedIcon () {
if (collapsedIcon == null) {
deriveColorsAndMargin();
}
return collapsedIcon;
}
/** Get the color for expandable sets when they are not selected */
static Color getSetRendererColor () {
if (setRendererColor == null) {
deriveColorsAndMargin();
}
return setRendererColor;
}
/** Get the background color for expandable sets when they are selected */
static Color getSelectedSetRendererColor() {
if (selectedSetRendererColor == null) {
deriveColorsAndMargin();
}
return selectedSetRendererColor;
}
/** Get the color for expandable sets when not selected */
static Color getSetForegroundColor() {
if (setForegroundColor == null) {
deriveColorsAndMargin();
}
return setForegroundColor;
}
/** Get the text color for expandable sets when they are selected */
static Color getSelectedSetForegroundColor() {
if (selectedSetForegroundColor == null) {
deriveColorsAndMargin();
}
return selectedSetForegroundColor;
}
/** Get the total width that the left side margin should have. This is
* calculated based on the width of the expandable set icon plus some
* spacing */
static int getMarginWidth() {
if (marginWidth == -1) {
deriveColorsAndMargin();
}
return marginWidth;
}
/** Get the height of the expansion icon */
static int getSpinnerHeight() {
if (spinnerHeight == -1) {
deriveColorsAndMargin();
}
return spinnerHeight;
}
/** Get the number of pixels that should separate the expandable set
* icon from the left edge of the property sheet. For Metal, this can
* be zero; for the smaller icon supplied by Windows look and feels,
* it should be a larger number */
static int getIconMargin () {
if (iconMargin == -1) {
deriveColorsAndMargin();
}
return iconMargin;
}
/** Lazily creates the custom editor button icon */
static Icon getCustomButtonIcon() {
if (bpIcon == null) {
bpIcon = new BpIcon();
}
return bpIcon;
}
/** Adjust an rgb color component.
* @param base the color, an RGB value 0-255
* @param adjBright the amount to subtract if base > 128
* @param adjDark the amount to add if base <=128 */
private static int adjustColorComponent (int base, int adjBright, int adjDark) {
if (base > 128) {
base -= adjBright;
} else {
base += adjDark;
}
if (base < 0) base = 0;
if (base > 255) base = 255;
return base;
}
/** Get the localized name for the default category of properties - in
* English this is "Properties". Used to provide the basic category
* and tab name */
static String basicPropsTabName () {
if (bptn == null) {
bptn = NbBundle.getMessage (PropUtils.class, "LBL_BasicTab"); //NOI18N
}
return bptn;
}
static Comparator getTabListComparator() {
if (comp == null) {
comp = new TabListComparator();
}
return comp;
}
/** Comparator for sorting the list of tabs such that basic properties
* is always the first tab. */
private static class TabListComparator implements Comparator {
public int compare(Object o1, Object o2) {
String s1 = (String) o1;
String s2 = (String) o2;
if (s1 == s2) return 0;
String bn = basicPropsTabName();
if (bn.equals(s1)) return -1;
if (bn.equals(s2)) return 1;
return s1.compareTo(s2);
}
}
static SplitPaneUI createSplitPaneUI () {
return new CleanSplitPaneUI();
}
private static class CleanSplitPaneUI extends BasicSplitPaneUI {
protected void installDefaults(){
super.installDefaults();
divider.setBorder (new SplitBorder());
}
}
/** A split border subclass that does not draw the metal drag texture */
private static class SplitBorder implements Border {
public Insets getBorderInsets(Component c) {
if (UIManager.getLookAndFeel() instanceof MetalLookAndFeel) {
return new Insets (2,0,1,0);
} else {
return new Insets (1,0,1,0);
}
}
public boolean isBorderOpaque() {
return true;
}
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
if (UIManager.getLookAndFeel() instanceof MetalLookAndFeel) {
g.setColor (UIManager.getColor ("controlShadow"));
g.drawLine (x, y, x+width, y);
g.setColor (UIManager.getColor ("controlHighlight"));
g.drawLine (x, y+1, x+width, y+1);
g.drawLine (x, y + height-1, x+width, y + height-1);
g.setColor (UIManager.getColor ("controlShadow"));
g.drawLine (x, y + height -2, x+width, y + height-2);
} else {
//don't do flush 3d for non-metal L&F
g.setColor (UIManager.getColor ("controlHighlight"));
g.drawLine (x, y, x+width, y);
g.setColor (UIManager.getColor ("controlShadow"));
g.drawLine (x, y + height-1, x+width, y + height-1);
}
}
}
/*
private static Preferences getPreferences() {
return Preferences.userNodeForPackage(
PropertySheet.class);
}
*/
// private static boolean shouldShowDescription=true; //XXX pending registry
static boolean shouldShowDescription() {
// return shouldShowDescription; //XXX pending registry
// return getPreferences().getBoolean(PREF_KEY_SHOWDESCRIPTION, true);
return PsSettings.getDefault().getBoolean(PREF_KEY_SHOWDESCRIPTION,
true);
}
static void saveShowDescription(boolean b) {
// shouldShowDescription = b; //XXX pending registry
// getPreferences().putBoolean(PREF_KEY_SHOWDESCRIPTION, b);
PsSettings.getDefault().putBoolean(PREF_KEY_SHOWDESCRIPTION, b);
}
static String[] getSavedClosedSetNames () {
// String s = getPreferences().get(PREF_KEY_CLOSEDSETNAMES, null);
// String s = savedClosedSetNames; //XXX pending registry
String s = PsSettings.getDefault().get(PREF_KEY_CLOSEDSETNAMES, null);
if (s != null) {
StringTokenizer tok = new StringTokenizer (s, ","); //NOI18N
String[] result = new String[tok.countTokens()];
int i=0;
while (tok.hasMoreElements()) {
result[i] = tok.nextToken();
i++;
}
return result;
} else {
return new String[0];
}
}
// private static String savedClosedSetNames=""; //XXX pending registry
static void putSavedClosedSetNames (Set s) {
if (s.size() > 0) {
StringBuffer sb = new StringBuffer(s.size() * 20);
Iterator i = s.iterator();
while (i.hasNext()) {
sb.append (i.next());
if (i.hasNext()) {
sb.append (','); //NOI18N
}
}
// savedClosedSetNames=sb.toString(); //XXX pending registry
// getPreferences().put (PREF_KEY_CLOSEDSETNAMES, sb.toString());
PsSettings.getDefault().put(PREF_KEY_CLOSEDSETNAMES, sb.toString());
} else {
// getPreferences().put (PREF_KEY_CLOSEDSETNAMES, "");
// savedClosedSetNames=""; //XXX pending registry
PsSettings.getDefault().put(PREF_KEY_CLOSEDSETNAMES, "");
}
}
// private static int sortOrder=PropertySheet.UNSORTED;
static void putSortOrder (int i) {
// getPreferences().putInt(PREF_KEY_SORTORDER, i);
// sortOrder=i;
PsSettings.getDefault().putInt(PREF_KEY_SORTORDER, i);
}
static int getSavedSortOrder() {
// return getPreferences().getInt(PREF_KEY_SORTORDER, PropertySheet.UNSORTED);
// return sortOrder;
return PsSettings.getDefault().getInt(PREF_KEY_SORTORDER,
PropertySheet.UNSORTED);
}
/** Fetch the margin that should come between the edge of a property sheet
* cell and its text. The default is 2, or it can be customized via the
* UIManager integer key netbeans.ps.textMargin */
static int getTextMargin() {
if ("apple.laf.AquaLookAndFeel".equals(UIManager.getLookAndFeel().getClass().getName())) {
return 0;
}
if (textMargin == -1) {
Object o = UIManager.get("netbeans.ps.textMargin"); //NOI18N
if (o instanceof Integer) {
textMargin = ((Integer) o).intValue();
} else {
textMargin=2;
}
}
return textMargin;
}
/** HTML-ize a tooltip, splitting long lines */
static String createHtmlTooltip(String title, String s) {
if (s.indexOf("<") != -1) { //NOI18N
Utilities.replaceString(s, "<", "<"); //NOI18N
}
if (s.indexOf(">") != -1) { //NOI18N
Utilities.replaceString(s, ">", ">"); //NOI18N
}
String token=null;
if (s.indexOf(" ") != -1) { //NOI18N
token = " "; //NOI18N
} else if (s.indexOf(",") != -1) { //NOI18N
token = ","; //NOI18N
} else if (s.indexOf(";") != -1) { //NOI18N
token = ";"; //NOI18N
} else if (s.indexOf("/") != -1) { //NOI18N
token = "/"; //NOI18N
} else if (s.indexOf("\\") != -1) { //NOI18N
token = "\\"; //NOI18N
} else {
//give up
return s;
}
StringTokenizer tk = new StringTokenizer (s, token); //NOI18N
StringBuffer sb = new StringBuffer (s.length() + 20);
sb.append (""); //NOI18N
sb.append (""); //NOI18N
sb.append (title);
sb.append ("
"); //NOI18N
int charCount=0;
int lineCount=0;
while (tk.hasMoreTokens()) {
String a = tk.nextToken();
a = Utilities.replaceString(a, "<", "<"); //NOI18N
a = Utilities.replaceString(a, ">", ">"); //NOI18N
charCount += a.length();
sb.append (a);
if (tk.hasMoreTokens()) {
charCount++;
}
if (charCount > 80) {
sb.append ("
"); //NOI18N
charCount = 0;
lineCount++;
if (lineCount > 10) {
//Don't let things like VCS variables create
//a tooltip bigger than the screen. 99% of the
//time this is not a problem.
sb.append (NbBundle.getMessage(PropUtils.class,
"MSG_ELLIPSIS")); //NOI18N
return sb.toString();
}
} else {
sb.append (token); //NOI18N
}
}
sb.append (""); //NOI18N
return sb.toString();
}
/** ButtonPanel and IconPanel implement inplace editor and proxy the
* inner component that is the thing the user actually interacts with.
* This method will locate the inner component of interest */
static InplaceEditor findInnermostInplaceEditor(InplaceEditor ine) {
while (ine instanceof IconPanel || ine instanceof ButtonPanel) {
if (ine instanceof IconPanel) {
ine = ((IconPanel) ine).getInplaceEditor();
} else {
ine = ((ButtonPanel) ine).getInplaceEditor();
}
}
return ine;
}
/** Utility method to determine if the left edge margin should
* be painted. This is determined by whether the property sheet
* items are sorted by name or natural sort, or can be overridden
* globally with a startup flag */
static boolean shouldDrawMargin (PropertySetModel psm) {
if (neverMargin) return false;
//hide margin and expansion handle if only one property
//set, if flag is set (there is disagreement about the
//right thing to do here, so it's an option for now)
int setCount = psm.getSetCount();
if (psm.getComparator() != null) return false;
boolean includeMargin =
setCount > 1 ||
(!(setCount == 1 && hideSingleExpansion));
return includeMargin;
}
/** Returns a fixed foreground color for the custom button icon on win xp.
* On win xp, the button background is a white bitmap, so if we use the
* white foreground color for the cell, the ... in the icon seems to
* disappear when a row with a custom editor button is selected.
* @see org.netbeans.core.NbTheme.installCustomDefaultsWinXP */
private static final Color getIconForeground() {
return UIManager.getColor("PropSheet.customButtonForeground"); //NOI18N
}
/** The ... icon for the custom editor button. Will draw slightly
* larger if the font size is greater */
static class BpIcon implements Icon {
boolean larger;
public BpIcon () {
Font f = UIManager.getFont("Table.font"); //NOI18N
larger = f != null ? f.getSize() > 13 : false;
}
public int getIconHeight() {
return 12;
}
public int getIconWidth() {
return larger ? 16 : 12;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
int w = c.getWidth();
int h = c.getHeight();
int ybase = h-5;
int pos2 = (w/2);
int pos1 = pos2 - 4;
int pos3 = pos2 + 4;
g.setColor (getIconForeground() == null ? c.getForeground() : getIconForeground());
drawDot (g, pos1+1, ybase, larger);
drawDot (g, pos2, ybase, larger);
drawDot (g, pos3-1, ybase, larger);
}
private void drawDot (Graphics g, int x, int y, boolean larger) {
if (!larger) {
g.drawLine(x, y, x, y);
} else{
g.drawLine(x-1, y, x+1, y);
g.drawLine(x, y-1, x, y+1);
}
}
}
public static boolean isXPTheme () {
Boolean isXP = (Boolean)Toolkit.getDefaultToolkit().
getDesktopProperty("win.xpstyle.themeActive");
return isXP == null ? false : isXP.booleanValue();
}
/**
* Workaround for GTK L&F - they provide an icon which does not know its
* width or height until it has been painted. So for it to work correctly,
* we have this silliness.
*/
private static final class WrapperSizeIcon implements Icon {
private Icon wrapped;
public WrapperSizeIcon (Icon wrapped) {
this.wrapped = wrapped;
}
public int getIconWidth() {
int result = wrapped.getIconWidth();
if (result <= 0) {
result = 11;
}
return result;
}
public int getIconHeight() {
int result = wrapped.getIconHeight();
if (result <= 0) {
result = 11;
}
return result;
}
public void paintIcon(Component c, Graphics g, int x, int y) {
wrapped.paintIcon (c, g, x, y);
}
}
}