All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy