KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > explorer > propertysheet > PropUtils


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.openide.explorer.propertysheet;
20
21 import java.util.logging.Level JavaDoc;
22 import java.util.logging.Logger JavaDoc;
23 import java.util.prefs.Preferences JavaDoc;
24 import javax.accessibility.Accessible JavaDoc;
25 import javax.accessibility.AccessibleContext JavaDoc;
26 import org.openide.*;
27 import org.openide.nodes.*;
28 import org.openide.nodes.Node.*;
29 import org.openide.util.*;
30
31 import java.awt.*;
32 import java.awt.event.*;
33 import java.awt.image.BufferedImage JavaDoc;
34
35 import java.beans.*;
36
37 import java.io.PrintWriter JavaDoc;
38 import java.io.StringWriter JavaDoc;
39
40 import java.lang.reflect.InvocationTargetException JavaDoc;
41
42 import java.text.MessageFormat JavaDoc;
43
44 import java.util.*;
45 import javax.accessibility.AccessibleRole JavaDoc;
46
47 import javax.swing.*;
48 import javax.swing.border.Border JavaDoc;
49 import javax.swing.plaf.SplitPaneUI JavaDoc;
50 import javax.swing.plaf.basic.BasicSplitPaneDivider JavaDoc;
51 import javax.swing.plaf.basic.BasicSplitPaneUI JavaDoc;
52 import javax.swing.plaf.metal.*;
53
54 import org.netbeans.modules.openide.explorer.UIException;
55
56
57 /** A few utility methods useful to implementors of Inplace Editors.
58  * @author Tim Boudreau
59  */

60 final class PropUtils {
61     /**If true, hides custom editor buttons unless in editing mode.
62      * Auto popup of combo boxes is suppressed in this mode */

63     static final boolean noCustomButtons = Boolean.getBoolean("netbeans.ps.noCustomButtons"); //NOI18N
64

65     /**If true, radio button boolean editor will always be used */
66     static boolean forceRadioButtons =
67         // !Boolean.getBoolean ("netbeans.ps.useCheckbox");
68
Boolean.getBoolean("netbeans.ps.forceRadioButtons");
69
70     /**If true, caption on the checkbox boolean editor will be suppressed */
71     static final boolean noCheckboxCaption = !Boolean.getBoolean("netbeans.ps.checkboxCaption"); //NOI18N
72

73     /** Flag which, when true, property set expansion handles will not be
74      * shown when the node only has one property set. Leaving as an option
75      * since there is still disagreement about the right way this should
76      * work, and I don't want to repeatedly reimplement it */

77     static final boolean hideSingleExpansion = Boolean.getBoolean("netbeans.ps.hideSingleExpansion"); //NOI18N
78
static final boolean neverMargin = true;
79
80     // static final boolean neverMargin = Boolean.getBoolean(
81
// "netbeans.ps.neverMargin"); //NOI18N
82

83     /** If true, user will have to press enter to write the value,
84      * otherwise (the default), if a cell loses focus, its value
85      * gets written. */

86     static final boolean psCommitOnFocusLoss = !Boolean.getBoolean("netbeans.ps.NoCommitOnFocusLoss");
87
88     /** UIManager key for alternate color for table - if present, color will
89      * alternate between the standard background and this color */

90     private static final String JavaDoc KEY_ALTBG = "Tree.altbackground"; //NOI18N
91

92     /** UIManager key for the background color for expandable sets. If not
93      * set, the color will be derived from the default tree background
94      * color */

95     private static final String JavaDoc KEY_SETBG = "PropSheet.setBackground"; //NOI18N
96

97     /** UIManager key for background color of expandable sets when selected. If not
98      * set, the color will be derived from the default tree selection background
99      * color */

100     private static final String JavaDoc KEY_SELSETBG = "PropSheet.selectedSetBackground"; //NOI18N
101

102     /** UIManager key for foreground color of expandable sets. If not
103      * set, the color will be derived from the default tree selection background
104      * color */

105     private static final String JavaDoc KEY_SETFG = "PropSheet.setForeground"; //NOI18N
106

107     /** UIManager key for foreground color of expandable sets when selected. If not
108      * set, the color will be derived from the default tree selection background
109      * color */

110     private static final String JavaDoc KEY_SELSETFG = "PropSheet.selectedSetForeground"; //NOI18N
111

112     /** UIManager key for integer icon margin, amount of space to add beside
113      * the expandable set icon to make up the margin */

114     private static final String JavaDoc KEY_ICONMARGIN = "netbeans.ps.iconmargin"; //NOI18N
115

116     /** UIManager key for fixed row height for rows in property sheet. If not
117      * explicitly set, row height will be derived from the target font */

118     static final String JavaDoc KEY_ROWHEIGHT = "netbeans.ps.rowheight"; //NOI18N
119

120     private static Preferences JavaDoc preferences() {
121         return NbPreferences.forModule(PropUtils.class);
122     }
123
124     /** Preferences key for the show description area property */
125     private static final String JavaDoc PREF_KEY_SHOWDESCRIPTION = "showDescriptionArea"; //NOI18N
126

127     /** Preferences key for the storage of closed set names */
128     private static final String JavaDoc PREF_KEY_CLOSEDSETNAMES = "closedSetNames"; //NOI18N
129

130     /** Preferences key for the storage of sort order */
131     private static final String JavaDoc PREF_KEY_SORTORDER = "sortOrder"; //NOI18N
132

133     /** Disabled foreground color */
134     static Color disFg = null;
135
136     /** Factor by which default font is larger/smaller than 12 point, used for
137      * calculating preferred sizes and compensating for larger font size */

138     static float fsfactor = -1f;
139
140     /** Minimum width for a property panel */
141     static int minW = -1;
142
143     /** Minimum height for a property panel */
144     static int minH = -1;
145
146     /** TextField foreground color for property panel */
147     private static Color tfFg = null;
148
149     /** TextField background color for property panel */
150     private static Color tfBg = null;
151
152     /** Flag for presence of an alternative background color (alternating
153      * "zebra" style painting in property sheet) */

154     static Boolean JavaDoc noAltBg = null;
155
156     /** Field to hold the width of the margin. This is used for painting, so the
157      * grid is not displayed in the margin, and for figuring out if a mouse event
158      * occured in the margin (toggle expanded on a single click) or not. */

159     static int marginWidth = -1;
160
161     /** Field to hold additional space between spinner icons and set text */
162     private static int iconMargin = -1;
163
164     /** Color for selected property set expanders - should be
165      * darker than the selection color for regular properties,
166      * to differentiate and be consistent with their unselected color */

167     static Color selectedSetRendererColor = null;
168
169     /** Color for property set expanders */
170     static Color setRendererColor = null;
171
172     /** Cached height of for the icon */
173     static int spinnerHeight = -1;
174
175     /** UIManager or derived control color */
176     static Color controlColor = null;
177
178     /** UIManager or derived shadow color */
179     static Color shadowColor = null;
180
181     /** Alternative background color */
182     static Color altBg = null;
183
184     /** Tab name for basic properties */
185     private static String JavaDoc bptn = null;
186
187     /** Comparator used by property sheet */
188     private static Comparator comp = null;
189
190     /** Left hand margin for properties in the right column of the sheet */
191     private static int textMargin = -1;
192
193     /** Foreground color for expando sets */
194     private static Color setForegroundColor = null;
195
196     /** Foreground color for expando sets when selected */
197     private static Color selectedSetForegroundColor = null;
198
199     /** Painting the custom editor button is the most expensive thing
200      * we do on XP and Aqua; if this flag is set, the button panel
201      * will build a bitmap and blit it (this isn't appropriate for
202      * Metal, where the background color of the button changes if it
203      * is selected */

204     private static Boolean JavaDoc useOptimizedCustomButtonPainting = null;
205     static boolean isAqua = "Aqua".equals(UIManager.getLookAndFeel().getID()); //NOI18N
206
private static Graphics scratchGraphics = null;
207
208     //Comparators copied from original propertysheet implementation
209

210     /** Comparator which compares types */
211     private final static Comparator<Node.Property> SORTER_TYPE = new Comparator<Node.Property>() {
212             public int compare(Node.Property l, Node.Property r) {
213
214                 Class JavaDoc t1 = l.getValueType();
215                 Class JavaDoc t2 = r.getValueType();
216                 String JavaDoc s1 = (t1 != null) ? t1.getName() : ""; //NOI18N
217
String JavaDoc s2 = (t2 != null) ? t2.getName() : ""; //NOI18N
218

219                 int s = s1.compareToIgnoreCase(s2);
220
221                 if (s != 0) {
222                     return s;
223                 }
224
225                 s1 = l.getDisplayName();
226                 s2 = r.getDisplayName();
227
228                 return s1.compareToIgnoreCase(s2);
229             }
230
231             public String JavaDoc toString() {
232                 return "Type comparator"; //NOI18N
233
}
234         };
235
236     /** Comparator which compares PropertyDetils names */
237     private final static Comparator<Node.Property> SORTER_NAME = new Comparator<Node.Property>() {
238             public int compare(Node.Property l, Node.Property r) {
239                 String JavaDoc s1 = l.getDisplayName();
240                 String JavaDoc s2 = r.getDisplayName();
241
242                 return String.CASE_INSENSITIVE_ORDER.compare(s1, s2);
243             }
244
245             public String JavaDoc toString() {
246                 return "Name comparator"; //NOI18N
247
}
248         };
249
250     private static java.util.List JavaDoc<String JavaDoc> missing = null;
251
252     // #52179 don't affect just edited properties or their current
253
// changes will be lost due to the firing PropertyChangeEvents to
254
// theirs UI counterpart
255
private static Set<Property> externallyEdited = new HashSet<Property>(3);
256
257     /** Private constructor to hide from API */
258     private PropUtils() {
259         //do nothing
260
}
261
262     /** If true, ButtonPanel will build a bitmap of the custom editor
263      * button to use when painting - huge amounts of painting time in
264      * XP and Aqua are used scaling the L&F's background button
265      * bitmap (and the custom editor button is always a fixed size),
266      * so this yields
267      * better performance when a large number of custom editor buttons
268      * are displayed. */

269     static boolean useOptimizedCustomButtonPainting() {
270         if (useOptimizedCustomButtonPainting == null) {
271             if ("com.sun.java.swing.plaf.WindowsLookAndFeel".equals(UIManager.getLookAndFeel())) { //NOI18N
272
useOptimizedCustomButtonPainting = Boolean.valueOf(isXPTheme());
273             } else {
274                 useOptimizedCustomButtonPainting = Boolean.valueOf("Aqua".equals(UIManager.getLookAndFeel().getID()));
275             }
276         }
277
278         return useOptimizedCustomButtonPainting.booleanValue();
279     }
280
281     static void log(Class JavaDoc clazz, String JavaDoc msg, boolean dumpstack) {
282         log(clazz, msg);
283
284         if (dumpstack) {
285             dumpStack(clazz);
286         }
287     }
288
289     //logging code borrowed from winsys
290
static void log(Class JavaDoc clazz, String JavaDoc msg) {
291         Logger.getLogger(clazz.getName()).fine(msg);
292     }
293
294     static void log(Class JavaDoc clazz, FocusEvent fe) {
295         if (isLoggable(clazz)) {
296             StringBuffer JavaDoc sb = new StringBuffer JavaDoc(30);
297             focusEventToString(fe, sb);
298             log(clazz, sb.toString());
299         }
300     }
301
302     static boolean isLoggable(Class JavaDoc clazz) {
303         return Logger.getLogger(clazz.getName()).isLoggable(Level.FINE);
304     }
305
306     static void logFocusOwner(Class JavaDoc clazz, String JavaDoc where) {
307         if (isLoggable(clazz)) {
308             StringBuffer JavaDoc sb = new StringBuffer JavaDoc(where);
309             sb.append(" focus owner: "); //NOI18N
310

311             Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
312             compToString(owner, sb);
313         }
314     }
315
316     static void focusEventToString(FocusEvent fe, final StringBuffer JavaDoc sb) {
317         Component target = (Component) fe.getSource();
318         Component opposite = (Component) fe.getOppositeComponent();
319         sb.append(" focus "); //NOI18N
320
sb.append((fe.getID() == FocusEvent.FOCUS_GAINED) ? " gained by " : " lost by "); //NOI18N
321
compToString(target, sb);
322         sb.append((fe.getID() == FocusEvent.FOCUS_GAINED) ? " from " : " to "); //NOI18N
323
compToString(opposite, sb);
324         sb.append(" isTemporary: "); //NOI18N
325
sb.append(fe.isTemporary());
326     }
327
328     static void compToString(Component c, final StringBuffer JavaDoc sb) {
329         if (c == null) {
330             sb.append(" null "); //NOI18N
331

332             return;
333         }
334
335         String JavaDoc name = c.getName();
336         Class JavaDoc clazz = c.getClass();
337         String JavaDoc classname = clazz.getName();
338         int i = classname.lastIndexOf('.');
339
340         if ((i != -1) && (i != (classname.length() - 1))) {
341             classname = classname.substring(i + 1);
342         }
343
344         if (name != null) {
345             sb.append("\""); //NOI18N
346
sb.append(name);
347             sb.append("\" ("); //NOI18N
348
sb.append(classname);
349             sb.append(") "); //NOI18N
350
} else {
351             sb.append(' '); //NOI18N
352
sb.append(classname);
353             sb.append(' '); //NOI18N
354
}
355
356         if (!c.isVisible()) {
357             sb.append(" [NOT VISIBLE] "); //NOI18N
358
}
359
360         if (!c.isDisplayable()) {
361             sb.append(" [HAS NO PARENT COMPONENT] "); //NOI18N
362
}
363     }
364
365     public static void dumpStack(Class JavaDoc clazz) {
366         if (Logger.getLogger(clazz.getName()).isLoggable(Level.FINE)) {
367             StringWriter JavaDoc sw = new StringWriter JavaDoc();
368             new Throwable JavaDoc().printStackTrace(new PrintWriter JavaDoc(sw));
369             log(clazz, sw.getBuffer().toString());
370         }
371     }
372
373     /** Get the color for the custom editor button if specified by the theme or
374      * look and feel, or null if the defaults should simply be used */

375     static Color getButtonColor() {
376         return UIManager.getColor("netbeans.ps.buttonColor"); //NOI18N
377
}
378
379     /** Get the width required by the custom editor button (this varies with
380      * font size). */

381     static int getCustomButtonWidth() {
382         Icon ic = getCustomButtonIcon();
383
384         return ic.getIconWidth() + (isAqua ? 5 : 3);
385     }
386
387     /** Check the myriad ways in which a property may be non-editable */
388     static boolean checkEnabled(Component c, PropertyEditor editor, PropertyEnv env) {
389         if (editor instanceof NoPropertyEditorEditor) {
390             return false;
391         }
392
393         if (env != null) {
394             Boolean JavaDoc canEditAsText = (Boolean JavaDoc) env.getFeatureDescriptor().getValue("canEditAsText"); // NOI18N
395

396             if (!env.isEditable() || Boolean.FALSE.equals(canEditAsText)) {
397                 return false;
398             }
399         }
400
401         return true;
402     }
403
404     /** Get a scratch graphics object which can be used to calculate string
405      * widths offscreen */

406     static Graphics getScratchGraphics(Component c) {
407         if (scratchGraphics == null) {
408             scratchGraphics = new BufferedImage JavaDoc(1, 1, BufferedImage.TYPE_INT_RGB).getGraphics();
409         }
410
411         return scratchGraphics;
412     }
413
414     /** Get the color that should be used for text when an error or exception
415      * is encountered in displaying a value. Either the look and feel or
416      * theme can supply a color via the UIDefaults key nb.errorColor or
417      * a default (currently Color.RED) will be used */

418     static Color getErrorColor() {
419         //allow theme.xml to override error color
420
Color result = UIManager.getColor("nb.errorForeground"); //NOI18N
421

422         if (result == null) {
423             result = Color.RED;
424         }
425
426         return result;
427     }
428
429     /** Get the foreground color for text on disabled components */
430     static Color getDisabledForeground() {
431         if (disFg == null) {
432             disFg = UIManager.getColor("textInactiveText"); //NOI18N
433

434             if (disFg == null) {
435                 disFg = Color.GRAY;
436             }
437         }
438
439         return disFg;
440     }
441
442     /** Get a factor of the difference between the default font size NetBeans
443      * uses, and the actual font size which may be different if the -fontsize
444      * argument was used on startup. */

445     static float getFontSizeFactor() {
446         if (fsfactor == -1) {
447             Font f = UIManager.getFont("controlFont"); //NOI18N
448

449             if (f == null) {
450                 JLabel jl = new JLabel();
451                 f = jl.getFont();
452             }
453
454             int baseSize = 12; //default font size
455
fsfactor = baseSize / f.getSize();
456         }
457
458         return fsfactor;
459     }
460
461     /** Minimum width for an instance of PropPanel, based on the default
462      * font size */

463     static int getMinimumPropPanelWidth() {
464         if (minW == -1) {
465             int base = 50;
466             minW = Math.round(base * getFontSizeFactor());
467         }
468
469         return minW;
470     }
471
472     /** Minimum height for an instance of PropPanel based on the default
473      * font size */

474     static int getMinimumPropPanelHeight() {
475         if (minH == -1) {
476             int base = 18;
477             minH = Math.round(base * getFontSizeFactor());
478         }
479
480         return minH;
481     }
482
483     /** Minimum size for an instance of PropPanel based on the default
484      * font size */

485     static Dimension getMinimumPanelSize() {
486         return new Dimension(getMinimumPropPanelWidth(), getMinimumPropPanelHeight());
487     }
488
489     /** Update a property model with value provided by a property editor,
490      * showing a dialog if the value is invalid */

491     static boolean updateProp(PropertyModel mdl, PropertyEditor ed, String JavaDoc title) {
492         // System.err.println("UPDATEPROP(model=" + mdl + ", editor=" + ed + ", title=" + title);
493
Object JavaDoc newValue = ed.getValue();
494         Object JavaDoc o = noDlgUpdateProp(mdl, ed);
495
496         // System.err.println(" result of noDlgUpdatePRop:" + o);
497
if (o instanceof Exception JavaDoc) {
498             if( o instanceof InvocationTargetException JavaDoc )
499                 o = ((InvocationTargetException JavaDoc)o).getTargetException();
500             processThrowable((Exception JavaDoc) o, title, newValue);
501         }
502
503         boolean result = (o instanceof Boolean JavaDoc) ? ((Boolean JavaDoc) o).booleanValue() : false;
504
505         // System.err.println("RETURNING " + result);
506
return result;
507     }
508
509     /** Try to update a property model with the value held by a property editor.
510      * If any exceptions are thrown, return them - the invoker can choose to
511      * process them as it wishes, but it is preferable to return them unaltered
512      * rather than annotate using ErrorManager and force the client to fish
513      * through the annotations to find the exception of interest */

514     static Object JavaDoc noDlgUpdateProp(PropertyModel mdl, PropertyEditor ed) {
515         // System.err.println(" NO_DLG_UPDATE_PROP - editor value: " + ed.getValue() + " editor=" + ed + " model=" + mdl);
516
Object JavaDoc newValue = ed.getValue();
517         Object JavaDoc result = Boolean.FALSE;
518
519         try {
520             try {
521                 Object JavaDoc oldValue = mdl.getValue();
522                 
523                 int selBeans = 0;
524                 if( mdl instanceof NodePropertyModel ) {
525                     Object JavaDoc[] beans = ((NodePropertyModel)mdl).getBeans();
526                     if( null != beans )
527                         selBeans = beans.length;
528                 }
529
530                 // test if newValue is not equal to oldValue
531
if (((newValue != null) && !newValue.equals(oldValue))
532                         || ((newValue == null) && (oldValue != null || selBeans > 1))) {
533                     mdl.setValue(newValue);
534                     result = Boolean.TRUE;
535                 } else {
536                     //do nothing
537
}
538             } catch (ProxyNode.DifferentValuesException dve) {
539                 //issue 34982, failure setting value of JSP breakpoint,
540
//and probably other cases as well
541
mdl.setValue(newValue);
542                 result = Boolean.TRUE;
543             }
544         } catch (Exception JavaDoc e) {
545             result = e;
546         }
547
548         return result;
549     }
550
551     /** Try to update a property editor with a value object (presumably)
552      * provided by an InplaceEditor. If exceptions are thrown, returns them
553      * for the caller to do with as it wishes (for example a panel that
554      * commits multiple values from several editors at once will probably want to
555      * aggregate them into a single error message) */

556     static Exception JavaDoc updatePropertyEditor(PropertyEditor ed, Object JavaDoc value) {
557         // System.err.println("UpdatePropertyEditor " + ed + " to " + value );
558
Exception JavaDoc result = null;
559
560         try {
561             if (value instanceof String JavaDoc) {
562                 ed.setAsText((String JavaDoc) value);
563             } else {
564                 ed.setValue(value);
565             }
566         } catch (Exception JavaDoc e) {
567             result = e;
568         }
569
570         // System.err.println("returning " + result);
571
return result;
572     }
573
574     /** Update a property using the model and value cached by an inplace editor */
575     static boolean updateProp(InplaceEditor ine) {
576         // System.err.println("UPDATEPROP(inplaceEditor)");
577
Component c = ine.getComponent();
578         Cursor oldCursor = c.getCursor();
579
580         try {
581             c.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
582
583             Object JavaDoc o = ine.getValue();
584             Exception JavaDoc e = updatePropertyEditor(ine.getPropertyEditor(), o);
585
586             // System.err.println("UPDATE PROPERTY EDITOR RETURNED " + e);
587
String JavaDoc newValString = (o == null) ? NbBundle.getMessage(PropUtils.class, "NULL") : o.toString(); //NOI18N
588

589             if (e != null) {
590                 PropertyModel pm = ine.getPropertyModel();
591                 String JavaDoc propName;
592
593                 if (pm instanceof NodePropertyModel) {
594                     Node.Property p = ((NodePropertyModel) pm).getProperty();
595                     propName = p.getDisplayName();
596                 } else if (pm instanceof DefaultPropertyModel) {
597                     propName = ((DefaultPropertyModel) pm).propertyName;
598                 } else {
599                     //who knows what it is...
600
propName = NbBundle.getMessage(PropUtils.class, "MSG_unknown_property_name"); //NOI18N
601
}
602
603                 processThrowable(e, propName, newValString);
604             }
605
606             boolean result = (e == null)
607                 ? PropUtils.updateProp(ine.getPropertyModel(), ine.getPropertyEditor(), newValString) : false;
608
609             // System.err.println("Returning " + result);
610
return result;
611         } finally {
612             c.setCursor(oldCursor);
613         }
614     }
615
616     /** Processes <code>Throwable</code> thrown from <code>setAsText</code>
617      * or <code>setValue</code> call on <code>editor</code>. Helper method. */

618     private static void processThrowable(Throwable JavaDoc throwable, String JavaDoc title, Object JavaDoc newValue) {
619         //Copied from old PropertyPanel impl
620
if (throwable instanceof ThreadDeath JavaDoc) {
621             throw (ThreadDeath JavaDoc) throwable;
622         }
623
624         String JavaDoc locMsg = Exceptions.findLocalizedMessage(throwable);
625
626         if (locMsg != null
627             && (throwable.getLocalizedMessage() != throwable.getMessage())) { //XXX See issue 34569
628

629             String JavaDoc msg = MessageFormat.format(
630                     NbBundle.getMessage(PropUtils.class, "FMT_ErrorSettingProperty"), new Object JavaDoc[] { newValue, title }
631                 ); //NOI18N
632
UIException.annotateUser(throwable, msg,
633                                      throwable.getLocalizedMessage(), throwable,
634                                      new Date());
635         } else if (throwable instanceof NumberFormatException JavaDoc) {
636             //Handle NFE's from the core sun.beans property editors w/o raising stack traces
637
UIException.annotateUser(throwable, throwable.getMessage(),
638                                      MessageFormat.format(NbBundle.getMessage(PropUtils.class,
639                                                                               "FMT_BAD_NUMBER_FORMAT"),
640                                                           new Object JavaDoc[]{newValue}),
641                                      null, null);
642         }
643
644         Exceptions.printStackTrace(throwable);
645     }
646
647     /** Fetches a localized message for an exception that may be displayed to
648      * the user. If no localized message can be found in the annotations,
649      * it will provide a generic one.
650      * @see org.openide.explorer.propertysheet.PropertyDisplayer.Editable.isModifiedValueLegal */

651     static synchronized String JavaDoc findLocalizedMessage(Throwable JavaDoc throwable, Object JavaDoc newValue, String JavaDoc title) {
652         try {
653             if (throwable == null) {
654                 //need to catch this - for mysterious reasons, calling
655
//getLocalizedMessage on a null throwable hangs/or results in an
656
//endless loop - thread will stop doing anything unrecoverably
657
return null;
658             }
659
660             if (throwable.getLocalizedMessage() != throwable.getMessage()) {
661                 return throwable.getLocalizedMessage();
662             }
663
664             String JavaDoc msg = Exceptions.findLocalizedMessage(throwable);
665             if (msg != null) {
666                 return msg;
667             }
668
669             if (throwable instanceof NumberFormatException JavaDoc) {
670                 //Handle NFE's from the core sun.beans property editors w/o raising stack traces
671
return MessageFormat.format(
672                     NbBundle.getMessage(PropUtils.class, "FMT_BAD_NUMBER_FORMAT"), //NOI18N
673
new Object JavaDoc[] { newValue }
674                 );
675             }
676             //No localized message could be found, log the exception
677
//ErrorManager.getDefault().annotate(throwable, ErrorManager.WARNING, null, null, null, null);
678

679             //punt
680
return MessageFormat.format(
681                 NbBundle.getMessage(PropUtils.class, "FMT_CannotUpdateProperty"), new Object JavaDoc[] { newValue, title }
682             ); //NOI18N
683
} catch (Exception JavaDoc e) {
684             //We ABSOLUTELY cannot let this method throw exceptions or it will
685
//quietly endlessly
686
Exceptions.printStackTrace(e);
687
688             return null;
689         }
690     }
691
692     /** Utility method to fetch a comparator for properties
693      * based on sorting mode defined in PropertySheet. */

694     static Comparator<Property> getComparator(int sortingMode) {
695         switch (sortingMode) {
696         case PropertySheet.UNSORTED:
697             return null;
698
699         case PropertySheet.SORTED_BY_NAMES:
700             return SORTER_NAME;
701
702         case PropertySheet.SORTED_BY_TYPES:
703             return SORTER_TYPE;
704
705         default:
706             throw new IllegalArgumentException JavaDoc("Unknown sorting mode: " + Integer.toString(sortingMode)); //NOI18N
707
}
708     }
709
710     /** Create a ComboBoxUI that does not display borders on the combo box.
711      * Since the property sheet is rendered as a table, this looks better.
712      * This UI also delegates closing of its popup to the property sheet, which
713      * has better knowledge of when this is appropriate, in the case of focus
714      * changes. Inplace editors which employ a combo box should use the UI
715      * returned by this method, rather than the default for the look and feel.
716      * Thus the appearance will be consistent with the rest of the property
717      * sheet. */

718     public static javax.swing.plaf.ComboBoxUI JavaDoc createComboUI(JComboBox box, boolean tableUI) {
719         return new CleanComboUI(tableUI);
720     }
721
722     /** A convenience map for missing property editor classes, so users
723      * are only notified once, not every time a table cell is drawn.
724      */

725     private static java.util.List JavaDoc<String JavaDoc> getMissing() {
726         if (missing == null) {
727             missing = new ArrayList<String JavaDoc>();
728         }
729
730         return missing;
731     }
732
733     /**
734      * Gets a property editor appropriate to the class.
735      * First checks {@link PropertyEditorManager}.
736      * Also handles enum types, and has a fallback dummy editor.
737      */

738     static PropertyEditor getPropertyEditor(Class JavaDoc<?> c) {
739         PropertyEditor result = PropertyEditorManager.findEditor(c);
740         
741         if (result == null && Enum JavaDoc.class.isAssignableFrom(c)) {
742             // XXX should this rather be done in Node.getPropertyEditor?
743
result = new EnumPropertyEditor(c.asSubclass(Enum JavaDoc.class));
744         }
745
746         if (result == null) {
747             result = new NoPropertyEditorEditor();
748         }
749
750         return result;
751     }
752
753     /**
754      * Call getPropertyEditor(Node.Property, boolean) method with
755      * <code>updateEditor</code> as true.
756      *
757      * @see #getPropertyEditor(org.openide.nodes.Node.Property, boolean)
758      */

759     static PropertyEditor getPropertyEditor(Property p) {
760         return getPropertyEditor(p, true);
761     }
762
763     /** Gets a property editor appropriate to the property.
764      * This method centralizes all code for fetching property editors
765      * used by the property sheet, such that future alternative
766      * registration systems for property editors may be easily
767      * implemented.
768      * <P><strong>Note:</strong> This method will return a property
769      * editor with the value of the property already assigned. Client
770      * code need not set the value in the property editor unless it
771      * should be changed, as this can result in unnecessary event
772      * firing.
773      */

774     static PropertyEditor getPropertyEditor(Property p, boolean updateEditor) {
775         PropertyEditor result = p.getPropertyEditor();
776
777         //XXX Next few lines replicate a hack in the original property sheet.
778
//Correct solution is to move IndexedPropertyEditor & custom editor
779
//to the Nodes package and just return an instance from
780
//IndexedProperty.getPropertyEditor. Appears to be here due to some
781
//kind of dependancy avoidance.
782
if (p instanceof Node.IndexedProperty && (result == null)) {
783             result = new IndexedPropertyEditor();
784
785             // indexed property editor does not want to fire immediately
786
p.setValue(PropertyEnv.PROP_CHANGE_IMMEDIATE, Boolean.FALSE);
787         }
788
789         if (result == null) {
790             result = getPropertyEditor(p.getValueType()); //XXX is this agood idea?
791
}
792
793         //handle a type with no registered property editor here
794
if (result == null) {
795             java.util.List JavaDoc<String JavaDoc> missing = getMissing();
796             String JavaDoc type = p.getValueType().getName();
797
798             if (!(missing.contains(type))) {
799                 Logger.getAnonymousLogger().fine(
800                     "No property editor registered for type " + type
801                 ); //NOI18N
802
missing.add(type);
803             }
804
805             result = new NoPropertyEditorEditor();
806         } else if (p.canRead()) {
807             try {
808                 try {
809                     try {
810                         if (
811                             ((p.getValueType() == Boolean JavaDoc.class) || (p.getValueType() == Boolean.TYPE)) &&
812                                 (p.getValue() == null)
813                         ) {
814                             // Allows Module folder nodes that use null to
815
// indicate indeterminate state to work
816
result = new Boolean3WayEditor();
817                         }
818
819                         if (updateEditor) {
820                             updateEdFromProp(p, result, p.getDisplayName());
821                         }
822                     } catch (ProxyNode.DifferentValuesException dve) {
823                         if ((p.getValueType() == Boolean JavaDoc.class) || (p.getValueType() == Boolean.TYPE)) {
824                             result = new Boolean3WayEditor();
825                         } else {
826                              if(result instanceof ExPropertyEditor)
827                                  result = new ExDifferentValuesEditor(result);
828                              else
829                                  result = new DifferentValuesEditor(result);
830                         }
831                     }
832                 } catch (IllegalAccessException JavaDoc iae) {
833                     throw (IllegalStateException JavaDoc) new IllegalStateException JavaDoc("Error getting property value").initCause(iae);
834                 }
835             } catch (InvocationTargetException JavaDoc ite) {
836                 throw (IllegalStateException JavaDoc) new IllegalStateException JavaDoc("Error getting property value").initCause(ite);
837             }
838         }
839
840         return result;
841     }
842
843     /** Update a property editor from a Node.Property, such that the
844      * value in the property editor will reflect the value of the property */

845     private static void updateEdFromProp(Property p, PropertyEditor ed, String JavaDoc title)
846     throws ProxyNode.DifferentValuesException, IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
847         Object JavaDoc newValue = p.getValue();
848
849         //IZ 44152, monstrous length strings from the debugger - skip the
850
//equality test and blindly assign
851
if (newValue instanceof String JavaDoc && (((String JavaDoc) newValue).length() > 2048)) {
852             ed.setValue(newValue);
853
854             return;
855         }
856
857         Object JavaDoc oldValue = ed.getValue();
858
859         if ((newValue == null) && (oldValue == null)) {
860             return;
861         }
862
863         // test if newValue is not equal to oldValue
864
if (((newValue != null) && !newValue.equals(oldValue)) || ((newValue == null) && (oldValue != null))) {
865             // <RAVE>
866
// #5050567 Kind of mysterious livelock experienced here.
867
// When updating arrays.
868
// The question is whether the setter of the bean has to check if the value
869
// is equal and not fire property change.. or is if the responsibility
870
// is also here.
871
if (
872                 oldValue instanceof Object JavaDoc[] && newValue instanceof Object JavaDoc[] &&
873                     Arrays.equals((Object JavaDoc[]) oldValue, (Object JavaDoc[]) newValue)
874             ) {
875                 return;
876             }
877
878             // </RAVE>
879
ed.setValue(newValue);
880         }
881     }
882
883     /** Get the basic color for controls in the look and feel, or a reasonable
884      * default if the look and feel does not supply a value from
885      * UIManager.getColor(&quot;control&quot;) */

886     static Color getControlColor() {
887         if (controlColor == null) {
888             deriveColorsAndMargin();
889         }
890
891         return controlColor;
892     }
893
894     /** Get the shadow color specified by the look and feel, or a reasonable
895      * default if none was specified */

896     static Color getShadowColor() {
897         if (shadowColor == null) {
898             deriveColorsAndMargin();
899         }
900
901         return shadowColor;
902     }
903
904     /** Get the alternate background color, if any. If non-null, the table will
905      * show every other row using the color specified here */

906     static Color getAltBg() {
907         if (altBg == null) {
908             deriveColorsAndMargin();
909         }
910
911         return altBg;
912     }
913
914     /** Determine if an alternate background color has been specified in the
915      * look and feel or theme. If one is supplied, then the property sheet
916      * table will not show a grid, but instead, every other line will have
917      * a different background color. This method simply avoids repeated
918      * attempts to look up the alternate background color if none was specified */

919     static boolean noAltBg() {
920         if (noAltBg == null) {
921             noAltBg = (UIManager.getColor(KEY_ALTBG) == null) ? //NOI18N
922
Boolean.TRUE : Boolean.FALSE;
923         }
924
925         return noAltBg.booleanValue();
926     }
927
928     /** Get the forground color for text editable property editors in edit
929      * mode. This is required because the tree selection color is typically
930      * the same color as the tree selection color. So if a string editor is
931      * given the background color of a selected row in the table, it is
932      * impossible to tell when some text in the field has been selected,
933      * because they will be the same color. */

934     static Color getTextFieldBackground() {
935         if (tfBg == null) {
936             tfBg = UIManager.getColor("TextField.background"); //NOI18N
937

938             if (tfBg == null) {
939                 tfBg = UIManager.getColor("text"); //NOI18N
940
}
941
942             if (tfBg == null) {
943                 tfBg = Color.WHITE;
944             }
945         }
946
947         return tfBg;
948     }
949
950     /** Get the forground color for text editable property editors in edit
951      * mode. This is required because the tree selection color is typically
952      * the same color as the tree selection color. */

953     static Color getTextFieldForeground() {
954         if (tfFg == null) {
955             tfFg = UIManager.getColor("TextField.foreground"); //NOI18N
956

957             if (tfFg == null) {
958                 tfFg = UIManager.getColor("textText"); //NOI18N
959
}
960
961             if (tfFg == null) {
962                 tfFg = Color.BLACK;
963             }
964         }
965
966         return tfFg;
967     }
968
969     /** Initialize the various colors we will be using. */
970     private static void deriveColorsAndMargin() {
971         controlColor = UIManager.getColor("control"); //NOI18N
972

973         if (controlColor == null) {
974             controlColor = Color.LIGHT_GRAY;
975         }
976
977         int red;
978         int green;
979         int blue;
980
981         boolean windows = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel".equals(
982                 UIManager.getLookAndFeel().getClass().getName()
983             );
984
985         boolean aqua = "Aqua".equals(UIManager.getLookAndFeel().getID());
986
987         setRendererColor = UIManager.getColor(KEY_SETBG); //NOI18N
988
selectedSetRendererColor = UIManager.getColor(KEY_SELSETBG); //NOI18N
989

990         if (setRendererColor == null) {
991             if (aqua) {
992                 setRendererColor = new Color(225, 235, 240);
993             } else {
994                 if (setRendererColor == null) {
995                     red = adjustColorComponent(controlColor.getRed(), -25, -25);
996                     green = adjustColorComponent(controlColor.getGreen(), -25, -25);
997                     blue = adjustColorComponent(controlColor.getBlue(), -25, -25);
998                     setRendererColor = new Color(red, green, blue);
999                 }
1000            }
1001        }
1002
1003        if (aqua) {
1004            selectedSetRendererColor = UIManager.getColor("Table.selectionBackground");
1005        }
1006
1007        if (selectedSetRendererColor == null) {
1008            Color col = windows ? UIManager.getColor("Table.selectionBackground")
1009                                : UIManager.getColor("activeCaptionBorder"); //NOI18N
1010

1011            if (col == null) {
1012                col = Color.BLUE;
1013            }
1014
1015            red = adjustColorComponent(col.getRed(), -25, -25);
1016            green = adjustColorComponent(col.getGreen(), -25, -25);
1017            blue = adjustColorComponent(col.getBlue(), -25, -25);
1018            selectedSetRendererColor = new Color(red, green, blue);
1019        }
1020
1021        shadowColor = UIManager.getColor("controlShadow"); //NOI18N
1022

1023        if (shadowColor == null) {
1024            shadowColor = Color.GRAY;
1025        }
1026
1027        setForegroundColor = UIManager.getColor(KEY_SETFG);
1028
1029        if (setForegroundColor == null) {
1030            setForegroundColor = UIManager.getColor("Table.foreground"); //NOI18N
1031

1032            if (setForegroundColor == null) {
1033                setForegroundColor = UIManager.getColor("textText");
1034
1035                if (setForegroundColor == null) {
1036                    setForegroundColor = Color.BLACK;
1037                }
1038            }
1039        }
1040
1041        selectedSetForegroundColor = UIManager.getColor(KEY_SELSETFG);
1042
1043        if (selectedSetForegroundColor == null) {
1044            selectedSetForegroundColor = UIManager.getColor("Table.selectionForeground"); //NOI18N
1045

1046            if (selectedSetForegroundColor == null) {
1047                selectedSetForegroundColor = Color.WHITE;
1048            }
1049        }
1050
1051        altBg = UIManager.getColor(KEY_ALTBG); //NOI18N
1052

1053        if (altBg == null) {
1054            altBg = UIManager.getColor("Tree.background"); //NOI18N
1055

1056            if (altBg == null) {
1057                altBg = Color.WHITE;
1058            }
1059
1060            noAltBg = Boolean.TRUE;
1061        } else {
1062            noAltBg = Boolean.FALSE;
1063        }
1064
1065        Icon collapsedIcon = UIManager.getIcon("Tree.collapsedIcon"); //NOI18N
1066
Icon expandedIcon = UIManager.getIcon("Tree.expandedIcon"); //NOI18N
1067

1068        assert collapsedIcon != null: "no Tree.collapsedIcon found";
1069        assert expandedIcon != null: "no Tree.expandedIcon found";
1070
1071            
1072        int iconSize = collapsedIcon.getIconWidth();
1073        if (collapsedIcon != null) {
1074            marginWidth = Math.max(14, iconSize - 2);
1075        } else {
1076            //on the off chance a L&F doesn't provide this
1077
marginWidth = 13;
1078        }
1079
1080        Integer JavaDoc i = (Integer JavaDoc) UIManager.get(KEY_ICONMARGIN); //NOI18N
1081

1082        if (i != null) {
1083            iconMargin = i.intValue();
1084        } else {
1085            if (
1086                "com.sun.java.swing.plaf.windows.WindowsLookAndFeel".equals(
1087                        UIManager.getLookAndFeel().getClass().getName()
1088                    )
1089            ) {
1090                iconMargin = 4;
1091            } else {
1092                iconMargin = 0;
1093            }
1094        }
1095
1096        i = (Integer JavaDoc) UIManager.get(KEY_ROWHEIGHT); //NOI18N
1097

1098        if (i != null) {
1099            spinnerHeight = i.intValue();
1100        } else {
1101            spinnerHeight = iconSize;
1102        }
1103    }
1104
1105    /** Get the icon displayed by an expanded set. Typically this is just the
1106     * same icon the look and feel supplies for trees */

1107    static Icon getExpandedIcon() {
1108        Icon expandedIcon = UIManager.getIcon("Tree.expandedIcon"); //NOI18N
1109
assert expandedIcon != null: "no Tree.expandedIcon found";
1110        return expandedIcon;
1111    }
1112    
1113    /** Get the icon displayed by a collapsed set. Typically this is just the
1114     * icon the look and feel supplies for trees */

1115    static Icon getCollapsedIcon() {
1116        Icon collapsedIcon = UIManager.getIcon("Tree.collapsedIcon"); //NOI18N
1117
assert collapsedIcon != null: "no Tree.collapsedIcon found";
1118        return collapsedIcon;
1119    }
1120
1121    /** Get the color for expandable sets when they are not selected */
1122    static Color getSetRendererColor() {
1123        if (setRendererColor == null) {
1124            deriveColorsAndMargin();
1125        }
1126
1127        return setRendererColor;
1128    }
1129
1130    /** Get the background color for expandable sets when they are selected */
1131    static Color getSelectedSetRendererColor() {
1132        if (selectedSetRendererColor == null) {
1133            deriveColorsAndMargin();
1134        }
1135
1136        return selectedSetRendererColor;
1137    }
1138
1139    /** Get the color for expandable sets when not selected */
1140    static Color getSetForegroundColor() {
1141        if (setForegroundColor == null) {
1142            deriveColorsAndMargin();
1143        }
1144
1145        return setForegroundColor;
1146    }
1147
1148    /** Get the text color for expandable sets when they are selected */
1149    static Color getSelectedSetForegroundColor() {
1150        if (selectedSetForegroundColor == null) {
1151            deriveColorsAndMargin();
1152        }
1153
1154        return selectedSetForegroundColor;
1155    }
1156
1157    /** Get the total width that the left side margin should have. This is
1158     * calculated based on the width of the expandable set icon plus some
1159     * spacing */

1160    static int getMarginWidth() {
1161        if (marginWidth == -1) {
1162            deriveColorsAndMargin();
1163        }
1164
1165        return marginWidth;
1166    }
1167
1168    /** Get the height of the expansion icon */
1169    static int getSpinnerHeight() {
1170        if (spinnerHeight == -1) {
1171            deriveColorsAndMargin();
1172        }
1173
1174        return spinnerHeight;
1175    }
1176
1177    /** Get the number of pixels that should separate the expandable set
1178     * icon from the left edge of the property sheet. For Metal, this can
1179     * be zero; for the smaller icon supplied by Windows look and feels,
1180     * it should be a larger number */

1181    static int getIconMargin() {
1182        if (iconMargin == -1) {
1183            deriveColorsAndMargin();
1184        }
1185
1186        return iconMargin;
1187    }
1188
1189    /** Lazily creates the custom editor button icon */
1190    static Icon getCustomButtonIcon() {
1191        return new BpIcon();
1192    }
1193
1194    /** Adjust an rgb color component.
1195     * @param base the color, an RGB value 0-255
1196     * @param adjBright the amount to subtract if base > 128
1197     * @param adjDark the amount to add if base <=128 */

1198    private static int adjustColorComponent(int base, int adjBright, int adjDark) {
1199        if (base > 128) {
1200            base -= adjBright;
1201        } else {
1202            base += adjDark;
1203        }
1204
1205        if (base < 0) {
1206            base = 0;
1207        }
1208
1209        if (base > 255) {
1210            base = 255;
1211        }
1212
1213        return base;
1214    }
1215
1216    /** Get the localized name for the default category of properties - in
1217     * English this is &quot;Properties&quot;. Used to provide the basic category
1218     * and tab name */

1219    static String JavaDoc basicPropsTabName() {
1220        if (bptn == null) {
1221            bptn = NbBundle.getMessage(PropUtils.class, "LBL_BasicTab"); //NOI18N
1222
}
1223
1224        return bptn;
1225    }
1226
1227    static Comparator getTabListComparator() {
1228        if (comp == null) {
1229            comp = new TabListComparator();
1230        }
1231
1232        return comp;
1233    }
1234
1235    static SplitPaneUI JavaDoc createSplitPaneUI() {
1236        return new CleanSplitPaneUI();
1237    }
1238
1239    static boolean shouldShowDescription() {
1240        return preferences().getBoolean(PREF_KEY_SHOWDESCRIPTION, true);
1241    }
1242
1243    static void saveShowDescription(boolean b) {
1244        preferences().putBoolean(PREF_KEY_SHOWDESCRIPTION, b);
1245    }
1246
1247    static String JavaDoc[] getSavedClosedSetNames() {
1248        String JavaDoc s = preferences().get(PREF_KEY_CLOSEDSETNAMES, null);
1249
1250        if (s != null) {
1251            StringTokenizer tok = new StringTokenizer(s, ","); //NOI18N
1252
String JavaDoc[] result = new String JavaDoc[tok.countTokens()];
1253            int i = 0;
1254
1255            while (tok.hasMoreElements()) {
1256                result[i] = tok.nextToken();
1257                i++;
1258            }
1259
1260            return result;
1261        } else {
1262            return new String JavaDoc[0];
1263        }
1264    }
1265
1266    static void putSavedClosedSetNames(Set s) {
1267        if (s.size() > 0) {
1268            StringBuffer JavaDoc sb = new StringBuffer JavaDoc(s.size() * 20);
1269            Iterator i = s.iterator();
1270
1271            while (i.hasNext()) {
1272                sb.append(i.next());
1273
1274                if (i.hasNext()) {
1275                    sb.append(','); //NOI18N
1276
}
1277            }
1278
1279            preferences().put(PREF_KEY_CLOSEDSETNAMES, sb.toString());
1280        } else {
1281            preferences().put(PREF_KEY_CLOSEDSETNAMES, "");
1282        }
1283    }
1284
1285    static void putSortOrder(int i) {
1286        preferences().putInt(PREF_KEY_SORTORDER, i);
1287    }
1288
1289    static int getSavedSortOrder() {
1290        return preferences().getInt(PREF_KEY_SORTORDER, PropertySheet.UNSORTED);
1291    }
1292
1293    /** Fetch the margin that should come between the edge of a property sheet
1294     * cell and its text. The default is 2, or it can be customized via the
1295     * UIManager integer key netbeans.ps.textMargin */

1296    static int getTextMargin() {
1297        if ("apple.laf.AquaLookAndFeel".equals(UIManager.getLookAndFeel().getClass().getName())) {
1298            return 0;
1299        }
1300
1301        if (textMargin == -1) {
1302            Object JavaDoc o = UIManager.get("netbeans.ps.textMargin"); //NOI18N
1303

1304            if (o instanceof Integer JavaDoc) {
1305                textMargin = ((Integer JavaDoc) o).intValue();
1306            } else {
1307                textMargin = 2;
1308            }
1309        }
1310
1311        return textMargin;
1312    }
1313
1314    /** HTML-ize a tooltip, splitting long lines */
1315    static String JavaDoc createHtmlTooltip(String JavaDoc title, String JavaDoc s) {
1316        boolean wasHtml = false;
1317
1318        if (s.matches("\\<(html|HTML)\\>.*\\<\\/(html|HTML)\\>")) { // NOI18N
1319
s = s.replaceAll("\\<\\/{0,1}(html|HTML)\\>", ""); // NOI18N
1320
wasHtml = true;
1321        }
1322
1323        // break up massive tooltips
1324
String JavaDoc token = null;
1325
1326        if (s.indexOf(" ") != -1) { //NOI18N
1327
token = " "; //NOI18N
1328
} else if (s.indexOf(",") != -1) { //NOI18N
1329
token = ","; //NOI18N
1330
} else if (s.indexOf(";") != -1) { //NOI18N
1331
token = ";"; //NOI18N
1332
} else if (s.indexOf("/") != -1) { //NOI18N
1333
token = "/"; //NOI18N
1334
} else if (s.indexOf("\\") != -1) { //NOI18N
1335
token = "\\"; //NOI18N
1336
} else {
1337            //give up
1338
return s;
1339        }
1340
1341        StringTokenizer tk = new StringTokenizer(s, token, true);
1342
1343        StringBuffer JavaDoc sb = new StringBuffer JavaDoc(s.length() + 20);
1344        sb.append("<html>"); //NOI18N
1345
sb.append("<b><u>"); //NOI18N
1346
sb.append(title);
1347        sb.append("</u></b><br>"); //NOI18N
1348

1349        int charCount = 0;
1350        int lineCount = 0;
1351
1352        while (tk.hasMoreTokens()) {
1353            String JavaDoc a = tk.nextToken();
1354
1355            if (!wasHtml) {
1356                // HTML-ize only non-html values. HTML values should already
1357
// contain correct HTML strings.
1358
a = Utilities.replaceString(a, "&", "&amp;"); //NOI18N
1359
a = Utilities.replaceString(a, "<", "&lt;"); //NOI18N
1360
a = Utilities.replaceString(a, ">", "&gt;"); //NOI18N
1361
}
1362
1363            charCount += a.length();
1364            sb.append(a);
1365
1366            if (tk.hasMoreTokens()) {
1367                charCount++;
1368            }
1369
1370            if (charCount > 80) {
1371                sb.append("<br>"); //NOI18N
1372
charCount = 0;
1373                lineCount++;
1374
1375                if (lineCount > 10) {
1376                    //Don't let things like VCS variables create
1377
//a tooltip bigger than the screen. 99% of the
1378
//time this is not a problem.
1379
sb.append(NbBundle.getMessage(PropUtils.class, "MSG_ELLIPSIS")); //NOI18N
1380

1381                    return sb.toString();
1382                }
1383            }
1384        }
1385
1386        sb.append("</html>"); //NOI18N
1387

1388        return sb.toString();
1389    }
1390
1391    /** ButtonPanel and IconPanel implement inplace editor and proxy the
1392     * inner component that is the thing the user actually interacts with.
1393     * This method will locate the inner component of interest */

1394    static InplaceEditor findInnermostInplaceEditor(InplaceEditor ine) {
1395        while (ine instanceof IconPanel || ine instanceof ButtonPanel) {
1396            if (ine instanceof IconPanel) {
1397                ine = ((IconPanel) ine).getInplaceEditor();
1398            } else {
1399                ine = ((ButtonPanel) ine).getInplaceEditor();
1400            }
1401        }
1402
1403        return ine;
1404    }
1405
1406    /** Utility method to determine if the left edge margin should
1407     * be painted. This is determined by whether the property sheet
1408     * items are sorted by name or natural sort, or can be overridden
1409     * globally with a startup flag */

1410    static boolean shouldDrawMargin(PropertySetModel psm) {
1411        if (neverMargin) {
1412            return false;
1413        }
1414
1415        //hide margin and expansion handle if only one property
1416
//set, if flag is set (there is disagreement about the
1417
//right thing to do here, so it's an option for now)
1418
int setCount = psm.getSetCount();
1419
1420        if (psm.getComparator() != null) {
1421            return false;
1422        }
1423
1424        boolean includeMargin = (setCount > 1) || (!((setCount == 1) && hideSingleExpansion));
1425
1426        return includeMargin;
1427    }
1428
1429    /** Returns a fixed foreground color for the custom button icon on win xp.
1430     * On win xp, the button background is a white bitmap, so if we use the
1431     * white foreground color for the cell, the ... in the icon seems to
1432     * disappear when a row with a custom editor button is selected.
1433     * @see org.netbeans.core.NbTheme.installCustomDefaultsWinXP */

1434    private static final Color getIconForeground() {
1435        return UIManager.getColor("PropSheet.customButtonForeground"); //NOI18N
1436
}
1437
1438    public static boolean isXPTheme() {
1439        Boolean JavaDoc isXP = (Boolean JavaDoc) Toolkit.getDefaultToolkit().getDesktopProperty("win.xpstyle.themeActive");
1440
1441        return (isXP == null) ? false : isXP.booleanValue();
1442    }
1443
1444    /**
1445     * Just a helper method which delegates to shallBeRDVEnabled(Node.Property).
1446     */

1447    static final boolean shallBeRDVEnabled(FeatureDescriptor fd) {
1448        if ((fd != null) && fd instanceof Node.Property) {
1449            return shallBeRDVEnabled((Node.Property) fd);
1450        }
1451
1452        return false;
1453    }
1454
1455    /**
1456     * Returns if the "Restore Default Value" action should be enabled for the
1457     * given <code>Node.Property</code>. If the properties is not null, is
1458     * instanceof Node.Property, supportsDefaultValue() and isDefaultValue()
1459     * returns true the property shall be enabled. Otherwise not.
1460     * <p>
1461     * Note that due to the backward compatibilty we return true also for
1462     * properties which only override Node.Property.supportsDefaultValue() to
1463     * return true and don't override Node.Property.isDefaultValue(). The
1464     * isDefaultValue() return false by default.<br>
1465     * For more information and detailed reason why we do so see
1466     * <a HREF="http://www.netbeans.org/issues/show_bug.cgi?id=51907">
1467     * Issue 51907</a>.
1468     */

1469    static final boolean shallBeRDVEnabled(Node.Property property) {
1470        if ((property == null) || !property.supportsDefaultValue()) {
1471            return false;
1472        }
1473
1474        try {
1475            if (property.getClass().getMethod("isDefaultValue").getDeclaringClass() == Node.Property.class) {
1476                // if the given property didn't override isDefaultValue method
1477
// but override supportsDefaultValue the RDV should be always
1478
// enabled
1479
return true;
1480            } else {
1481                return !property.isDefaultValue();
1482            }
1483        } catch (NoSuchMethodException JavaDoc e) {
1484            // cannot happen since isDefaultValue is defined in Node.Property
1485
assert false : "No isDefaultValue in " + property.getClass() + ": " + e;
1486
1487            return true; // satisfy compiler for case when assertion is disabled
1488
}
1489    }
1490
1491    static void addExternallyEdited(Property p) {
1492        externallyEdited.add(p);
1493    }
1494
1495    static void removeExternallyEdited(Property p) {
1496        externallyEdited.remove(p);
1497    }
1498
1499    static boolean isExternallyEdited(Property p) {
1500        return externallyEdited.contains(p);
1501    }
1502
1503    /** Property editor for properties which belong to more than one property, but have
1504     * different values. */

1505    static class DifferentValuesEditor implements PropertyEditor {
1506        protected PropertyEditor ed;
1507        private boolean notSet = true;
1508
1509        public DifferentValuesEditor(PropertyEditor ed) {
1510            this.ed = ed;
1511            addPropertyChangeListener(new PropertyChangeListener() {
1512                public void propertyChange(PropertyChangeEvent evt){
1513                   notSet=false;
1514                }
1515            });
1516        }
1517
1518        public void addPropertyChangeListener(PropertyChangeListener listener) {
1519            ed.addPropertyChangeListener(listener);
1520        }
1521
1522        public String JavaDoc getAsText() {
1523            String JavaDoc result;
1524
1525            if (notSet) {
1526                result = NbBundle.getMessage(PropUtils.class, "CTL_Different_Values"); //NOI18N
1527
} else {
1528                result = ed.getAsText();
1529            }
1530
1531            return result;
1532        }
1533
1534        public java.awt.Component JavaDoc getCustomEditor() {
1535            return ed.getCustomEditor();
1536        }
1537
1538        public String JavaDoc getJavaInitializationString() {
1539            return ed.getJavaInitializationString();
1540        }
1541
1542        public String JavaDoc[] getTags() {
1543            return ed.getTags();
1544        }
1545
1546        public Object JavaDoc getValue() {
1547            Object JavaDoc result;
1548
1549            if (notSet) {
1550                result = null;
1551            } else {
1552                result = ed.getValue();
1553            }
1554
1555            return result;
1556        }
1557
1558        public boolean isPaintable() {
1559            return notSet ? false : ed.isPaintable();
1560        }
1561
1562        public void paintValue(java.awt.Graphics JavaDoc gfx, java.awt.Rectangle JavaDoc box) {
1563            //issue 33341 - don't allow editors to paint if value not set
1564
if (isPaintable()) {
1565                ed.paintValue(gfx, box);
1566            }
1567        }
1568
1569        public void removePropertyChangeListener(PropertyChangeListener listener) {
1570            ed.removePropertyChangeListener(listener);
1571        }
1572
1573        public void setAsText(String JavaDoc text) throws java.lang.IllegalArgumentException JavaDoc {
1574            ed.setAsText(text);
1575            notSet = false;
1576        }
1577
1578        public void setValue(Object JavaDoc value) {
1579            ed.setValue(value);
1580            notSet = false;
1581        }
1582
1583        public boolean supportsCustomEditor() {
1584            return ed.supportsCustomEditor();
1585        }
1586    }
1587
1588    /**
1589      * Extended Property editor for properties which belong to more than one property, but have
1590      * different values.
1591      */

1592    static final class ExDifferentValuesEditor extends DifferentValuesEditor implements ExPropertyEditor{
1593 
1594        public ExDifferentValuesEditor(PropertyEditor ed){
1595            super(ed);
1596        }
1597
1598        public void attachEnv(PropertyEnv env){
1599            ((ExPropertyEditor)ed).attachEnv(env);
1600        }
1601    }
1602 
1603        
1604    /** Dummy property editor for properties which have no real editor.
1605     * The property sheet does not handle null property editors; this editor
1606     * stands in, and returns &quot;No property editor&quot; from getAsText() */

1607    static final class NoPropertyEditorEditor implements PropertyEditor {
1608        public void addPropertyChangeListener(PropertyChangeListener listener) {
1609            //no-op
1610
}
1611
1612        public String JavaDoc getAsText() {
1613            return NbBundle.getMessage(PropertySheet.class, "CTL_NoPropertyEditor"); //NOI18N
1614
}
1615
1616        public java.awt.Component JavaDoc getCustomEditor() {
1617            return null;
1618        }
1619
1620        public String JavaDoc getJavaInitializationString() {
1621            return "";
1622        }
1623
1624        public String JavaDoc[] getTags() {
1625            return null;
1626        }
1627
1628        public Object JavaDoc getValue() {
1629            return getAsText();
1630        }
1631
1632        public boolean isPaintable() {
1633            return false;
1634        }
1635
1636        public void paintValue(java.awt.Graphics JavaDoc gfx, java.awt.Rectangle JavaDoc box) {
1637            //no-op
1638
}
1639
1640        public void removePropertyChangeListener(PropertyChangeListener listener) {
1641            //no-op
1642
}
1643
1644        public void setAsText(String JavaDoc text) throws java.lang.IllegalArgumentException JavaDoc {
1645            //no-op
1646
}
1647
1648        public void setValue(Object JavaDoc value) {
1649            //no-op
1650
}
1651
1652        public boolean supportsCustomEditor() {
1653            return false;
1654        }
1655    }
1656
1657    /** Comparator for sorting the list of tabs such that basic properties
1658     * is always the first tab. */

1659    private static class TabListComparator implements Comparator {
1660        public int compare(Object JavaDoc o1, Object JavaDoc o2) {
1661            String JavaDoc s1 = (String JavaDoc) o1;
1662            String JavaDoc s2 = (String JavaDoc) o2;
1663
1664            if (s1 == s2) {
1665                return 0;
1666            }
1667
1668            String JavaDoc bn = basicPropsTabName();
1669
1670            if (bn.equals(s1)) {
1671                return -1;
1672            }
1673
1674            if (bn.equals(s2)) {
1675                return 1;
1676            }
1677
1678            return s1.compareTo(s2);
1679        }
1680    }
1681
1682    private static class CleanSplitPaneUI extends BasicSplitPaneUI JavaDoc {
1683        protected void installDefaults() {
1684            super.installDefaults();
1685            divider.setBorder(new SplitBorder());
1686        }
1687        
1688        public BasicSplitPaneDivider JavaDoc createDefaultDivider() {
1689            return new CleanSplitPaneDivider(this);
1690        }
1691    }
1692    
1693    private static class CleanSplitPaneDivider extends BasicSplitPaneDivider JavaDoc implements Accessible JavaDoc {
1694        private AccessibleContext JavaDoc accessibleContext;
1695        
1696        public CleanSplitPaneDivider( BasicSplitPaneUI JavaDoc ui ) {
1697            super( ui );
1698        }
1699        public AccessibleContext JavaDoc getAccessibleContext() {
1700            if( null == accessibleContext ) {
1701                accessibleContext = new AccessibleAWTComponent() {
1702                            public AccessibleRole JavaDoc getAccessibleRole() {
1703                                return AccessibleRole.SPLIT_PANE;
1704                            }
1705                        };
1706
1707                accessibleContext.setAccessibleName( NbBundle.getMessage(DescriptionComponent.class, "ACS_Splitter") );
1708                accessibleContext.setAccessibleDescription( NbBundle.getMessage(DescriptionComponent.class, "ACSD_Splitter") );
1709            }
1710            return accessibleContext;
1711        }
1712    }
1713
1714    /** A split border subclass that does not draw the metal drag texture */
1715    private static class SplitBorder implements Border JavaDoc {
1716        public Insets getBorderInsets(Component c) {
1717            if (UIManager.getLookAndFeel() instanceof MetalLookAndFeel) {
1718                return new Insets(2, 0, 1, 0);
1719            } else {
1720                return new Insets(1, 0, 1, 0);
1721            }
1722        }
1723
1724        public boolean isBorderOpaque() {
1725            return true;
1726        }
1727
1728        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
1729            if (UIManager.getLookAndFeel() instanceof MetalLookAndFeel) {
1730                g.setColor(UIManager.getColor("controlShadow"));
1731                g.drawLine(x, y, x + width, y);
1732                g.setColor(UIManager.getColor("controlHighlight"));
1733                g.drawLine(x, y + 1, x + width, y + 1);
1734                g.drawLine(x, (y + height) - 1, x + width, (y + height) - 1);
1735                g.setColor(UIManager.getColor("controlShadow"));
1736                g.drawLine(x, (y + height) - 2, x + width, (y + height) - 2);
1737            } else {
1738                //don't do flush 3d for non-metal L&F
1739
g.setColor(UIManager.getColor("controlHighlight"));
1740                g.drawLine(x, y, x + width, y);
1741                g.setColor(UIManager.getColor("controlShadow"));
1742                g.drawLine(x, (y + height) - 1, x + width, (y + height) - 1);
1743            }
1744        }
1745    }
1746
1747    /** The ... icon for the custom editor button. Will draw slightly
1748     * larger if the font size is greater */

1749    static class BpIcon implements Icon {
1750        boolean larger;
1751
1752        public BpIcon() {
1753            Font f = UIManager.getFont("Table.font"); //NOI18N
1754
larger = (f != null) ? (f.getSize() > 13) : false;
1755        }
1756
1757        public int getIconHeight() {
1758            return 12;
1759        }
1760
1761        public int getIconWidth() {
1762            return larger ? 16 : 12;
1763        }
1764
1765        public void paintIcon(Component c, Graphics g, int x, int y) {
1766            int w = c.getWidth();
1767            int h = c.getHeight();
1768            int ybase = h - 5;
1769            int pos2 = (w / 2);
1770            int pos1 = pos2 - 4;
1771            int pos3 = pos2 + 4;
1772            g.setColor((getIconForeground() == null) ? c.getForeground() : getIconForeground());
1773            drawDot(g, pos1 + 1, ybase, larger);
1774            drawDot(g, pos2, ybase, larger);
1775            drawDot(g, pos3 - 1, ybase, larger);
1776        }
1777
1778        private void drawDot(Graphics g, int x, int y, boolean larger) {
1779            if (!larger) {
1780                g.drawLine(x, y, x, y);
1781            } else {
1782                g.drawLine(x - 1, y, x + 1, y);
1783                g.drawLine(x, y - 1, x, y + 1);
1784            }
1785        }
1786    }
1787
1788}
1789
Popular Tags