HtmlRenderer
. If the underlying
+ * FileSystem.Status
is an instance of HmlStatus,
+ * this method will return non-null if status information is added.
+ *
+ * @return a string containing compliant HTML markup or null
+ * @see org.openide.awt.HtmlRenderer
+ * @see org.openide.nodes.Node.getHtmlDisplayName */
+ public String getHtmlDisplayName() {
+ try {
+ FileSystem.Status stat =
+ obj.getPrimaryFile().getFileSystem().getStatus();
+ if (stat instanceof HtmlStatus) {
+ HtmlStatus hstat = (HtmlStatus) stat;
+
+ String result = hstat.annotateNameHtml (
+ super.getDisplayName(), new LazyFilesSet());
+
+ //Make sure the super string was really modified
+ if (!super.getDisplayName().equals(result)) {
+ return result;
+ }
+ }
+ } catch (FileStateInvalidException e) {
+ //do nothing and fall through
+ }
+ return super.getHtmlDisplayName();
+ }
/** Get the displayed icon for this node.
* A filesystem may {@link org.openide.filesystems.FileSystem#getStatus specially alter} this.
Index: openide/masterfs/src/org/netbeans/modules/masterfs/MasterFileSystem.java
===================================================================
RCS file: /cvs/openide/masterfs/src/org/netbeans/modules/masterfs/MasterFileSystem.java,v
retrieving revision 1.8
diff -u -u -r1.8 MasterFileSystem.java
--- openide/masterfs/src/org/netbeans/modules/masterfs/MasterFileSystem.java 26 Apr 2004 07:51:39 -0000 1.8
+++ openide/masterfs/src/org/netbeans/modules/masterfs/MasterFileSystem.java 28 Apr 2004 17:20:03 -0000
@@ -249,7 +249,7 @@
}
- private static final class StatusImpl implements FileSystem.Status {
+ private static final class StatusImpl implements FileSystem.HtmlStatus {
public Image annotateIcon(Image icon, int iconType, Set files) {
//int size = files.size();
Set transformedSet = new HashSet();
@@ -280,6 +280,18 @@
name = (fs != null) ? fs.getStatus().annotateName(name, transformedSet) : name;
}
return name;
+ }
+
+ public String annotateNameHtml(String name, Set files) {
+ Set transformedSet = new HashSet();
+ MasterFileObject hfo = Utils.transformSet(files, transformedSet);
+ if (hfo != null) {
+ FileSystem fs = hfo.getDelegateFileSystem();
+ if (fs != null && fs.getStatus() instanceof FileSystem.HtmlStatus) {
+ return ((FileSystem.HtmlStatus) fs.getStatus()).annotateNameHtml(name, transformedSet);
+ }
+ }
+ return null;
}
}
}
Index: openide/src/org/openide/awt/HtmlLabelUI.java
===================================================================
RCS file: openide/src/org/openide/awt/HtmlLabelUI.java
diff -N openide/src/org/openide/awt/HtmlLabelUI.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/src/org/openide/awt/HtmlLabelUI.java 28 Apr 2004 17:20:03 -0000
@@ -0,0 +1,368 @@
+/*
+ * Sun Public License Notice
+ *
+ * The contents of this file are subject to the Sun Public License
+ * Version 1.0 (the "License"). You may not use this file except in
+ * compliance with the License. A copy of the License is available at
+ * http://www.sun.com/
+ *
+ * The Original Code is NetBeans. The Initial Developer of the Original
+ * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
+ * Microsystems, Inc. All Rights Reserved.
+ */
+package org.openide.awt;
+
+import org.openide.ErrorManager;
+import org.openide.awt.HtmlRenderer;
+
+import javax.swing.*;
+import javax.swing.plaf.LabelUI;
+import javax.swing.plaf.ComponentUI;
+import java.awt.*;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * A LabelUI which uses the lightweight HTML renderer. Stateless - only one instance should ever exist.
+ */
+public class HtmlLabelUI extends LabelUI {
+ /** System property to automatically turn on antialiasing for html strings */
+ private static final boolean antialias = Boolean.getBoolean(
+ "nb.cellrenderer.antialiasing"); //NOI18N
+
+ private static HtmlLabelUI uiInstance = null;
+
+ public static ComponentUI createUI (JComponent c) {
+ assert c instanceof HtmlRendererImpl;
+ if (uiInstance == null) {
+ uiInstance = new HtmlLabelUI();
+ }
+ return uiInstance;
+ }
+
+ public Dimension getPreferredSize(JComponent c) {
+ return calcPreferredSize ((HtmlRendererImpl) c);
+ }
+
+ /** Get the width of the text */
+ private static int textWidth(String text, Graphics g, Font f, boolean html) {
+ if (text != null) {
+ if (html) {
+ return Math.round(Math.round(HtmlRenderer.renderHTML(text, g, 0, 0,
+ Integer.MAX_VALUE, Integer.MAX_VALUE, f,
+ Color.BLACK, HtmlRenderer.STYLE_TRUNCATE, false)));
+ } else {
+ return Math.round(Math.round(HtmlRenderer.renderPlainString(text, g, 0, 0,
+ Integer.MAX_VALUE, Integer.MAX_VALUE, f,
+ Color.BLACK, HtmlRenderer.STYLE_TRUNCATE, false)));
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ private Dimension calcPreferredSize(HtmlRendererImpl r) {
+ Insets ins = r.getInsets();
+ Dimension prefSize = new java.awt.Dimension(ins.left + ins.right, ins.top + ins.bottom);
+ String text = r.getText();
+
+ Graphics g = r.getGraphics();
+ Icon icon = r.getIcon();
+
+ if (text != null) {
+ FontMetrics fm = g.getFontMetrics (r.getFont());
+ prefSize.height += fm.getMaxAscent() + fm.getMaxDescent();
+ }
+
+ if (icon != null) {
+ if (r.isCentered()) {
+ prefSize.height += icon.getIconHeight() + r.getIconTextGap();
+ prefSize.width += icon.getIconWidth();
+ } else {
+ prefSize.height = Math.max (icon.getIconHeight(), prefSize.height);
+ prefSize.width += icon.getIconWidth() + r.getIconTextGap();
+ }
+ }
+
+ //Antialiasing affects the text metrics, so use it if needed when
+ //calculating preferred size or the result here will be narrower
+ //than the space actually needed
+ if (antialias) {
+ //For L&Fs such as Aqua and SmoothMetal, we will need to manually apply
+ //rendering hints to get antialiasing, since we're doing our
+ //own painting logic - they don't do this for things they don't
+ //know about
+ ((Graphics2D)g).addRenderingHints(getHints());
+ }
+
+ int textwidth = textWidth(text, g, r.getFont(), r.isHtml()) + 2;
+ if (r.isCentered()) {
+ prefSize.width = Math.max (prefSize.width, textwidth + ins.right + ins.left);
+ } else {
+ prefSize.width += textwidth + r.getIndent();
+ }
+ return prefSize;
+ }
+
+ private static Map hintsMap = null;
+ static final Map getHints() {
+ if (hintsMap == null) {
+ hintsMap = new HashMap();
+ hintsMap.put(RenderingHints.KEY_FRACTIONALMETRICS,
+ RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+ hintsMap.put(RenderingHints.KEY_TEXT_ANTIALIASING,
+ RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+ hintsMap.put(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ }
+ return hintsMap;
+ }
+
+
+ public void update(Graphics g, JComponent c) {
+ Color bg = getBackgroundFor ((HtmlRendererImpl) c);
+ if (bg != null) {
+ g.setColor(bg);
+ g.fillRect(0, 0, c.getWidth(),c.getHeight());
+ if (((HtmlRendererImpl) c).isLeadSelection()) {
+ Color focus = UIManager.getColor("textHighlight");
+ if (focus == null) {
+ focus = Color.BLUE;
+ }
+ g.setColor(focus);
+ g.drawRect (0, 0, c.getWidth()-1, c.getHeight()-1);
+ }
+ }
+ paint(g, c);
+ }
+
+ public void paint(Graphics g, JComponent c) {
+ if (antialias) {
+ //For L&Fs such as Aqua and SmoothMetal, we will need to manually apply
+ //rendering hints to get antialiasing, since we're doing our
+ //own painting logic - they don't do this for things they don't
+ //know about
+ ((Graphics2D)g).addRenderingHints(getHints());
+ }
+ HtmlRendererImpl r = (HtmlRendererImpl) c;
+ if (r.isCentered()) {
+ paintIconAndTextCentered (g, r);
+ } else {
+ paintIconAndText (g, r);
+ }
+ }
+
+ /** Actually paint the icon and text using our own html rendering engine. */
+ private void paintIconAndText(Graphics g, HtmlRendererImpl r) {
+ assert SwingUtilities.isEventDispatchThread();
+
+ Font f = r.getFont();
+ g.setFont(f);
+ FontMetrics fm = g.getFontMetrics();
+
+ //Find out what height we need
+ int txtH = fm.getHeight();
+ Insets ins = r.getInsets();
+
+ //find out the available height less the insets
+ int availH = r.getHeight() - (ins.top + ins.bottom);
+ int txtY;
+ if (availH >= txtH) {
+ //Center the text if we have space
+ txtY = txtH + ins.top + ((availH / 2) - (txtH / 2)) - 4;
+ } else {
+ //Okay, it's not going to fit, punt.
+ txtY = 0;//txtH + ins.top - 1;
+ }
+ int txtX = r.getIndent();
+
+ Icon icon = r.getIcon();
+ //Check the icon non-null and height (see TabData.NO_ICON for why)
+ if (icon != null && icon.getIconWidth() > 0 && icon.getIconHeight() > 0) {
+ int iconY;
+ if (availH > icon.getIconHeight()) {
+ //add 2 to make sure icon top pixels are not cut off by outline
+ iconY = ins.top + ((availH / 2) - (icon.getIconHeight() / 2));// + 2;
+ } else if (availH == icon.getIconHeight()){
+ //They're an exact match, make it 0
+ iconY = 0;
+ } else {
+ //Won't fit; make the top visible and cut the rest off (option:
+ //center it and clip it on top and bottom - probably even harder
+ //to recognize that way, though)
+ iconY = ins.top;
+ }
+ //add in the insets
+ int iconX = ins.left + r.getIndent();
+ try {
+ //Diagnostic - the CPP module currently is constructing
+ //some ImageIcon from a null image in Options. So, catch it and at
+ //least give a meaningful message that indicates what node
+ //is the culprit
+ icon.paintIcon(r, g, iconX, iconY);
+
+ } catch (NullPointerException npe) {
+ ErrorManager.getDefault().annotate(npe, ErrorManager.EXCEPTION,
+ "Probably an ImageIcon with a null source image: " +
+ r.getText(), null, null, null); //NOI18N
+ ErrorManager.getDefault().notify(npe);
+ }
+ txtX = iconX + icon.getIconWidth() + r.getIconTextGap();
+ } else {
+ //If there's no icon, paint the text where the icon would start
+ txtX += ins.left;
+ }
+
+ String text = r.getText();
+ if (text == null) {
+ //No text, we're done
+ return;
+ }
+
+ //Get the available horizontal pixels for text
+ int txtW = icon != null ? r.getWidth() - (ins.left + ins.right +
+ icon.getIconWidth() + r.getIconTextGap() + r.getIndent()) : r.getWidth() -
+ (ins.left + ins.right + r.getIndent());
+
+ Color foreground = getForegroundFor (r);
+
+ if (r.isHtml()) {
+ HtmlRenderer.renderHTML(text, g, txtX, txtY, txtW, txtH, f,
+ foreground, r.getRenderStyle(), true);
+ } else {
+ HtmlRenderer.renderString(text, g, txtX, txtY, txtW, txtH, f,
+ foreground, r.getRenderStyle(), true);
+ }
+ }
+
+ private void paintIconAndTextCentered (Graphics g, HtmlRendererImpl r) {
+ Insets ins = r.getInsets();
+ Icon ic = r.getIcon();
+ int w = r.getWidth() - (ins.left + ins.right);
+ int txtX = ins.left;
+ int txtY = 0;
+ if (ic != null && ic.getIconWidth() > 0 && ic.getIconHeight() > 0) {
+ int iconx = w > ic.getIconWidth() ? (w / 2) - (ic.getIconWidth() / 2) : txtX;
+ int icony = 0;
+ ic.paintIcon(r, g, iconx, icony);
+ txtY += ic.getIconHeight() + r.getIconTextGap();
+ }
+ int txtW = r.getPreferredSize().width;
+ txtX = txtW < r.getWidth() ? (r.getWidth() / 2) - (txtW / 2) : 0;
+ int txtH = r.getHeight() - txtY;
+
+ Font f = r.getFont();
+ g.setFont(f);
+ FontMetrics fm = g.getFontMetrics(f);
+ txtY += fm.getMaxAscent();
+
+ Color foreground = getForegroundFor (r);
+ if (r.isHtml()) {
+ HtmlRenderer.renderHTML(r.getText(), g, txtX, txtY, txtW, txtH,f,
+ foreground, r.getRenderStyle(), true);
+ } else {
+ HtmlRenderer.renderString(r.getText(), g, txtX, txtY, txtW, txtH, r.getFont(),
+ foreground, r.getRenderStyle(), true);
+ }
+ }
+
+ static Color getBackgroundFor (HtmlRendererImpl r) {
+ if (r.isOpaque()) {
+ return r.getBackground();
+ }
+
+ if (r.isSelected() && !r.isParentFocused() && !isGTK()) {
+ return getUnfocusedSelectionBackground();
+ }
+
+ if (isGTK()) {
+ //GTK does its own thing, we'll only screw it up by painting the background ourselves
+ return null;
+ }
+
+ Color result = null;
+
+ if (r.isSelected()) {
+ switch (r.getType()) {
+ case HtmlRendererImpl.TYPE_LIST :
+ result = UIManager.getColor ("List.selectionBackground"); //NOI18N
+ break;
+ case HtmlRendererImpl.TYPE_TABLE :
+ result = UIManager.getColor ("Table.selectionBackground"); //NOI18N
+ break;
+ case HtmlRendererImpl.TYPE_TREE :
+ return UIManager.getColor ("Tree.selectionBackground"); //NOI18N
+ }
+ return result == null ? r.getBackground() : result;
+ }
+ return null;
+ }
+
+ static Color getForegroundFor (HtmlRendererImpl r) {
+ if (r.isSelected() && !r.isParentFocused()) {
+ return getUnfocusedSelectionForeground();
+ }
+
+ if (!r.isEnabled()) {
+ return UIManager.getColor ("textInactiveText"); //NOI18N
+ }
+
+ Color result = null;
+ if (r.isSelected()) {
+ switch (r.getType()) {
+ case HtmlRendererImpl.TYPE_LIST :
+ result = UIManager.getColor ("List.selectionForeground"); //NOI18N
+ case HtmlRendererImpl.TYPE_TABLE :
+ result = UIManager.getColor ("Table.selectionForeground"); //NOI18N
+ case HtmlRendererImpl.TYPE_TREE :
+ result = UIManager.getColor ("Tree.selectionForeground"); //NOI18N
+ }
+ }
+ return result == null ? r.getForeground() : result;
+ }
+
+ private static boolean isGTK() {
+ return "GTK".equals (UIManager.getLookAndFeel().getID());
+ }
+
+ private static Color unfocusedSelBg = null;
+ private static Color unfocusedSelFg = null;
+ /** Get the system-wide unfocused selection background color */
+ private static Color getUnfocusedSelectionBackground() {
+ if (unfocusedSelBg == null) {
+ //allow theme/ui custom definition
+ unfocusedSelBg =
+ UIManager.getColor("nb.explorer.unfocusedSelBg"); //NOI18N
+ if (unfocusedSelBg == null) {
+ //try to get standard shadow color
+ unfocusedSelBg = UIManager.getColor("controlShadow"); //NOI18N
+ if (unfocusedSelBg == null) {
+ //Okay, the look and feel doesn't suport it, punt
+ unfocusedSelBg = Color.lightGray;
+ }
+ //Lighten it a bit because disabled text will use controlShadow/
+ //gray
+ unfocusedSelBg = unfocusedSelBg.brighter();
+ }
+ }
+ return unfocusedSelBg;
+ }
+
+ /** Get the system-wide unfocused selection foreground color */
+ private static Color getUnfocusedSelectionForeground() {
+ if (unfocusedSelFg == null) {
+ //allow theme/ui custom definition
+ unfocusedSelFg =
+ UIManager.getColor("nb.explorer.unfocusedSelFg"); //NOI18N
+ if (unfocusedSelFg == null) {
+ //try to get standard shadow color
+ unfocusedSelFg = UIManager.getColor("textText"); //NOI18N
+ if (unfocusedSelFg == null) {
+ //Okay, the look and feel doesn't suport it, punt
+ unfocusedSelFg = Color.BLACK;
+ }
+ }
+ }
+ return unfocusedSelFg;
+ }
+}
Index: openide/src/org/openide/awt/HtmlRenderer.java
===================================================================
RCS file: openide/src/org/openide/awt/HtmlRenderer.java
diff -N openide/src/org/openide/awt/HtmlRenderer.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/src/org/openide/awt/HtmlRenderer.java 28 Apr 2004 17:20:04 -0000
@@ -0,0 +1,1187 @@
+/*
+ * Sun Public License Notice
+ *
+ * The contents of this file are subject to the Sun Public License
+ * Version 1.0 (the "License"). You may not use this file except in
+ * compliance with the License. A copy of the License is available at
+ * http://www.sun.com/
+ *
+ * The Original Code is NetBeans. The Initial Developer of the Original
+ * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
+ * Microsystems, Inc. All Rights Reserved.
+ */
+
+/*
+ * HtmlRenderer.java
+ *
+ * Created on January 2, 2004, 12:49 AM
+ */
+package org.openide.awt;
+
+import org.openide.ErrorManager;
+
+import javax.swing.*;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.tree.TreeCellRenderer;
+import java.awt.*;
+import java.awt.font.LineMetrics;
+import java.awt.geom.Area;
+import java.awt.geom.Rectangle2D;
+import java.util.*;
+
+/** A generic cell renderer class which implements
+ * a lightweight html renderer supporting a minimal subset of HTML used for
+ * markup purposes only - basic font styles, colors, etc. Also supports
+ * named logical colors specified by a preceding ! character for specifying
+ * colors that should be looked up in the current look and feel's UIDefaults
+ * (e.g. <font color=&!textText&>
).
+ *
+ * If you only need to paint some HTML quickly, use the static methods for
+ * painting - renderString
, renderPlainString
or
+ * renderHtml
. These methods differ as follows:
+ *
renderPlainString
+ * or renderHtml
as appropriate. Note this method does not tolerate
+ * whitespace in opening html tags - it expects exactly 6 characters to make up
+ * the opening tag if present.renderHtml
, but will also honor
+ * STYLE_TRUNCATE
, so strings can be rendered with trailing
+ * elipsis if there is not enough spacerenderPlainString
is faster for that. It is useful
+ * if you want to render a string you know to be compliant
+ * HTML markup, but which does not have opening and closing HTML tags (though
+ * they are harmless if present). + * This parser is designed entirely for performance; there are no separate parsing + * and rendering loops. In order to acheive its performance, some trade offs + * are required. + * To reiterate: This is not a forgiving HTML parser - the HTML supplied + * must follow the guidelines documented here! + *
+ * The following tags are supported, in upper or lower (but not mixed) case: + * + *
<B> | + *Boldface text | + *
<S> | + *Strikethrough text | + *
<U> | + *Underline text | + *
<I> | + *Italic text | + *
<EM> | + *Emphasized text (same as italic) | + *
<STRONG> | + *Strong text (same as bold) | + *
<font> | + *Font color - font attributes other than color are not supported. Colors
+ * may be specified as hexidecimal strings, such as #FF0000 or as logical colors
+ * defined in the current look and feel by specifying a ! character as the first
+ * character of the color name. Logical colors are colors available from the
+ * current look and feel's UIManager. For example, <font
+ * color="!Tree.background"> will set the font color to the
+ * result of UIManager.getColor("Tree.background") .
+ * Font size tags are not supported.
+ * |
+ *
quot, lt, amp, lsquo, rsquo, ldquo, rdquo, ndash, mdash, ne,
+ * le, ge, copy, reg, trade.
. It also supports numeric entities
+ * (e.g. &8822;
).
+ * Why not use the JDK's HTML support? The JDK's HTML support works + * well for stable components, but suffers from performance problems in the + * case of cell renderers - each call to set the text (which happens once per + * cell, per paint) causes a document tree to be created in memory. For small, + * markup only strings, this is overkill. For rendering short strings + * (for example, in a tree or table cell renderer) + * with limited HTML, this method is approximately 10x faster than standard + * Swing HTML rendering. + * + *
Specifying logical colors
+ * Hardcoded text colors are undesirable, as they can be incompatible (even
+ * invisible) on some look and feels or themes, depending on the background
+ * color.
+ * The lightweight HTML renderer supports a non-standard syntax for specifying
+ * font colors via a key for a color in the UI defaults for the current look
+ * and feel. This is accomplished by prefixing the key name with a !
+ * character. For example: <font color='!controlShadow'>
.
+ *
+ *
Modes of operation
+ * This method supports two modes of operation:
+ *
STYLE_CLIP
- as much text as will fit in the pixel width passed
+ * to the method should be painted, and the text should be cut off at the maximum
+ * width or clip rectangle maximum X boundary for the graphics object, whichever is
+ * smaller.STYLE_TRUNCATE
- paint as much text as will fit in the pixel
+ * width passed to the method, but paint the last three characters as .'s, in the
+ * same manner as a JLabel truncates its text when the available space is too
+ * small.
+ * The paint methods can also be used in non-painting mode to establish the space
+ * necessary to paint a string. This is accomplished by passing the value of the
+ * paint
argument as false. The return value will be the required
+ * width in pixels
+ * to display the text. Note that in order to retrieve an
+ * accurate value, the argument for available width should be passed
+ * as Integer.MAX_VALUE
or an appropriate maximum size - otherwise
+ * the return value will either be the passed maximum width or the required
+ * width, whichever is smaller. Also, the clip shape for the passed graphics
+ * object should be null or a value larger than the maximum possible render size,
+ * or text size measurement will stop at the clip bounds. HtmlRenderer.getGraphics()
+ * will always return non-null and non-clipped, and is suitable to pass in such a
+ * situation.
+ *
+ *
+ * @author Tim Boudreau
+ */
+public final class HtmlRenderer {
+
+ private static final HtmlRendererImpl INSTANCE = new HtmlRendererImpl();
+
+ private HtmlRenderer() {
+
+ }
+
+ /**
+ * Returns an instance of Renderer which may be used as a table/tree/list cell renderer.
+ * This method must be called on the AWT event thread. If you know you will
+ * be passing it legal HTML (legal as documented here), call
+ * Do not add the result of this call to the AWT hierarchy - it is designed for offscreen
+ * painting, and may be reconfigured to paint something else at any time.
+ *
+ * This method must be called from the AWT event thread.
+ *
+ * @return A JLabel
+ */
+ public static final JLabel sharedLabel () {
+ assert SwingUtilities.isEventDispatchThread();
+ INSTANCE.reset();
+ return INSTANCE;
+ }
+
+ /** Interface aggregating TableCellRenderer, TreeCellRenderer and ListCellRenderer.
+ * Return type of This method is not thread-safe and should not be called off
+ * the AWT thread.
+ * @param s The string to render
+ * @param g A graphics object into which the string should be drawn, or which should be
+ * used for calculating the appropriate size
+ * @param x The x coordinate to paint at.
+ * @param y The y position at which to paint. Note that this method does not calculate font
+ * height/descent - this value should be the baseline for the line of text, not
+ * the upper corner of the rectangle to paint in.
+ * @param w The maximum width within which to paint.
+ * @param h The maximum height within which to paint.
+ * @param f The base font to be used for painting or calculating string width/height.
+ * @param defaultColor The base color to use if no font color is specified as html tags
+ * @param style The wrapping style to use, either When to use this method instead of the JDK's HTML support: when
+ * rendering short strings (for example, in a tree or table cell renderer)
+ * with limited HTML, this method is approximately 10x faster than JDK HTML
+ * rendering (it does not build and parse a document tree).
+ *
+ * Specifying logical colors Modes of operation
+ * This method can also be used in non-painting mode to establish the space
+ * necessary to paint a string. This is accomplished by passing the value of the
+ *
+ * This method will log a warning if it encounters HTML markup it cannot
+ * render. To aid diagnostics, if NetBeans is run with the argument
+ *
+ * This method is not thread-safe and should not be called off
+ * the AWT thread!
+ *
+ * @param s The string to render
+ * @param g A graphics object into which the string should be drawn, or which should be
+ * used for calculating the appropriate size
+ * @param x The x coordinate to paint at.
+ * @param y The y position at which to paint. Note that this method does not calculate font
+ * height/descent - this value should be the baseline for the line of text, not
+ * the upper corner of the rectangle to paint in.
+ * @param w The maximum width within which to paint.
+ * @param h The maximum height within which to paint.
+ * @param f The base font to be used for painting or calculating string width/height.
+ * @param defaultColor The base color to use if no font color is specified as html tags
+ * @param style The wrapping style to use, either
-* There is at most one VisualizerNode for one node. All of them are hold in a cache.
+* There is at most one VisualizerNode for one node. All of them are held in a cache.
*
-* The VisualizerNode level provides secure layer between Nodes and Swing AWT dispatch
+* VisualizerNode provides a thread-safe layer between Nodes, which may fire
+* property changes on any thread, and the AWT dispatch thread
* thread.
*
* @author Jaroslav Tulach
@@ -54,6 +60,12 @@
private static final ErrorManager err = ErrorManager.getDefault().getInstance("org.openide.explorer.view.VisualizerNode"); // NOI18N
+ /** Cached icon - pre-html, there was a separate cache in NodeRenderer, but
+ * if we're keeping a weak cache of VisualizerNodes, there's no reason not
+ * to keep it here */
+ private Icon icon = null;
+
+
// bugfix #29435, getVisualizer is synchronized in place of be called only from EventQueue
/** Finds VisualizerNode for given node.
* @param ch the children this visualizer should belong to
@@ -274,12 +286,17 @@
*/
public void propertyChange(final java.beans.PropertyChangeEvent evt) {
String name = evt.getPropertyName ();
+ boolean isIconChange = Node.PROP_ICON.equals(name) || Node.PROP_OPENED_ICON.equals(name);
if (
Node.PROP_NAME.equals (name) ||
Node.PROP_DISPLAY_NAME.equals (name) ||
- Node.PROP_ICON.equals (name) ||
- Node.PROP_OPENED_ICON.equals (name)
+ isIconChange
) {
+ if (isIconChange) {
+ //Ditch the cached icon type so the next call to getIcon() will
+ //recreate the ImageIcon
+ cachedIconType = -1;
+ }
SwingUtilities.invokeLater (this);
return;
}
@@ -305,13 +322,6 @@
}
} );
}
- /*
- if (
- "lookAndFeel".equals (name) // NOI18N
- ) {
- SwingUtilities.invokeLater (this);
- }
- */
}
/** Update the state of this class by retrieving new name, etc.
@@ -391,6 +401,60 @@
return getDisplayName ();
}
+ public String getHtmlDisplayName() {
+ return node.getHtmlDisplayName();
+ }
+
+ Icon getIcon(boolean opened, boolean large) {
+ int newCacheType = getCacheType(opened, large);
+
+ if (cachedIconType != newCacheType) {
+ int iconType = large ? BeanInfo.ICON_COLOR_32x32 :
+ BeanInfo.ICON_COLOR_16x16;
+
+ Image image = opened ? node.getOpenedIcon(
+ iconType) : node.getIcon(
+ iconType);
+
+ // bugfix #28515, check if getIcon contract isn't broken
+ if (image == null) {
+ String method = opened ? "getOpenedIcon" : "getIcon"; // NOI18N
+ ErrorManager.getDefault ().log (ErrorManager.WARNING, "Node \""
+ + node.getName () +
+ "\" [" + node.getClass().getName()+
+ "] cannot return null from " + method + "(). See Node." +
+ method + " contract."); // NOI18N
+
+ icon = defaultIcon;
+ } else {
+ icon = new ImageIcon (image);
+ }
+ }
+ cachedIconType = newCacheType;
+ return icon;
+ }
+
+ /** Some simple bitmasking to determine the type of the cached icon.
+ * Generally, it's worth caching one, but not a bunch - generally one will
+ * be used repeatedly. */
+ private static final int getCacheType (boolean opened, boolean large) {
+ return (opened ? 2 : 0) | (large ? 1 : 0);
+ }
+
+ /** loaded default icon */
+ private static Icon defaultIcon;
+ private int cachedIconType = -1;
+
+ /** default icon to use when none is present */
+ private static final String DEFAULT_ICON = "org/openide/resources/defaultNode.gif"; // NOI18N
+ /** Loads default icon if not loaded. */
+ private static Icon getDefaultIcon () {
+ if (defaultIcon == null) {
+ defaultIcon = new ImageIcon(Utilities.loadImage(DEFAULT_ICON));
+ }
+ return defaultIcon;
+ }
+
/** Strong reference.
*/
private static final class StrongReference extends WeakReference {
Index: openide/src/org/openide/filesystems/FileSystem.java
===================================================================
RCS file: /cvs/openide/src/org/openide/filesystems/FileSystem.java,v
retrieving revision 1.78
diff -u -u -r1.78 FileSystem.java
--- openide/src/org/openide/filesystems/FileSystem.java 16 Apr 2004 00:17:22 -0000 1.78
+++ openide/src/org/openide/filesystems/FileSystem.java 28 Apr 2004 17:20:07 -0000
@@ -700,6 +700,33 @@
public java.awt.Image annotateIcon (java.awt.Image icon, int iconType, java.util.Set files);
}
+ /** Extension interface for Status provides HTML-formatted annotations.
+ * Principally this is used to deëmphasize status text by presenting
+ * it in a lighter color, by placing it inside
+ * <font color=!controlShadow> tags. Note that it is preferable to
+ * use logical colors (such as controlShadow) which are resolved by calling
+ * UIManager.getColor(key) — this way they will always fit with the
+ * look and feel. To use a logical color, prefix the color name with a
+ * ! character.
+ *
+ * Please use only the limited markup subset of HTML supported by the
+ * lightweight HTML renderer.
+ * @see org.openide.awt.HtmlRenderer */
+ public static interface HtmlStatus extends Status {
+ /** Annotate a name such that the returned value contains HTML markup.
+ * The return value less the html content should typically be the same
+ * as the return value from
+ * For consistency with This method should return either an HTML display name
+ * or null; it should not return the non-html display name.
+ *
+ * @see org.openide.awt.HtmlRenderer
+ * @return a String containing conformant, legal HTML markup which
+ * represents the display name, or null. The default implementation
+ * returns null. */
+ public String getHtmlDisplayName() {
+ return null;
+ }
+
/** Register delegating lookup so it can always be found.
*/
final void registerDelegatingLookup (NodeLookup l) {
@@ -1051,6 +1069,24 @@
public int hashCode () {
return getName().hashCode ();
}
+
+ /** Return a variant of the display name containing HTML markup
+ * conforming to the limited subset of font-markup HTML supported by
+ * the lightweight HTML renderer org.openide.awt.HtmlRenderer
+ * (font color, bold, italic and strikethrough supported; font
+ * colors can be UIManager color keys if they are prefixed with
+ * a ! character, i.e. <font color=&'controlShadow'>).
+ * Enclosing html tags are not needed.
+ * This method should return either an HTML display name
+ * or null; it should not return the non-html display name.
+ *
+ * @see org.openide.awt.HtmlRenderer
+ * @return a String containing conformant, legal HTML markup which
+ * represents the display name, or null. The default implementation
+ * returns null. */
+ public String getHtmlDisplayName() {
+ return null;
+ }
}
/** Description of a Bean property on a node, and operations on it.
@@ -1197,6 +1233,24 @@
return getName ().hashCode () *
( valueType == null ? 1 : valueType.hashCode () );
+ }
+
+ /** Return a variant of the display name containing HTML markup
+ * conforming to the limited subset of font-markup HTML supported by
+ * the lightweight HTML renderer org.openide.awt.HtmlRenderer
+ * (font color, bold, italic and strikethrough supported; font
+ * colors can be UIManager color keys if they are prefixed with
+ * a ! character, i.e. <font color=&'controlShadow'>).
+ * Enclosing html tags are not needed.
+ * This method should return either an HTML display name
+ * or null; it should not return the non-html display name.
+ *
+ * @see org.openide.awt.HtmlRenderer
+ * @return a String containing conformant, legal HTML markup which
+ * represents the display name, or null. The default implementation
+ * returns null. */
+ public String getHtmlDisplayName() {
+ return null;
}
}
Index: projects/projectui/src/org/netbeans/modules/project/ui/ProjectsRootNode.java
===================================================================
RCS file: /cvs/projects/projectui/src/org/netbeans/modules/project/ui/ProjectsRootNode.java,v
retrieving revision 1.4
diff -u -u -r1.4 ProjectsRootNode.java
--- projects/projectui/src/org/netbeans/modules/project/ui/ProjectsRootNode.java 19 Apr 2004 12:39:37 -0000 1.4
+++ projects/projectui/src/org/netbeans/modules/project/ui/ProjectsRootNode.java 28 Apr 2004 17:20:18 -0000
@@ -252,6 +252,10 @@
return isMain() ? MessageFormat.format( badgedNamePattern, new Object[] { original } ) : original;
}
+ public String getHtmlDisplayName() {
+ return isMain() ? "" + super.getDisplayName() + "" : null; //NOI18N
+ }
+
public Image getIcon( int type ) {
Image original = super.getIcon( type );
return isMain() ? Utilities.mergeImages( original, mainProjectBadge, 5, 10 ) : original;
Index: vcscore/src/org/netbeans/modules/vcscore/VcsFileSystem.java
===================================================================
RCS file: /cvs/vcscore/src/org/netbeans/modules/vcscore/VcsFileSystem.java,v
retrieving revision 1.258
diff -u -u -r1.258 VcsFileSystem.java
--- vcscore/src/org/netbeans/modules/vcscore/VcsFileSystem.java 27 Apr 2004 14:42:24 -0000 1.258
+++ vcscore/src/org/netbeans/modules/vcscore/VcsFileSystem.java 28 Apr 2004 17:20:49 -0000
@@ -105,7 +105,8 @@
AbstractFileSystem.List, AbstractFileSystem.Info,
AbstractFileSystem.Change, FileSystem.Status,
CommandExecutionContext, CacheHandlerListener,
- FileObjectExistence, VcsOISActivator, Serializable {
+ FileObjectExistence, VcsOISActivator, Serializable,
+ FileSystem.HtmlStatus {
public static interface IgnoreListSupport {
@@ -2591,6 +2592,13 @@
} else {
result = displayName;
}
+ return result;
+ }
+
+ public String annotateNameHtml (String name, java.util.Set files) {
+ String result = annotateName (name, files);
+ result = Utilities.replaceString(result, name,
+ name + "") + ""; //NOI18N
return result;
}
setHtml(true)
on the
+ * result of this call after calling getNNNCellRenderer to provide this hint.
+ *
+ * @return A cell renderer that can render HTML.
+ */
+ public static final Renderer sharedRenderer () {
+ assert SwingUtilities.isEventDispatchThread();
+ return INSTANCE;
+ }
+
+ /**
+ * For HTML rendering jobs outside of trees/lists/tables, returns a JLabel which will paint its text using
+ * the lightweight HTML renderer. The result of this call will implement the Renderer
interface.
+ * sharedInstance()
.
+ */
+ public interface Renderer extends TableCellRenderer, TreeCellRenderer, ListCellRenderer {
+ /** Indicate that the component being rendered has keyboard focus. NetBeans requires that a different
+ * selection color be used depending on whether the view has focus.
+ *
+ * @param parentFocused Whether or not the focused selection color should be used
+ */
+ void setParentFocused (boolean parentFocused);
+
+ /**
+ * Indicate that the text should be painted centered below the icon. This is primarily used
+ * by org.openide.explorer.view.IconView
+ *
+ * @param centered Whether or not centered painting should be used.
+ */
+ void setCentered (boolean centered);
+
+ /**
+ * Set a number of pixels the icon and text should be indented. Used by ChoiceView and ListView to
+ * fake tree-style nesting. This value has no effect if setCentered(true)
has been called.
+ *
+ * @param pixels The number of pixels to indent
+ */
+ void setIndent (int pixels);
+
+ /**
+ * Explicitly tell the renderer it is going to receive HTML markup, or it is not. If the renderer should
+ * check the string for opening HTML tags to determine this, don't call this method. If you know
+ * the string will be compliant HTML, it is preferable to call this method with true; if you want to intentionally
+ * render HTML markup literally, call this method with false.
+ *
+ * @param val
+ */
+ void setHtml (boolean val);
+
+ /**
+ * Set the rendering style - this can be JLabel-style truncated-with-elipsis (...) text, or clipped text.
+ * The default is STYLE_CLIP.
+ *
+ * @param style The text style
+ */
+ void setRenderStyle (int style);
+
+ /** Set the icon to be used for painting
+ *
+ * @param icon An icon or null
+ */
+ void setIcon (Icon icon);
+
+ /** Clear any stale data from previous use by other components,
+ * clearing the icon, text, disabled state and other customizations, returning the component
+ * to its initialized state. This is done automatically when get*CellRenderer() is called.
+ */
+ void reset();
+
+ /** Set the text to be displayed. Use this if the object being rendered's toString() does not
+ * return a real user-displayable string, after calling get**CellRenderer(). Typically after calling
+ * this one calls setHtml() if the text is known to either be or not be HTML markup.
+ *
+ * @param txt The text that should be displayed
+ */
+ void setText (String txt);
+
+ /**
+ * Convenience method to set the gap between the icon and text.
+ *
+ * @param gap an integer number of pixels
+ */
+ void setIconTextGap (int gap);
+ }
+
+ /** Stack object used during HTML rendering to hold previous colors in
+ * the case of nested color entries. */
+ private static Stack colorStack = new Stack();
+ /** Constant used by renderString
, renderPlainString
+ * renderHTML
, and HtmlRenderer.setRenderStyle
+ * if painting should simply be cut off at the boundary of the cooordinates passed. */
+ public static final int STYLE_CLIP=0;
+ /** Constant used by renderString
, renderPlainString
+ * renderHTML
and HtmlRenderer.setRenderStyle
if
+ * painting should produce an ellipsis (...)
+ * if the text would overlap the boundary of the coordinates passed */
+ public static final int STYLE_TRUNCATE=1;
+ /** Constant used by renderString
, renderPlainString
+ * renderHTML
and HtmlRenderer.setRenderStyle
+ * if painting should word wrap the text. In
+ * this case, the return value of any of the above methods will be the
+ * height, rather than width painted. */
+ private static final int STYLE_WORDWRAP=2;
+ /** System property to cause exceptions to be thrown when unparsable
+ * html is encountered */
+ private static final boolean strictHTML = Boolean.getBoolean(
+ "netbeans.lwhtml.strict"); //NOI18N
+ /** Cache for strings which have produced errors, so we don't post an
+ * error message more than once */
+ private static Set badStrings=null;
+ /** Definitions for a limited subset of sgml character entities */
+ private static final Object[] entities = new Object[] {
+ new char[] {'g','t'}, new char[] {'l','t'},
+ new char[] {'q','u','o','t'}, new char[] {'a','m','p'},
+ new char[] {'l','s','q','u','o'},
+ new char[] {'r','s','q','u','o'},
+ new char[] {'l','d','q','u','o'},
+ new char[] {'r','d','q','u','o'},
+ new char[] {'n','d','a','s','h'},
+ new char[] {'m','d','a','s','h'},
+ new char[] {'n','e'},
+ new char[] {'l','e'},
+ new char[] {'g','e'},
+
+ new char[] {'c','o','p','y'},
+ new char[] {'r','e','g'},
+ new char[] {'t','r','a','d','e'}
+ //The rest of the SGML entities are left as an excercise for the reader
+ }; //NOI18N
+ /** Mappings for the array of sgml character entities to characters */
+ private static final char[] entitySubstitutions = new char[] {
+ '>','<','"','&',8216, 8217, 8220, 8221, 8211, 8212, 8800, 8804, 8805,
+ 169, 174, 8482
+ };
+
+ /**Render a string to a graphics instance, using the same API as renderHTML().
+ * Can render a string using JLabel-style ellipsis (...) in the case that
+ * it will not fit in the passed rectangle, if the style parameter is
+ * STYLE_CLIP. Returns the width in pixels successfully painted.
+ * This method is not thread-safe and should not be called off
+ * the AWT thread.
+ *
+ * @see #renderHTML */
+ public static double renderPlainString(String s, Graphics g, int x, int y, int w, int h, Font f, Color defaultColor, int style, boolean paint) {
+ assert SwingUtilities.isEventDispatchThread();
+ //per Jarda's request, keep the word wrapping code but don't expose it.
+ if (style < 0 || style > 1) {
+ throw new IllegalArgumentException(
+ "Unknown rendering mode: " + style); //NOI18N
+ }
+ return _renderPlainString(s, g, x, y, w, h, f, defaultColor, style,
+ paint);
+ }
+
+ private static double _renderPlainString(String s, Graphics g, int x, int y, int w, int h, Font f, Color foreground, int style, boolean paint) {
+ g.setColor(foreground);
+ g.setFont(f);
+ FontMetrics fm = g.getFontMetrics(f);
+ Rectangle2D r = fm.getStringBounds(s, g);
+ if ((r.getWidth() <= w) || (style == STYLE_CLIP)) {
+ if (paint) {
+ g.drawString(s, x, y);
+ }
+ } else {
+ char[] chars = new char[s.length()];
+ s.getChars(0, s.length()-1, chars, 0);
+ if (chars.length == 0) {
+ return 0;
+ }
+ double chWidth = r.getWidth() / chars.length;
+ int estCharsOver = new Double((r.getWidth() - w) / chWidth).intValue();
+ if (style == STYLE_TRUNCATE) {
+ int length = chars.length - estCharsOver;
+ if (length <=0) {
+ return 0;
+ }
+ if (paint) {
+ if (length > 3) {
+ Arrays.fill(chars, length-3, length, '.');
+ g.drawChars(chars, 0, length, x, y);
+ } else {
+ Shape shape = g.getClip();
+ if (s != null) {
+ Area area = new Area(shape);
+ area.intersect (new Area(new Rectangle(x,y,w,h)));
+ g.setClip(area);
+ } else {
+ g.setClip(new Rectangle(x,y,w,h));
+ }
+ g.drawString("...", x,y);
+ g.setClip(shape);
+ }
+ }
+ } else {
+ //TODO implement plaintext word wrap if we want to support it at some point
+ }
+ }
+ return r.getWidth();
+ }
+
+ /** Render a string to a graphics context, using HTML markup if the string
+ * begins with html tags. Delegates to renderPlainString()
+ * or renderHTML()
as appropriate. See the documentation for
+ * renderHTML()
for details of the subset of HTML that is
+ * supported.
+ * STYLE_CLIP
,
+ * or STYLE_TRUNCATE
+ * @param paint True if actual painting should occur. If false, this method will not actually
+ * paint anything, only return a value representing the width/height needed to
+ * paint the passed string.
+ * @return The width in pixels required
+ * to paint the complete string, or the passed parameter w
if it is
+ * smaller than the required width.
+ */
+ public static double renderString(String s, Graphics g, int x, int y, int w, int h, Font f, Color defaultColor, int style, boolean paint) {
+ if (s.startsWith(" 1) {
+ throw new IllegalArgumentException(
+ "Unknown rendering mode: " + style); //NOI18N
+ }
+ return _renderHTML(6, s, g, x, y, w, h, f, defaultColor, style, paint);
+ } else {
+ return renderPlainString(s, g, x, y, w, h, f, defaultColor, style, paint);
+ }
+ }
+
+ /** Render a string as HTML using a fast, lightweight renderer supporting a limited
+ * subset of HTML. The following tags are supported, in upper or lower case:
+ *
+ *
+ *
+ * The lightweight html renderer supports the following named sgml character
+ * entities:
+ *
+ * <B>
+ * Boldface text
+ *
+ *
+ * <S>
+ * Strikethrough text
+ *
+ *
+ * <U>
+ * Underline text
+ *
+ *
+ * <I>
+ * Italic text
+ *
+ *
+ * <EM>
+ * Emphasized text (same as italic)
+ *
+ *
+ * <STRONG>
+ * Strong text (same as bold)
+ *
+ *
+ * <font>
+ * Font color - font attributes other than color are not supported. Colors
+ * may be specified as hexidecimal strings, such as #FF0000 or as logical colors
+ * defined in the current look and feel by specifying a ! character as the first
+ * character of the color name. Logical colors are colors available from the
+ * current look and feel's UIManager. For example,
+ * <font
+ * color="!Tree.background">
will set the font color to the
+ * result of UIManager.getColor("Tree.background")
.
+ * Font size tags are not supported.
+ * quot, lt, amp, lsquo, rsquo, ldquo, rdquo, ndash, mdash, ne,
+ * le, ge, copy, reg, trade.
. It also supports numeric entities
+ * (e.g. &8822;
).
+ *
+ * Hardcoded text colors are undesirable, as they can be incompatible (even
+ * invisible) on some look and feels or themes.
+ * The lightweight HTML renderer supports a non-standard syntax for specifying
+ * font colors via a key for a color in the UI defaults for the current look
+ * and feel. This is accomplished by prefixing the key name with a !
+ * character. For example: <font color='!controlShadow'>
.
+ *
+ *
+ * This method supports two modes of operation:
+ *
+ *
+ * STYLE_CLIP
- as much text as will fit in the pixel width passed
+ * to the method should be painted, and the text should be cut off at the maximum
+ * width or clip rectangle maximum X boundary for the graphics object, whichever is
+ * smaller.STYLE_TRUNCATE
- paint as much text as will fit in the pixel
+ * width passed to the method, but paint the last three characters as .'s, in the
+ * same manner as a JLabel truncates its text when the available space is too
+ * small.paint
argument as false. The return value will be the required
+ * width in pixels
+ * to display the text. Note that in order to retrieve an
+ * accurate value, the argument for available width should be passed
+ * as Integer.MAX_VALUE
or an appropriate maximum size - otherwise
+ * the return value will either be the passed maximum width or the required
+ * width, whichever is smaller. Also, the clip shape for the passed graphics
+ * object should be null or a value larger than the maximum possible render size.
+ * -J-Dnetbeans.lwhtml.strict=true
an exception will be thrown
+ * when an attempt is made to render unsupported HTML.STYLE_CLIP
,
+ * or STYLE_TRUNCATE
+ * @param paint True if actual painting should occur. If false, this method will not actually
+ * paint anything, only return a value representing the width/height needed to
+ * paint the passed string.
+ * @return The width in pixels required
+ * to paint the complete string, or the passed parameter w
if it is
+ * smaller than the required width.
+ */
+ public static double renderHTML(String s, Graphics g, int x, int y,
+ int w, int h, Font f,
+ Color defaultColor, int style,
+ boolean paint) {
+
+ assert SwingUtilities.isEventDispatchThread();
+
+ //per Jarda's request, keep the word wrapping code but don't expose it.
+ if (style < 0 || style > 1) {
+ throw new IllegalArgumentException(
+ "Unknown rendering mode: " + style); //NOI18N
+ }
+ return _renderHTML(0, s, g, x, y, w, h, f, defaultColor, style,
+ paint);
+ }
+
+ /** Implementation of HTML rendering */
+ private static double _renderHTML(int pos, String s, Graphics g, int x,
+ int y, int w, int h, Font f, Color defaultColor, int style,
+ boolean paint) {
+
+ g.setColor(defaultColor);
+ g.setFont(f);
+ char[] chars = s.toCharArray();
+ int origX = x;
+ boolean done = false; //flag if rendering completed, either by finishing the string or running out of space
+ boolean inTag = false; //flag if the current position is inside a tag, and the tag should be processed rather than rendering
+ boolean inClosingTag = false; //flag if the current position is inside a closing tag
+ boolean strikethrough = false; //flag if a strikethrough line should be painted
+ boolean underline = false; //flag if an underline should be painted
+ boolean bold = false; //flag if text is currently bold
+ boolean italic = false; //flag if text is currently italic
+ boolean truncated = false; //flag if the last possible character has been painted, and the next loop should paint "..." and return
+ double widthPainted = 0; //the total width painted, for calculating needed space
+ double heightPainted = 0; //the total height painted, for calculating needed space
+ boolean lastWasWhitespace = false; //flag to skip additional whitespace if one whitespace char already painted
+ double lastHeight=0; //the last line height, for calculating total required height
+
+ double dotWidth = 0;
+ //Calculate the width of a . character if we may need to truncate
+ if (style == STYLE_TRUNCATE) {
+ dotWidth = g.getFontMetrics().charWidth('.'); //NOI18N
+ }
+
+ /* How this all works, for anyone maintaining this code (hopefully it will
+ never need it):
+ 1. The string is converted to a char array
+ 2. Loop over the characters. Variable pos is the current point.
+ 2a. See if we're in a tag by or'ing inTag with currChar == '<'
+ If WE ARE IN A TAG:
+ 2a1: is it an opening tag?
+ If YES:
+ - Identify the tag, Configure the Graphics object with
+ the appropriate font, color, etc. Set pos = the first
+ character after the tag
+ If NO (it's a closing tag)
+ - Identify the tag. Reconfigure the Graphics object
+ with the state it should be in outside the tag
+ (reset the font if italic, pop a color off the stack, etc.)
+ 2b. If WE ARE NOT IN A TAG
+ - Locate the next < or & character or the end of the string
+ - Paint the characters using the Graphics object
+ - Check underline and strikethrough tags, and paint line if
+ needed
+ See if we're out of space, and do the right thing for the style
+ (paint ..., give up or skip to the next line)
+ */
+
+ //Clear any junk left behind from a previous rendering loop
+ colorStack.clear();
+
+ //Enter the painting loop
+ while (!done) {
+ if (pos == s.length()) {
+ return widthPainted;
+ }
+ //see if we're in a tag
+ try {
+ inTag |= chars[pos] == '<';
+ } catch (ArrayIndexOutOfBoundsException e) {
+ //Should there be any problem, give a meaningful enough
+ //message to reproduce the problem
+ ArrayIndexOutOfBoundsException aib =
+ new ArrayIndexOutOfBoundsException(
+ "HTML rendering failed at position " + pos + " in String \""
+ + s + "\". Please report this at http://www.netbeans.org"); //NOI18N
+ throw aib;
+ }
+ inClosingTag = inTag && (pos+1 < chars.length) && chars[pos+1]
+ == '/'; //NOI18N
+
+ if (truncated) {
+ //Then we've almost run out of space, time to print ... and quit
+ g.setColor(defaultColor);
+ g.setFont(f);
+ if (paint) {
+ g.drawString("...", x, y); //NOI18N
+ }
+ done = true;
+ } else if (inTag) {
+ //If we're in a tag, don't paint, process it
+ pos++;
+ int tagEnd = pos;
+ while (!done && (chars[tagEnd] != '>')) {
+ done = tagEnd == chars.length -1;
+ tagEnd++;
+ }
+
+ if (inClosingTag) {
+ //Handle closing tags by resetting the Graphics object (font, etc.)
+ pos++;
+ switch (chars[pos]) {
+ case 'P' :
+ case 'p' :
+ case 'H' :
+ case 'h' : break; //ignore html opening/closing tags
+ case 'B' :
+ case 'b' :
+ if (chars[pos+1] == 'r' || chars[pos+1] == 'R') {
+ break;
+ }
+ if (!bold) {
+ throwBadHTML("Closing bold tag w/o " + //NOI18N
+ "opening bold tag", pos, chars); //NOI18N
+ }
+ if (italic) {
+ g.setFont(f.deriveFont(Font.ITALIC));
+ } else {
+ g.setFont(f.deriveFont(Font.PLAIN));
+ }
+ bold = false;
+ break;
+ case 'E' :
+ case 'e' : //em tag
+ case 'I' :
+ case 'i' :
+ if (bold) {
+ g.setFont(f.deriveFont(Font.BOLD));
+ } else {
+ g.setFont(f.deriveFont(Font.PLAIN));
+ }
+ if (!italic) {
+ throwBadHTML("Closing italics tag w/o" //NOI18N
+ + "opening italics tag", pos, chars); //NOI18N
+ }
+ italic = false;
+ break;
+ case 'S' :
+ case 's' :
+ switch (chars[pos+1]) {
+ case 'T' :
+ case 't' : if (italic) {
+ g.setFont(f.deriveFont(
+ Font.ITALIC));
+ } else {
+ g.setFont(f.deriveFont(
+ Font.PLAIN));
+ }
+ bold = false;
+ break;
+ case '>' :
+ strikethrough = false;
+ break;
+ }
+ break;
+ case 'U' :
+ case 'u' : underline = false;
+ break;
+ case 'F' :
+ case 'f' :
+ if (colorStack.isEmpty()) {
+ g.setColor(defaultColor);
+ } else {
+ g.setColor((Color) colorStack.pop());
+ }
+ break;
+ default :
+ throwBadHTML(
+ "Malformed or unsupported HTML", //NOI18N
+ pos, chars);
+ }
+ } else {
+ //Okay, we're in an opening tag. See which one and configure the Graphics object
+ switch (chars[pos]) {
+ case 'B' :
+ case 'b' :
+ switch (chars[pos+1]) {
+ case 'R' :
+ case 'r' :
+ if (style == STYLE_WORDWRAP) {
+ x = origX;
+ int lineHeight = g.getFontMetrics().getHeight();
+ y += lineHeight;
+ heightPainted += lineHeight;
+ widthPainted = 0;
+ }
+ break;
+ case '>' :
+ bold = true;
+ if (italic) {
+ g.setFont(f.deriveFont(Font.BOLD | Font.ITALIC));
+ } else {
+ g.setFont(f.deriveFont(Font.BOLD));
+ }
+ break;
+ }
+ break;
+ case 'e' : //em tag
+ case 'E' :
+ case 'I' :
+ case 'i' :
+ italic = true;
+ if (bold) {
+ g.setFont(f.deriveFont(Font.ITALIC | Font.BOLD));
+ } else {
+ g.setFont(f.deriveFont(Font.ITALIC));
+ }
+ break;
+ case 'S' :
+ case 's' :
+ switch (chars[pos+1]) {
+ case '>' :
+ strikethrough = true;
+ break;
+ case 'T' :
+ case 't' :
+ bold = true;
+ if (italic) {
+ g.setFont(f.deriveFont(Font.BOLD | Font.ITALIC));
+ } else {
+ g.setFont(f.deriveFont(Font.BOLD));
+ }
+ break;
+ }
+ break;
+ case 'U' :
+ case 'u' :
+ underline = true;
+ break;
+ case 'f' :
+ case 'F' :
+ Color c = findColor(chars, pos, tagEnd);
+ colorStack.push(g.getColor());
+ g.setColor(c);
+ break;
+ case 'P' :
+ case 'p' :
+ if (style == STYLE_WORDWRAP) {
+ x = origX;
+ int lineHeight=g.getFontMetrics().getHeight();
+ y += lineHeight + (lineHeight / 2);
+ heightPainted = y + lineHeight;
+ widthPainted = 0;
+ }
+ break;
+ case 'H' :
+ case 'h' : //Just an opening HTML tag
+ if (pos == 1) {
+ break;
+ }
+ default : throwBadHTML(
+ "Malformed or unsupported HTML", pos, chars); //NOI18N
+ }
+ }
+
+ pos = tagEnd + (done ? 0 : 1);
+ inTag = false;
+ } else {
+ //Okay, we're not in a tag, we need to paint
+
+ if (lastWasWhitespace) {
+ //Skip multiple whitespace characters
+ while (pos < s.length() && Character.isWhitespace(chars[pos])) {
+ pos++;
+ }
+ //Check strings terminating with multiple whitespace -
+ //otherwise could get an AIOOBE here
+ if (pos == chars.length - 1) {
+ return style != STYLE_WORDWRAP ? widthPainted : heightPainted;
+ }
+ }
+
+ //Flag to indicate if an ampersand entity was processed,
+ //so the resulting & doesn't get treated as the beginning of
+ //another entity (and loop endlessly)
+ boolean isAmp=false;
+ //Flag to indicate the next found < character really should
+ //be painted (it came from an entity), it is not the beginning
+ //of a tag
+ boolean nextLtIsEntity=false;
+ int nextTag = chars.length-1;
+ if ((chars[pos] == '&')) {
+ boolean inEntity=pos != chars.length-1;
+ if (inEntity) {
+ int newPos = substEntity(chars, pos+1);
+ inEntity = newPos != -1;
+ if (inEntity) {
+ pos = newPos;
+ isAmp = chars[pos] == '&';
+ //flag it so the next iteration won't think the <
+ //starts a tag
+ nextLtIsEntity = chars[pos] == '<';
+ } else {
+ nextLtIsEntity = false;
+ isAmp = true;
+ }
+ }
+ } else {
+ nextLtIsEntity=false;
+ }
+
+ for (int i=pos; i < chars.length; i++) {
+ if (((chars[i] == '<') && (!nextLtIsEntity)) || ((chars[i] == '&') && !isAmp)) {
+ nextTag = i-1;
+ break;
+ }
+ //Reset these flags so we don't skip all & or < chars for the rest of the string
+ isAmp = false;
+ nextLtIsEntity=false;
+ }
+
+
+ FontMetrics fm = g.getFontMetrics(g.getFont());
+ //Get the bounds of the substring we'll paint
+ Rectangle2D r = fm.getStringBounds(chars, pos, nextTag + 1, g);
+ //Store the height, so we can add it if we're in word wrap mode,
+ //to return the height painted
+ lastHeight = r.getHeight();
+ //Work out the length of this tag
+ int length = (nextTag + 1) - pos;
+
+ //Flag to be set to true if we run out of space
+ boolean goToNextRow = false;
+
+ //Flag that the current line is longer than the available width,
+ //and should be wrapped without finding a word boundary
+ boolean brutalWrap = false;
+ //Work out the per-character avg width of the string, for estimating
+ //when we'll be out of space and should start the ... in truncate
+ //mode
+ double chWidth;
+
+ if (style == STYLE_TRUNCATE) {
+ //if we're truncating, use the width of one dot from an
+ //ellipsis to get an accurate result for truncation
+ chWidth = dotWidth;
+ } else {
+ //calculate an average character width
+ chWidth= r.getWidth() / (nextTag - pos);
+ //can return this sometimes, so handle it
+ if (chWidth == Double.POSITIVE_INFINITY || chWidth == Double.NEGATIVE_INFINITY) {
+ chWidth = fm.getMaxAdvance();
+ }
+ }
+
+
+ if ((style != STYLE_CLIP) &&
+ ((style == STYLE_TRUNCATE &&
+ (widthPainted + r.getWidth() > w - (chWidth * 2)))) ||
+ (style == STYLE_WORDWRAP &&
+ (widthPainted + r.getWidth() > w))) {
+
+ if (chWidth > 3) {
+ double pixelsOff = (widthPainted + (
+ r.getWidth() + 5)
+ ) - w;
+
+ double estCharsOver = pixelsOff / chWidth;
+
+ if (style == STYLE_TRUNCATE) {
+ int charsToPaint = Math.round(Math.round((w - widthPainted) / chWidth));
+/* System.err.println("estCharsOver = " + estCharsOver);
+ System.err.println("Chars to paint " + charsToPaint + " chwidth = " + chWidth + " widthPainted " + widthPainted);
+ System.err.println("Width painted + width of tag: " + (widthPainted + r.getWidth()) + " available: " + w);
+ */
+
+ int startPeriodsPos = pos + charsToPaint -3;
+ if (startPeriodsPos >= chars.length) {
+ startPeriodsPos = chars.length - 4;
+ }
+ length = (startPeriodsPos - pos);
+ if (length < 0) length = 0;
+ r = fm.getStringBounds(chars, pos, pos+length, g);
+// System.err.println("Truncated set to true at " + pos + " (" + chars[pos] + ")");
+ truncated = true;
+ } else {
+ goToNextRow = true;
+ int lastChar = new Double(nextTag -
+ estCharsOver).intValue();
+ brutalWrap = x == 0;
+ for (int i = lastChar; i > pos; i--) {
+ lastChar--;
+ if (Character.isWhitespace(chars[i])) {
+ length = (lastChar - pos) + 1;
+ brutalWrap = false;
+ break;
+ }
+ }
+ if ((lastChar <= pos) && (length > estCharsOver)
+ && !brutalWrap) {
+ x = origX;
+ y += r.getHeight();
+ heightPainted += r.getHeight();
+ boolean boundsChanged = false;
+ while (!done && Character.isWhitespace(
+ chars[pos]) && (pos < nextTag)) {
+ pos++;
+ boundsChanged = true;
+ done = pos == chars.length -1;
+ }
+ if (pos == nextTag) {
+ lastWasWhitespace = true;
+ }
+ if (boundsChanged) {
+ //recalculate the width we will add
+ r = fm.getStringBounds(chars, pos,
+ nextTag + 1, g);
+ }
+ goToNextRow = false;
+ widthPainted = 0;
+ if (chars[pos - 1 + length] == '<') {
+ length --;
+ }
+ } else if (brutalWrap) {
+ //wrap without checking word boundaries
+ length = (new Double(
+ (w - widthPainted) / chWidth)
+ ).intValue();
+ if (pos + length > nextTag) {
+ length = (nextTag - pos);
+ }
+ goToNextRow = true;
+ }
+ }
+ }
+ }
+ if (!done) {
+ if (paint) {
+ g.drawChars(chars, pos, length, x, y);
+ }
+
+ if ((strikethrough || underline)){
+ LineMetrics lm = fm.getLineMetrics(chars, pos,
+ length - 1, g);
+ int lineWidth = new Double(x +
+ r.getWidth()).intValue();
+ if (paint) {
+ if (strikethrough) {
+ int stPos = Math.round(
+ lm.getStrikethroughOffset()) +
+ g.getFont().getBaselineFor(chars[pos])
+ + 1;
+ // int stThick = Math.round (lm.getStrikethroughThickness()); //XXX
+ g.drawLine(x, y + stPos, lineWidth, y + stPos);
+ }
+ if (underline) {
+ int stPos = Math.round(
+ lm.getUnderlineOffset()) +
+ g.getFont().getBaselineFor(chars[pos])
+ + 1;
+ // int stThick = new Float (lm.getUnderlineThickness()).intValue(); //XXX
+ g.drawLine(x, y + stPos, lineWidth, y + stPos);
+ }
+ }
+ }
+ if (goToNextRow) {
+ //if we're in word wrap mode and need to go to the next
+ //line, reconfigure the x and y coordinates
+ x = origX;
+ y += r.getHeight();
+ heightPainted += r.getHeight();
+ widthPainted = 0;
+ pos += (length);
+ //skip any leading whitespace
+ while ((pos < chars.length) &&
+ (Character.isWhitespace(chars[pos])) &&
+ (chars[pos] != '<')) {
+ pos++;
+ }
+ lastWasWhitespace = true;
+ done |= pos >= chars.length;
+ } else {
+ x += r.getWidth();
+ widthPainted += r.getWidth();
+ lastWasWhitespace = Character.isWhitespace(
+ chars[nextTag]);
+ pos = nextTag + 1;
+ }
+ done |= nextTag == chars.length;
+ }
+ }
+ }
+ if (style != STYLE_WORDWRAP) {
+ return widthPainted;
+ } else {
+ return heightPainted + lastHeight;
+ }
+ }
+
+ /** Parse a font color tag and return an appopriate java.awt.Color instance */
+ private static Color findColor(final char[] ch, final int pos,
+ final int tagEnd) {
+ int colorPos = pos;
+ boolean useUIManager = false;
+ for (int i=pos; i < tagEnd; i ++) {
+ if (ch[i] == 'c') {
+ colorPos = i + 6;
+ if (ch[colorPos] == '\'' || ch[colorPos] == '"') {
+ colorPos++;
+ }
+ //skip the leading # character
+ if (ch[colorPos] == '#') {
+ colorPos++;
+ } else if (ch[colorPos] == '!') {
+ useUIManager = true;
+ colorPos++;
+ }
+ break;
+ }
+ }
+ if (colorPos == pos) {
+ String out = "Could not find color identifier in font declaration";
+ throwBadHTML(out, pos, ch);
+ }
+ //Okay, we're now on the first character of the hex color definition
+ String s;
+ if (useUIManager) {
+ int end = ch.length-1;
+ for (int i=colorPos; i < ch.length; i++) {
+ if (ch[i] == '"' || ch[i] == '\'') { //NOI18N
+ end = i;
+ break;
+ }
+ }
+ s = new String(ch, colorPos, end-colorPos);
+ } else {
+ s = new String(ch, colorPos, 6);
+ }
+ Color result=null;
+ if (useUIManager) {
+ result = UIManager.getColor(s);
+ //Not all look and feels will provide standard colors; handle it gracefully
+ if (result == null) {
+ throwBadHTML(
+ "Could not resolve logical font declared in HTML: " + s,
+ pos, ch);
+ result = UIManager.getColor("textText"); //NOI18N
+ //Avoid NPE in headless situation?
+ if (result == null) {
+ result = Color.BLACK;
+ }
+ }
+ } else {
+ try {
+ int rgb = Integer.parseInt(s, 16);
+ result = new Color(rgb);
+ } catch (NumberFormatException nfe) {
+ throwBadHTML(
+ "Illegal hexadecimal color text: " + s + //NOI18N
+ " in HTML string", colorPos, ch);
+ }
+ }
+ if (result == null) {
+ throwBadHTML("Unresolvable html color: " + s //NOI18N
+ + " in HTML string \n ", pos, ch);
+ }
+ return result;
+ }
+
+ /** Find an entity at the passed character position in the passed array.
+ * If an entity is found, the trailing ; character will be substituted
+ * with the resulting character, and the position of that character
+ * in the array will be returned as the new position to render from,
+ * causing the renderer to skip the intervening characters */
+ private static final int substEntity(char[] ch, int pos) {
+ //There are no 1 character entities, abort
+ if (pos >= ch.length-2) {
+ return -1;
+ }
+ //if it's numeric, parse out the number
+ if (ch[pos] == '#') {
+ return substNumericEntity(ch, pos+1);
+ }
+ //Okay, we've potentially got a named character entity. Try to find it.
+ boolean match;
+ for (int i=0; i < entities.length; i++) {
+ char[] c = (char[]) entities[i];
+ match = true;
+ if (c.length < ch.length-pos) {
+ for (int j=0; j < c.length; j++) {
+ match &= c[j] == ch[j+pos];
+ }
+ } else {
+ match = false;
+ }
+ if (match) {
+ //if it's a match, we still need the trailing ;
+ if (ch[pos+c.length] == ';') {
+ //substitute the character referenced by the entity
+ ch[pos+c.length] = entitySubstitutions[i];
+ return pos+c.length;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /** Finds a character defined as a numeric entity (e.g. „)
+ * and replaces the trailing ; with the referenced character, returning
+ * the position of it so the renderer can continue from there.
+ */
+ private static final int substNumericEntity(char[] ch, int pos) {
+ for (int i=pos; i < ch.length; i++) {
+ if (ch[i] == ';') {
+ try {
+ ch[i] = (char) Integer.parseInt(
+ new String(ch, pos, i - pos));
+ return i;
+ } catch (NumberFormatException nfe) {
+ throwBadHTML("Unparsable numeric entity: " +
+ new String(ch, pos, i - pos), pos, ch); //NOI18N
+ }
+ }
+ }
+ return -1;
+ }
+
+ /** Throw an exception for unsupported or bad html, indicating where the problem is
+ * in the message */
+ private static void throwBadHTML(String msg, int pos, char[] chars) {
+ char[] chh = new char[pos];
+ Arrays.fill(chh, ' '); //NOI18N
+ chh[pos-1] = '^'; //NOI18N
+ String out = msg + "\n " + new String(chars) + "\n "
+ + new String(chh) + "\n Full HTML string:" + new String(chars); //NOI18N
+ if (!strictHTML) {
+ if (ErrorManager.getDefault().isLoggable(ErrorManager.WARNING)) {
+ if (badStrings == null) {
+ badStrings = new HashSet();
+ }
+ if (!badStrings.contains(msg)) {
+ //ErrorManager bug, issue 38372 - log messages containing
+ //newlines are truncated - so for now we iterate the
+ //string we've just constructed
+ StringTokenizer tk = new StringTokenizer(out, "\n", false);
+ while (tk.hasMoreTokens()) {
+ ErrorManager.getDefault().log(ErrorManager.WARNING,
+ tk.nextToken());
+ }
+ badStrings.add(msg.intern());
+ }
+ }
+ } else {
+ throw new IllegalArgumentException(out);
+ }
+ }
+}
Index: openide/src/org/openide/awt/HtmlRendererImpl.java
===================================================================
RCS file: openide/src/org/openide/awt/HtmlRendererImpl.java
diff -N openide/src/org/openide/awt/HtmlRendererImpl.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/src/org/openide/awt/HtmlRendererImpl.java 28 Apr 2004 17:20:04 -0000
@@ -0,0 +1,524 @@
+/*
+ * Sun Public License Notice
+ *
+ * The contents of this file are subject to the Sun Public License
+ * Version 1.0 (the "License"). You may not use this file except in
+ * compliance with the License. A copy of the License is available at
+ * http://www.sun.com/
+ *
+ * The Original Code is NetBeans. The Initial Developer of the Original
+ * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
+ * Microsystems, Inc. All Rights Reserved.
+ */
+package org.openide.awt;
+import org.openide.awt.HtmlRenderer;
+import org.openide.awt.HtmlLabelUI;
+
+import javax.swing.*;
+import javax.swing.plaf.basic.BasicLabelUI;
+import javax.swing.plaf.LabelUI;
+import javax.swing.event.AncestorListener;
+import javax.swing.border.Border;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.beans.PropertyChangeListener;
+import java.beans.VetoableChangeListener;
+import java.io.*;
+
+/**
+ * Html renderer component implementation. The actual painting is done by HtmlLabelUI, which uses
+ * HtmlRenderer.renderString().
+ *
+ */
+public class HtmlRendererImpl extends JLabel implements HtmlRenderer.Renderer {
+
+ private boolean centered = false;
+ private boolean parentFocused = false;
+ private Boolean html = null;
+ private int indent = 0;
+ private Border border = null;
+ private boolean selected = false;
+ private boolean leadSelection = false;
+ private Dimension prefSize = null;
+ private int type = TYPE_UNKNOWN;
+ private int renderStyle = HtmlRenderer.STYLE_CLIP;
+ private static final Rectangle bounds = new Rectangle();
+ private boolean enabled = true;
+
+ private static final boolean swingRendering = Boolean.getBoolean ("nb.useSwingHtmlRendering"); //NOI18N
+ private static final Insets EMPTY_INSETS = new Insets (0, 0, 0, 0);
+
+ static final int TYPE_UNKNOWN = -1;
+ static final int TYPE_TREE = 0;
+ static final int TYPE_LIST = 1;
+ static final int TYPE_TABLE = 2;
+
+ /** Restore the renderer to a pristine state */
+ public void reset() {
+ parentFocused = false;
+ setCentered (false);
+ html = null;
+ indent = 0;
+ border = null;
+ setIcon (null);
+ setOpaque (false);
+ selected = false;
+ leadSelection = false;
+ prefSize = null;
+ type = TYPE_UNKNOWN;
+ renderStyle = HtmlRenderer.STYLE_CLIP;
+ setFont (UIManager.getFont("controlFont")); //NOI18N
+ setIconTextGap (3);
+ setEnabled (true);
+ border = null;
+
+ //Defensively ensure the insets haven't been messed with
+ EMPTY_INSETS.top = 0;
+ EMPTY_INSETS.left = 0;
+ EMPTY_INSETS.right = 0;
+ EMPTY_INSETS.bottom = 0;
+ }
+
+ public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean leadSelection,
+ int row, int column) {
+ reset();
+ configureFrom (value, table, selected, leadSelection);
+ type = TYPE_TABLE;
+ if (swingRendering && selected) {
+ setBackground (table.getSelectionBackground());
+ setBackground (table.getSelectionForeground());
+ setOpaque (true);
+ }
+ return this;
+ }
+
+ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded,
+ boolean leaf, int row, boolean leadSelection) {
+ reset();
+ configureFrom (value, tree, selected, leadSelection);
+ type = TYPE_TREE;
+ if (swingRendering && selected) {
+ setBackground (HtmlLabelUI.getBackgroundFor(this));
+ setForeground (HtmlLabelUI.getForegroundFor(this));
+ setOpaque (true);
+ }
+ return this;
+ }
+
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected,
+ boolean leadSelection) {
+
+ reset();
+ configureFrom (value, list, selected, leadSelection);
+ type = TYPE_LIST;
+ if (swingRendering && selected) {
+ setBackground (list.getSelectionBackground());
+ setForeground (list.getSelectionForeground());
+ setOpaque (true);
+ }
+ return this;
+ }
+
+ /** Generic code to set properties appropriately from any of the renderer
+ * fetching methods */
+ private void configureFrom (Object value, JComponent target,
+ boolean selected, boolean leadSelection) {
+
+ if (value == null) {
+ value = "";
+ }
+
+ setText (value instanceof String ? (String) value : value.toString());
+
+ setSelected(selected);
+ if (selected) {
+ setParentFocused(checkFocused(target));
+ } else {
+ setParentFocused(false);
+ }
+
+ setEnabled (target.isEnabled());
+
+ setLeadSelection (leadSelection);
+
+ setFont (target.getFont());
+ }
+
+ private boolean checkFocused(JComponent c) {
+ Component focused =
+ KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
+ boolean result = c == focused;
+ if (!result) {
+ result = c.isAncestorOf(focused);
+ }
+ return result;
+ }
+
+ public void setSelected (boolean val) {
+ selected = val;
+ }
+
+ public void setParentFocused (boolean val) {
+ parentFocused = val;
+ }
+
+ public void setLeadSelection (boolean val) {
+ leadSelection = val;
+ }
+
+ public void setCentered(boolean val) {
+ centered = val;
+ if (val) {
+ setIconTextGap (5);
+ }
+ if (swingRendering) {
+ if (val) {
+ setVerticalTextPosition(JLabel.BOTTOM);
+ setHorizontalAlignment(JLabel.CENTER);
+ setHorizontalTextPosition(JLabel.CENTER);
+ } else {
+ setVerticalTextPosition(JLabel.CENTER);
+ setHorizontalAlignment(JLabel.LEADING);
+ setHorizontalTextPosition(JLabel.TRAILING);
+ }
+ }
+ }
+
+ public void setIndent(int pixels) {
+ this.indent = pixels;
+ }
+
+ public void setHtml (boolean val) {
+ Boolean wasHtml = html;
+ String txt = getText();
+ html = val ? Boolean.TRUE : Boolean.FALSE;
+ if (swingRendering && html != wasHtml) {
+
+ //Ensure label UI gets updated and builds its little document tree...
+ firePropertyChange ("text", txt, getText());
+ }
+ }
+
+ public void setRenderStyle(int style) {
+ renderStyle = style;
+ }
+
+ int getRenderStyle () {
+ return renderStyle;
+ }
+
+ boolean isLeadSelection () {
+ return leadSelection;
+ }
+
+ boolean isCentered() {
+ return centered;
+ }
+
+ boolean isParentFocused() {
+ return parentFocused;
+ }
+
+ boolean isHtml() {
+ if (html == null) {
+ String s = getText();
+ html = checkHtml (s);
+ }
+ return html.booleanValue();
+ }
+
+ private Boolean checkHtml(String s) {
+ Boolean result;
+ if (s == null) {
+ result = Boolean.FALSE;
+ } else if (s.startsWith (""; //NOI18N
+ }
+ return s;
+ }
+
+ /** Overridden to do nothing under normal circumstances. If the boolean flag to not use the
+ * internal HTML renderer is in effect, this will fire changes normally */
+ protected final void firePropertyChange(String name, Object old, Object nue) {
+ if (swingRendering) {
+ if ("text".equals(name) && isHtml()) {
+ //Force in the HTML tags so the UI will set up swing HTML rendering appropriately
+ nue = getText();
+ }
+ super.firePropertyChange(name, old, nue);
+ }
+ }
+
+ public Border getBorder() {
+ Border result;
+ if (indent != 0 && swingRendering) {
+ result = BorderFactory.createEmptyBorder (0, indent, 0, 0);
+ } else {
+ result = border;
+ }
+ return result;
+ }
+
+ public void setBorder (Border b) {
+ Border old = border;
+ border = b;
+ if (swingRendering) {
+ firePropertyChange ("border", old, b);
+ }
+ }
+
+ public Insets getInsets() {
+ Insets result;
+
+ //Call getBorder(), not just read the field - if swingRendering, the border will be constructed, and the
+ //insets are what will make the indent property work; HtmlLabelUI doesn't need this, it just reads the
+ //insets property, but BasicLabelUI and its ilk do
+ Border b = getBorder();
+ if (b == null) {
+ result = EMPTY_INSETS;
+ } else {
+ result = b.getBorderInsets(this);
+ }
+ return result;
+ }
+
+ public void setEnabled (boolean b) {
+ //OptimizeIt shows about 12Ms overhead calling back to Component.enable(), so avoid it if possible
+ enabled = b;
+ if (swingRendering) {
+ super.setEnabled(b);
+ }
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public void updateUI() {
+ if (swingRendering) {
+ super.updateUI();
+ } else {
+ setUI (HtmlLabelUI.createUI(this));
+ }
+ }
+
+ /** Overridden to produce a graphics object even when isDisplayable() is
+ * false, so that calls to getPreferredSize() will return accurate
+ * dimensions (presuming the font and text are set correctly) even when
+ * not onscreen. */
+ public Graphics getGraphics() {
+ Graphics result = null;
+ if (isDisplayable()) {
+ result = super.getGraphics();
+ }
+ if (result == null) {
+ result = scratchGraphics();
+ }
+ return result;
+ }
+
+ private static Reference scratchGraphics = null;
+ /** Fetch a scratch graphics object for calculating preferred sizes while
+ * offscreen */
+ private static final Graphics scratchGraphics() {
+ Graphics result = null;
+ if (scratchGraphics != null) {
+ result = (Graphics) scratchGraphics.get();
+ if (result != null) {
+ result.setClip(null); //just in case somebody did something nasty
+ }
+ }
+ if (result == null) {
+ result = new BufferedImage (1, 1,
+ BufferedImage.TYPE_INT_RGB).getGraphics(); //XXX use GraphicsEnvironment?
+
+ scratchGraphics = new SoftReference (result);
+ }
+ return result;
+ }
+
+ public void setBounds (int x, int y, int w, int h) {
+ if (swingRendering) {
+ super.setBounds (x, y, w, h);
+ }
+ bounds.setBounds(x, y, w, h);
+ }
+
+ public int getWidth() {
+ return bounds.width;
+ }
+
+ public int getHeight() {
+ return bounds.height;
+ }
+
+ public Point getLocation() {
+ return bounds.getLocation();
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void validate() {
+ //do nothing
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void repaint (long tm, int x, int y, int w, int h) {
+ //do nothing
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void repaint() {
+ //do nothing
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void invalidate() {
+ //do nothing
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void revalidate() {
+ //do nothing
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void addAncestorListener (AncestorListener l) {
+ if (swingRendering) {
+ super.addAncestorListener (l);
+ }
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void addComponentListener (ComponentListener l) {
+ if (swingRendering) {
+ super.addComponentListener (l);
+ }
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void addContainerListener (ContainerListener l) {
+ if (swingRendering) {
+ super.addContainerListener (l);
+ }
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void addHierarchyListener (HierarchyListener l) {
+ if (swingRendering) {
+ super.addHierarchyListener (l);
+ }
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void addHierarchyBoundsListener (HierarchyBoundsListener l) {
+ if (swingRendering) {
+ super.addHierarchyBoundsListener (l);
+ }
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void addInputMethodListener (InputMethodListener l) {
+ if (swingRendering) {
+ super.addInputMethodListener (l);
+ }
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void addFocusListener (FocusListener fl) {
+ if (swingRendering) {
+ super.addFocusListener (fl);
+ }
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void addMouseListener (MouseListener ml) {
+ if (swingRendering) {
+ super.addMouseListener (ml);
+ }
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void addMouseWheelListener (MouseWheelListener ml) {
+ if (swingRendering) {
+ super.addMouseWheelListener (ml);
+ }
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void addMouseMotionListener (MouseMotionListener ml) {
+ if (swingRendering) {
+ super.addMouseMotionListener (ml);
+ }
+ }
+
+ /** Overridden to do nothing for performance reasons */
+ public void addVetoableChangeListener (VetoableChangeListener vl) {
+ if (swingRendering) {
+ super.addVetoableChangeListener (vl);
+ }
+ }
+
+ /** Overridden to do nothing for performance reasons, unless using standard swing rendering */
+ public void addPropertyChangeListener (String s, PropertyChangeListener l) {
+ if (swingRendering) {
+ super.addPropertyChangeListener (s, l);
+ }
+ }
+
+ public void addPropertyChangeListener (PropertyChangeListener l) {
+ if (swingRendering) {
+ super.addPropertyChangeListener (l);
+ }
+ }
+}
Index: openide/src/org/openide/explorer/propertysheet/BaseTable.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/BaseTable.java,v
retrieving revision 1.15
diff -u -u -r1.15 BaseTable.java
--- openide/src/org/openide/explorer/propertysheet/BaseTable.java 1 Mar 2004 01:26:19 -0000 1.15
+++ openide/src/org/openide/explorer/propertysheet/BaseTable.java 28 Apr 2004 17:20:04 -0000
@@ -552,6 +552,8 @@
Component result = renderer.getTableCellRendererComponent(this, value,
isSelected, false,
row, col);
+ if (result != null) {
+ /*
if (isSelected) {
result.setBackground(getSelectionBackground());
result.setForeground(getSelectionForeground());
@@ -565,6 +567,8 @@
result.setForeground(getForeground());
}
result.setFont(getFont());
+ */
+ }
return result;
}
Index: openide/src/org/openide/explorer/propertysheet/ButtonPanel.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/ButtonPanel.java,v
retrieving revision 1.16
diff -u -u -r1.16 ButtonPanel.java
--- openide/src/org/openide/explorer/propertysheet/ButtonPanel.java 4 Feb 2004 11:21:25 -0000 1.16
+++ openide/src/org/openide/explorer/propertysheet/ButtonPanel.java 28 Apr 2004 17:20:04 -0000
@@ -149,6 +149,12 @@
button.setRolloverIcon(PropUtils.getCustomButtonIcon());
}
+ public void setOpaque(boolean b) {
+ if (getInplaceEditor() != null) {
+ getInplaceEditor().getComponent().setOpaque(true);
+ }
+ }
+
public void setFont(Font f) {
if (comp != null) {
comp.setFont(f);
Index: openide/src/org/openide/explorer/propertysheet/ComboInplaceEditor.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/ComboInplaceEditor.java,v
retrieving revision 1.23
diff -u -u -r1.23 ComboInplaceEditor.java
--- openide/src/org/openide/explorer/propertysheet/ComboInplaceEditor.java 26 Apr 2004 18:10:44 -0000 1.23
+++ openide/src/org/openide/explorer/propertysheet/ComboInplaceEditor.java 28 Apr 2004 17:20:05 -0000
@@ -21,6 +21,7 @@
import javax.swing.plaf.ComboBoxUI;
import javax.swing.plaf.metal.MetalLookAndFeel;
import javax.swing.text.JTextComponent;
+import org.openide.awt.HtmlRenderer;
import org.openide.explorer.propertysheet.editors.EnhancedPropertyEditor;
/** A combo box inplace editor. Does a couple of necessary things:
@@ -60,7 +61,7 @@
* less borders & such */
public ComboInplaceEditor(boolean tableUI) {
if (tableUI) {
- putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
+ putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); //NOI18N
}
if (Boolean.getBoolean("netbeans.ps.combohack")) { //NOI18N
setLightWeightPopupEnabled(false);
@@ -73,6 +74,16 @@
updateUI();
}
}
+
+ /** Uses the shared instance of the fast HTML renderer */
+ public ListCellRenderer getRenderer() {
+ HtmlRenderer.Renderer result = HtmlRenderer.sharedRenderer();
+ //Popups should paint as the focused component even though the combo
+ //is what has focus
+ result.setParentFocused (true);
+ result.setIcon (null);
+ return result;
+ }
/** Overridden to add a listener to the editor if necessary, since the
* UI won't do that for us without a focus listener */
Index: openide/src/org/openide/explorer/propertysheet/IconPanel.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/IconPanel.java,v
retrieving revision 1.4
diff -u -u -r1.4 IconPanel.java
--- openide/src/org/openide/explorer/propertysheet/IconPanel.java 30 Dec 2003 16:58:40 -0000 1.4
+++ openide/src/org/openide/explorer/propertysheet/IconPanel.java 28 Apr 2004 17:20:05 -0000
@@ -174,6 +174,12 @@
}
}
+ public void setOpaque (boolean val) {
+ if (getInplaceEditor() != null) {
+ getInplaceEditor().getComponent().setOpaque(true);
+ }
+ }
+
/** Proxies the embedded inplace editor */
public javax.swing.JComponent getComponent() {
return this;
Index: openide/src/org/openide/explorer/propertysheet/PropUtils.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/PropUtils.java,v
retrieving revision 1.33
diff -u -u -r1.33 PropUtils.java
--- openide/src/org/openide/explorer/propertysheet/PropUtils.java 29 Feb 2004 03:56:21 -0000 1.33
+++ openide/src/org/openide/explorer/propertysheet/PropUtils.java 28 Apr 2004 17:20:05 -0000
@@ -25,7 +25,6 @@
import java.io.StringWriter;
import java.lang.reflect.*;
import java.util.*;
-import javax.accessibility.*;
import javax.swing.*;
import javax.swing.plaf.metal.*;
import java.text.MessageFormat;
@@ -323,27 +322,15 @@
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) {
- //OS-X 1.4.1 calling getGraphics() can cause cyclic repaints
- //if called while painting. Safer to use an offscreen, cached
- //resource, probably everywhere.
- Graphics result = null; //c.getGraphics();
- if (result == null) {
- //xxx this grabs the AWT tree lock
- /*
- result =
- GraphicsEnvironment.getLocalGraphicsEnvironment
- ().getDefaultScreenDevice().getDefaultConfiguration
- ().createCompatibleImage(1,1).getGraphics();
- */
- if (scratch == null) {
- scratch = new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB);
- }
- result = scratch.getGraphics();
+ if (scratchGraphics == null) {
+ scratchGraphics = new BufferedImage (1, 1, BufferedImage.TYPE_INT_RGB).getGraphics();
}
- return result;
+ return scratchGraphics;
}
Index: openide/src/org/openide/explorer/propertysheet/RendererFactory.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/RendererFactory.java,v
retrieving revision 1.10
diff -u -u -r1.10 RendererFactory.java
--- openide/src/org/openide/explorer/propertysheet/RendererFactory.java 25 Feb 2004 00:45:00 -0000 1.10
+++ openide/src/org/openide/explorer/propertysheet/RendererFactory.java 28 Apr 2004 17:20:05 -0000
@@ -21,10 +21,8 @@
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
-import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
-import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -47,8 +45,8 @@
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.event.ChangeListener;
-import javax.swing.table.DefaultTableCellRenderer;
import org.openide.ErrorManager;
+import org.openide.awt.HtmlRenderer;
import org.openide.nodes.Node.Property;
import org.openide.util.Utilities;
@@ -236,11 +234,11 @@
return lbl;
}
- public JLabel getStringRenderer() {
+ public JComponent getStringRenderer() {
StringRenderer result = stringRenderer();
result.clear();
result.setEnabled(true);
- return (JLabel) result;
+ return result;
}
private JComponent prepareRadioButtons(PropertyEditor editor, PropertyEnv env) {
@@ -426,14 +424,7 @@
/** Overridden only fire those properties needed */
protected void firePropertyChange(String name, Object old, Object nue) {
- //gtk L&F needs these, although bg and fg don't work on it in 1.4.2
- /*
- if ("foreground".equals(name) ||
- "background".equals(name) || "font".equals(name) ||
- "editable".equals(name) || "enabled".equals(name)) { //NOI18N
- super.firePropertyChange(name, old, nue);
- }
- */
+ //firing all changes for now - breaks text painting on OS-X
super.firePropertyChange(name,old,nue);
}
@@ -495,7 +486,7 @@
/** A renderer for string properties, which can also delegate to the
* property editor's paint()
method if possible. */
- private static final class StringRenderer extends DefaultTableCellRenderer implements InplaceEditor {
+ private static final class StringRenderer extends JLabel implements InplaceEditor {
private PropertyEditor editor=null;
private PropertyEnv env=null;
private boolean tableUI=false;
@@ -517,29 +508,39 @@
return enabled;
}
- public Dimension getPreferredSize() {
- Graphics g = PropUtils.getScratchGraphics(this);
- FontMetrics fm = g.getFontMetrics(getFont());
- int w;
- if (getText() != null) {
- //avoid NPE in sun.awt.font.FontDesignMetrics.stringWidth():281
- w = fm.stringWidth(getText()) + 4;
- } else {
- w = PropUtils.getMinimumPropPanelWidth();
+ /** Overridden to do nothing */
+ protected void firePropertyChange(String name, Object old, Object nue) {
+ //do nothing
}
- int h = fm.getHeight() + 2;
- w = Math.max(w,PropUtils.getMinimumPropPanelWidth());
- h = Math.max(h,PropUtils.getMinimumPropPanelHeight());
- Dimension result = new Dimension(w,h);
- if (getIcon() != null) {
- result.height = Math.max (result.height, getIcon().getIconHeight());
- result.width += getIcon().getIconHeight();
- }
- if (getBorder() != null) {
- Insets i = getBorder().getBorderInsets(this);
- result.width += i.right+i.left;
- result.height += i.top+i.bottom;
+
+ public void validate() {
+ //do nothing
}
+
+ public void invalidate() {
+ //do nothing
+ }
+
+ public void revalidate() {
+ //do nothing
+ }
+
+ public void repaint() {
+ //do nothing
+ }
+
+ public void repaint (long tm, int x, int y, int w, int h) {
+ //do nothing
+ }
+
+ public Dimension getPreferredSize() {
+ Dimension result = super.getPreferredSize();
+ result.width = Math.max(result.width,
+ PropUtils.getMinimumPropPanelWidth());
+
+ result.height = Math.max(result.height,
+ PropUtils.getMinimumPropPanelHeight());
+
return result;
}
@@ -553,7 +554,18 @@
if (editor != null && editor.isPaintable()) {
delegatedPaint (g);
} else {
- super.paint (g);
+ JLabel lbl = (JLabel) HtmlRenderer.sharedRenderer();
+ ((HtmlRenderer.Renderer) lbl).reset();
+ lbl.setEnabled (isEnabled());
+ lbl.setText (getText());
+ lbl.setIcon (getIcon());
+ lbl.setIconTextGap (getIconTextGap());
+ lbl.setBounds (getBounds());
+ lbl.setOpaque (true);
+ lbl.setBackground (getBackground());
+ lbl.setForeground (getForeground());
+ ((HtmlRenderer.Renderer) lbl).setRenderStyle(HtmlRenderer.STYLE_TRUNCATE);
+ lbl.paint (g);
}
clear();
}
@@ -572,6 +584,8 @@
b.paintBorder(this, g, 0, 0, getWidth(), getHeight());
}
Rectangle r = getBounds();
+ //XXX May be the source of Rochelle's multiple rows of error
+ //marking misalignment problem...(I do not jest)
r.x = getWidth() > 16 ? editor instanceof Boolean3WayEditor ? 0 : 3 : 0; //align text with other renderers
r.width -= getWidth() > 16 ? editor instanceof Boolean3WayEditor ? 0 : 3 : 0; //align text with other renderers
r.y = 0;
@@ -584,19 +598,14 @@
public void clear() {
editor = null;
env = null;
- setText(""); //NOI18N
setIcon(null);
setOpaque(true);
}
+ private Object value = null;
public void setValue(Object o) {
- super.setValue(o);
- }
-
- public void setText(String s) {
- //XXX hotfix for the form editor until the HTML renderer can
- //be put into trunk - per Trung's request
- super.setText(stripHTML(s));
+ value = o;
+ setText (value instanceof String ? (String) value : value != null ? value.toString() : null);
}
public void connect(PropertyEditor p, PropertyEnv env) {
@@ -664,7 +673,7 @@
}
public boolean supportsTextEntry() {
- return true;
+ return false;
}
/** Overridden to do nothing */
@@ -675,38 +684,6 @@
protected void fireStateChanged() {
}
- /** Overridden to do nothing */
- protected void firePropertyChange(String name, Object old, Object nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, boolean old, boolean nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, int old, int nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, byte old, byte nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, char old, char nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, double old, double nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, float old, float nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, short old, short nue) {
- }
-
public void addActionListener(ActionListener al) {
//do nothing
}
@@ -739,34 +716,6 @@
super.firePropertyChange(name, old, nue);
}
}
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, boolean old, boolean nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, int old, int nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, byte old, byte nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, char old, char nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, double old, double nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, float old, float nue) {
- }
-
- /** Overridden to do nothing */
- public void firePropertyChange(String name, short old, short nue) {
- }
}
private static final class RadioRenderer extends RadioInplaceEditor {
@@ -1051,68 +1000,6 @@
public boolean supportsTextEntry() {
return false;
- }
-
- }
-
- public static boolean requiresSwingPainting (Component c) {
- if (!(c instanceof InplaceEditor)) {
- return true;
- }
- InplaceEditor ine = (InplaceEditor) c;
- if (ine instanceof ButtonPanel) {
- //ine = ((ButtonPanel) ine).getInplaceEditor();
- return ((ButtonPanel) ine).getInplaceEditor() instanceof JComboBox;
- }
- if (ine instanceof IconPanel) {
- ine = ((IconPanel) ine).getInplaceEditor();
- }
-
- if (ine.getComponent() instanceof JComboBox) {
- return true;
- }
-
- if (ine instanceof CheckboxInplaceEditor || ine instanceof RadioInplaceEditor ||
- ine instanceof StringRenderer || ine instanceof ExceptionRenderer ||
- ine instanceof Boolean3WayEditor.Boolean3Inplace || ine instanceof StringInplaceEditor) {
- return false;
- }
- return true;
- }
-
- private static boolean isHTML(String s) {
- if (s == null) return false;
- boolean result = s.startsWith("") || s.startsWith(""); //NOI18N
- return result;
- }
-
- private static String stripHTML(String s) {
- if (s == null) {
- return s;
- }
- if (isHTML(s)) {
- StringBuffer result = new StringBuffer(s.length());
- char[] c = s.toCharArray();
- boolean inTag = false;
- for (int i=0; i < c.length; i++) {
- //XXX need to handle entity includes
- boolean wasInTag = inTag;
- if (!inTag) {
- if (c[i] == '<') {
- inTag = true;
- }
- } else {
- if (c[i] == '>') {
- inTag = false;
- }
- }
- if (!inTag && wasInTag == inTag) {
- result.append(c[i]);
- }
- }
- return result.toString();
- } else {
- return s;
}
}
}
Index: openide/src/org/openide/explorer/propertysheet/SheetCellRenderer.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/SheetCellRenderer.java,v
retrieving revision 1.8
diff -u -u -r1.8 SheetCellRenderer.java
--- openide/src/org/openide/explorer/propertysheet/SheetCellRenderer.java 21 Jan 2004 20:36:53 -0000 1.8
+++ openide/src/org/openide/explorer/propertysheet/SheetCellRenderer.java 28 Apr 2004 17:20:05 -0000
@@ -17,19 +17,18 @@
package org.openide.explorer.propertysheet;
import java.awt.*;
-import java.awt.event.ActionEvent;
import java.beans.FeatureDescriptor;
import javax.swing.*;
import javax.swing.table.TableCellRenderer;
-import javax.swing.table.DefaultTableCellRenderer;
+import org.openide.awt.HtmlRenderer;
import org.openide.nodes.Node.*;
+
/** An implementation of SheetCellRenderer that wraps custom InplaceEditors
* to efficiently render properties.
*
* @author Tim Boudreau
*/
final class SheetCellRenderer implements TableCellRenderer {
- static SetRenderer setRenderer = null;
private RendererFactory factory=null;
private boolean tableUI;
boolean includeMargin=false;
@@ -52,14 +51,6 @@
rbMax = i;
}
- private SetRenderer getSetRenderer() {
- if (setRenderer == null) {
- setRenderer = new SetRenderer();
- }
- setRenderer.dontPaint = true;
- return setRenderer;
- }
-
public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) {
FeatureDescriptor fd = (FeatureDescriptor) value;
Component result;
@@ -68,21 +59,31 @@
selected |= hasFocus && table.getSelectedRow() == row;
if (fd instanceof PropertySet) {
- SetRenderer sr = getSetRenderer();
- sr.setText(fd.getDisplayName());
- sr.setExpanded(((SheetTable) table).getPropertySetModel().isExpanded(fd));
- result = sr;
+ return null;
} else {
if (column == 0) {
- JLabel lbl = factory().getStringRenderer();
- lbl.setText(fd.getDisplayName());
+ String txt = ((Property) fd).getHtmlDisplayName();
+ boolean isHtml = txt != null;
+ if (!isHtml) {
+ txt = fd.getDisplayName();
+ }
+ JLabel lbl = HtmlRenderer.sharedLabel();
+ HtmlRenderer.Renderer ren = (HtmlRenderer.Renderer) lbl;
+ ren.setRenderStyle(HtmlRenderer.STYLE_TRUNCATE);
+
+ lbl.setText (txt);
+ ren.setHtml (isHtml);
+ lbl.setOpaque (selected);
+ if (selected) {
+ lbl.setBackground (table.getSelectionBackground());
+ lbl.setForeground (table.getSelectionForeground());
+ lbl.setOpaque (true);
+ }
+
if (includeMargin) {
- lbl.setBorder(BorderFactory.createEmptyBorder(
- 0, PropUtils.getMarginWidth() + 2, 0, 1));
+ ren.setIndent (PropUtils.getMarginWidth() + 2);
} else {
- //Use a 2 pixel margin so it's not flush
- lbl.setBorder(BorderFactory.createEmptyBorder(0,
- PropUtils.getTextMargin(), 0, 0));
+ ren.setIndent (PropUtils.getTextMargin());
}
//Support for name marking with icon requested by form editor
@@ -91,21 +92,22 @@
lbl.setIcon((Icon) o);
} else if (o instanceof Image) {
lbl.setIcon(new ImageIcon((Image) o));
- } else {
- lbl.setIcon(null);
}
result = lbl;
} else {
result = factory().getRenderer((Property) fd);
- //Use a 2 pixel margin so it's not flush
- /*
- ((JComponent)result).setBorder(BorderFactory.createEmptyBorder(0,
- PropUtils.getTextMargin(), 0, 0));
- */
+ if (selected) {
+ result.setBackground (table.getSelectionBackground());
+ result.setForeground (table.getSelectionForeground());
+ ((JComponent)result).setOpaque(true);
+ } else {
+ result.setBackground (table.getBackground());
+ result.setForeground (table.getForeground());
+ ((JComponent)result).setOpaque(false);
+ }
}
}
-// result.setFont(table.getFont());
return result;
}
@@ -114,86 +116,5 @@
factory = new RendererFactory(true);
}
return factory;
- }
-
- /** A renderer for property sets, which should be rendered double-width.
- * This renderer intentionally does nothing when called in the normal
- * paint loop; if the boolean field dontPaint
is set
- * to true (calling getTableCellRendererComponent will set this to true)
- * its paint method is a no-op. The table will retrieve this component
- * and paint it across two columns after the rest of the paint cycle
- * is completed.
- */
- static class SetRenderer extends DefaultTableCellRenderer {
-
- public boolean dontPaint = true;
- int textY = -1;
- int iconY = -1;
-
- /** Discard/recalc UI dependent values */
- public void updateUI() {
- super.updateUI();
- iconY=-1;
- textY=-1;
- }
-
- private boolean expanded;
- public void setExpanded (boolean val) {
- expanded = val;
- }
-
- public Icon getIcon() {
- return expanded ? PropUtils.getExpandedIcon() :
- PropUtils.getCollapsedIcon();
- }
-
- /** Calculate y position to center text and icon vertically */
- private void calcYPos(FontMetrics fm) {
- int h = getHeight();
- int ih = getIcon().getIconHeight();
- int fh = fm.getHeight();
- if (fh >= h) {
- textY = 0 + fm.getAscent();
- } else {
- textY = ((h - fh) / 2) + fm.getAscent();
- }
-
- if (ih >= h) {
- iconY = 0;
- } else {
- iconY = (h - ih) / 2;
- }
- }
-
- /** Paint the component, if the dontPaint
field is
- * false. In the standard rendering pass of JTable, this will
- * be true; the table will iterate the available sets and paint
- * them across the entire width of the table after the rest has
- * been painted.
- */
- public void paint(Graphics g) {
- if (!dontPaint) {
- if (iconY == -1) calcYPos(g.getFontMetrics(getFont()));
- g.setFont(getFont());
- g.setColor(getBackground());
- g.fillRect(0,0,getWidth(),getHeight());
- Icon ic = getIcon();
- ic.paintIcon(this, g, PropUtils.getIconMargin(), iconY);
- g.setColor(getForeground());
- g.drawString(getText(), PropUtils.getIconMargin()+ PropUtils.getMarginWidth() + 2, //XXX text icon gap
- textY);
- }
- }
- /** Overridden to do nothing */
- protected void fireActionPerformed(ActionEvent ae) {
- }
-
- /** Overridden to do nothing */
- protected void fireStateChanged() {
- }
-
- /** Overridden to do nothing */
- protected void firePropertyChange(String name, Object old, Object nue) {
- }
}
}
Index: openide/src/org/openide/explorer/propertysheet/SheetTable.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/SheetTable.java,v
retrieving revision 1.38
diff -u -u -r1.38 SheetTable.java
--- openide/src/org/openide/explorer/propertysheet/SheetTable.java 22 Apr 2004 13:10:25 -0000 1.38
+++ openide/src/org/openide/explorer/propertysheet/SheetTable.java 28 Apr 2004 17:20:06 -0000
@@ -20,6 +20,7 @@
import javax.swing.table.*;
import javax.swing.event.*;
import org.openide.ErrorManager;
+import org.openide.awt.HtmlRenderer;
import org.openide.util.NbBundle;
import org.openide.nodes.Node.Property;
import org.openide.nodes.Node.PropertySet;
@@ -369,7 +370,7 @@
return;
}
- int max = min + getVisibleRowCount(); //psm.getCount();
+ int max = min + getVisibleRowCount();
for (int i=min; i < max; i++) {
FeatureDescriptor fd = psm.getFeatureDescriptor(i);
@@ -405,11 +406,12 @@
}
public Component prepareRenderer(TableCellRenderer renderer, int row, int col) {
+ Component result = super.prepareRenderer(renderer, row, col);
if (row < 0 || row >= getRowCount()) {
- return super.prepareRenderer(renderer, row, col);
+ return result;
}
Object value = getValueAt(row, col);
- Component result = super.prepareRenderer(renderer, row, col);
+ /*
if (value instanceof PropertySet) {
boolean selected = row == getSelectedRow();
result.setBackground (selected ?
@@ -426,49 +428,67 @@
result.setEnabled(writable);
}
}
+ */
+ if (result != null && value instanceof Property && col == 1) {
+ result.setEnabled (((Property) value).canWrite());
+ }
return result;
}
/** Paint the expandable sets. These are painted double width,
* across the entire width of the table. */
private void paintExpandableSets (Graphics g) {
- //special paint handling for double-wide expando sets
- //XXX build an array of appropriate component indices in getCellRenderer and don't iterate
- //the entire set of rows!
- int max = this.getRowCount();
- for (int i = 0; i < max; i++) {
- //find the items that are property sets
- if (!(getSheetModel().getPropertySetModel().isProperty(i))) {
- //get the rectangle and double its width
- Rectangle r = getCellRect (i, 0, false);
- if (r.y > getHeight()) {
- //Don't paint unnecessarily
- return;
- }
- r.width = getWidth();
- if (g.hitClip (r.x, r.y, r.width, r.height)) {
- //get the appropriate renderer
- TableCellRenderer tcr = getCellRenderer (i, 0);
- boolean selected = getSelectedRow()==i && isKnownComponent(
+ int start = getFirstVisibleRow();
+ int end = getVisibleRowCount();
+
+ Insets ins = getInsets();
+
+ boolean canBeSelected = isKnownComponent(
KeyboardFocusManager.getCurrentKeyboardFocusManager().
getPermanentFocusOwner());
- Component c = tcr.getTableCellRendererComponent(this,
- this.getValueAt (i,0), selected, false, i, 0);
- //A minor optimization - when the standard table painting
- //happens, the set renderer will simply do nothing. This
- //field controls whether its paint method is a no-op.
- ((SheetCellRenderer.SetRenderer) c).dontPaint = false;
- c.setBackground (selected ?
- PropUtils.getSelectedSetRendererColor() :
- PropUtils.getSetRendererColor());
-
- c.setForeground (selected ?
- PropUtils.getSelectedSetForegroundColor() :
- PropUtils.getSetForegroundColor());
+ for (int i=0; i < end; i++) {
+ int idx = start + i;
+ Object value = getValueAt (idx, 0);
+
+ if (value instanceof PropertySet) {
- paintComponent(g, c, r.x, r.y, r.width, r.height);
+ Rectangle r = getCellRect (idx, 0, false);
+ r.x = ins.left;
+ r.width = getWidth() - (ins.left + ins.right);
+ if (g.hitClip (r.x, r.y, r.width, r.height)) {
+ PropertySet ps = (PropertySet) value;
+
+ String txt = ps.getHtmlDisplayName();
+ boolean isHtml = txt != null;
+ if (!isHtml) {
+ txt = ps.getDisplayName();
+ }
+ boolean selected = canBeSelected && getSelectedRow() == idx;
+
+ HtmlRenderer.Renderer ren = HtmlRenderer.sharedRenderer();
+ JComponent painter = (JComponent) ren.getTableCellRendererComponent(this, txt, selected, selected, idx, 0);
+
+ ren.setHtml (isHtml);
+
+ ren.setIconTextGap (2);
+
+ ren.setIcon(
+ getPropertySetModel().isExpanded(ps) ?
+ PropUtils.getExpandedIcon() : PropUtils.getCollapsedIcon()
+ );
+
+ if (!selected) {
+ painter.setBackground (PropUtils.getSetRendererColor());
+ painter.setForeground (PropUtils.getSetForegroundColor());
+ } else {
+ painter.setBackground (PropUtils.getSelectedSetRendererColor());
+ painter.setForeground (PropUtils.getSelectedSetForegroundColor());
+ }
+ painter.setOpaque (true);
+
+ paintComponent (g, painter, r.x, r.y, r.width, r.height);
}
}
}
Index: openide/src/org/openide/explorer/view/ChoiceView.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/view/ChoiceView.java,v
retrieving revision 1.14
diff -u -u -r1.14 ChoiceView.java
--- openide/src/org/openide/explorer/view/ChoiceView.java 3 Dec 2002 14:11:38 -0000 1.14
+++ openide/src/org/openide/explorer/view/ChoiceView.java 28 Apr 2004 17:20:06 -0000
@@ -51,7 +51,7 @@
/** Initialize view. */
private void initializeChoice () {
- setRenderer (new NodeRenderer ());
+ setRenderer (NodeRenderer.sharedInstance());
setModel (model = createModel ());
Index: openide/src/org/openide/explorer/view/IconView.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/view/IconView.java,v
retrieving revision 1.19
diff -u -u -r1.19 IconView.java
--- openide/src/org/openide/explorer/view/IconView.java 27 Feb 2003 23:40:39 -0000 1.19
+++ openide/src/org/openide/explorer/view/IconView.java 28 Apr 2004 17:20:06 -0000
@@ -27,6 +27,10 @@
- improve cell renderer (two lines of text or hints)
- better behaviour during scrolling (ListPane)
- external selection bug (BUG ID: 01110034)
+ -
+ - XXX if doing anything with this class other than deleting it, rewrite it to use a JTable - that would be
+ - much more sensible and scalable. -Tim
+ -
*/
/** A view displaying icons.
@@ -67,7 +71,7 @@
return null;
}
};
- list.setCellRenderer (new NodeRenderer (true));
+ list.setCellRenderer (NodeRenderer.sharedInstance());
return list;
}
Index: openide/src/org/openide/explorer/view/ListViewDropSupport.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/view/ListViewDropSupport.java,v
retrieving revision 1.16
diff -u -u -r1.16 ListViewDropSupport.java
--- openide/src/org/openide/explorer/view/ListViewDropSupport.java 21 Nov 2003 09:05:25 -0000 1.16
+++ openide/src/org/openide/explorer/view/ListViewDropSupport.java 28 Apr 2004 17:20:06 -0000
@@ -53,9 +53,6 @@
/** The component we are supporting with drop support */
protected JList list;
- /** For managing visual appearance of JList cells. */
- protected NodeRenderer.List cellRenderer;
-
// Operations
public ListViewDropSupport (ListView view, JList list) {
this( view, list, true );
@@ -66,7 +63,6 @@
{
this.view = view;
this.list = list;
- //cellRenderer = (NodeListCellRenderer)list.getCellRenderer();
this.dropTargetPopupAllowed = dropTargetPopupAllowed;
}
@@ -265,13 +261,4 @@
}
return dropTarget;
}
-
- /** Safe getter for the cell renderer of asociated list */
- NodeRenderer.List getCellRenderer () {
- if (cellRenderer == null)
- cellRenderer = (NodeRenderer.List)list.getCellRenderer();
- return cellRenderer;
- }
-
-
}
Index: openide/src/org/openide/explorer/view/NodeRenderer.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/view/NodeRenderer.java,v
retrieving revision 1.30
diff -u -u -r1.30 NodeRenderer.java
--- openide/src/org/openide/explorer/view/NodeRenderer.java 22 Apr 2004 13:10:26 -0000 1.30
+++ openide/src/org/openide/explorer/view/NodeRenderer.java 28 Apr 2004 17:20:06 -0000
@@ -20,29 +20,17 @@
import java.awt.Image;
import java.awt.KeyboardFocusManager;
-import javax.swing.BorderFactory;
-import javax.swing.ImageIcon;
-import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JTree;
import javax.swing.ListCellRenderer;
-import javax.swing.UIManager;
-import javax.swing.border.Border;
-import javax.swing.border.LineBorder;
-import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;
-import java.beans.BeanInfo;
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
-import java.util.WeakHashMap;
-import javax.swing.JRootPane;
+import javax.swing.Icon;
import javax.swing.SwingUtilities;
-import javax.swing.plaf.ColorUIResource;
-import org.openide.ErrorManager;
+import org.openide.awt.HtmlRenderer;
+import org.openide.awt.ListPane;
import org.openide.nodes.Node;
-import org.openide.util.Utilities;
/** Default renderer for nodes. Can paint either Nodes directly or
@@ -50,44 +38,39 @@
*
* @see org.openide.nodes.Node
*
- * @author Jaroslav Tulach
+ * @author Jaroslav Tulach, Tim Boudreau
*/
public class NodeRenderer extends Object
implements TreeCellRenderer, ListCellRenderer {
+
/** Shared instance of NodeRenderer
. */
- private static NodeRenderer sharedInstance;
+ //no point in lazy initialization, it's the only thing this class
+ //is used for
+ private static NodeRenderer sharedInstance = new NodeRenderer();
/** Flag indicating if to use big icons. */
- private boolean bigIcons;
-
- static Border emptyBorder = BorderFactory.createEmptyBorder (1, 1, 1, 1);
+ private boolean bigIcons = false;
/** Creates default renderer. */
public NodeRenderer () {
}
-
/** Creates renderer.
* @param bigIcons use big icons if possible
+ * @deprecated bigIcons was only used by IconView, and not used by that anymore. Use sharedInstance()
+ * to get an instance of NodeRenderer.
*/
public NodeRenderer (boolean bigIcons) {
this.bigIcons = bigIcons;
}
- /** Gets for one singleton sharedInstance
. */
+ /** Get the singleton instance used by all explorer views. */
public static NodeRenderer sharedInstance () {
- if (sharedInstance == null) {
- sharedInstance = new NodeRenderer ();
- }
return sharedInstance;
}
- //
- // Rendering methods
- //
-
/** Finds the component that is capable of drawing the cell in a tree.
* @param value value can be either Node
* or a VisualizerNode
.
@@ -98,203 +81,105 @@
boolean sel, boolean expanded,
boolean leaf, int row, boolean hasFocus
) {
- return getTree().getTreeCellRendererComponent (
- tree, value, sel, expanded, leaf, row, hasFocus
- );
- }
-
-
- /** This is the only method defined by ListCellRenderer
. We just
- * reconfigure the Jlabel
each time we're called.
- */
- public Component getListCellRendererComponent (
- JList list,
- Object value, // value to display
- int index, // cell index
- boolean isSelected, // is the cell selected
- boolean cellHasFocus // the list and the cell have the focus
- ) {
- // accepting either Node or Visualizers
- VisualizerNode vis = (value instanceof Node) ?
- VisualizerNode.getVisualizer (null, (Node)value)
- :
- (VisualizerNode)value;
+ VisualizerNode vis = findVisualizerNode (value);
- if (vis == null) {
- vis = VisualizerNode.EMPTY;
+ String text = vis.getHtmlDisplayName();
+ boolean isHtml = text != null;
+ if (!isHtml) {
+ text = vis.getDisplayName();
}
- ListCellRenderer r = bigIcons ? (ListCellRenderer)getPane() : getList();
-
- Component result = r.getListCellRendererComponent (
- list, vis, index, isSelected, cellHasFocus
- );
- result.setFont(list.getFont());
- return result;
- }
+ HtmlRenderer.Renderer ren = HtmlRenderer.sharedRenderer();
- // ********************
- // Support for dragging
- // ********************
-
- /** Value of the cell with 'drag under' visual feedback */
- private static VisualizerNode draggedOver;
-
-
- /** DnD operation enters. Update look and feel to the 'drag under' state.
- * @param value the value of cell which should have 'drag under' visual feedback
- */
- static void dragEnter (Object dragged) {
- draggedOver = (VisualizerNode)dragged;
- }
-
- /** DnD operation exits. Revert to the normal look and feel. */
- static void dragExit () {
- draggedOver = null;
- }
-
-
- // ********************
- // Cache for ImageIcons
- // ********************
+ //Get our result value - really it is ren, but this call causes
+ //it to configure itself with the passed values
+ Component result = ren.getTreeCellRendererComponent(
+ tree, text, sel, expanded, leaf, row, hasFocus);
- /** default icon to use when none is present */
- private static final String DEFAULT_ICON = "org/openide/resources/defaultNode.gif"; // NOI18N
+ ren.setHtml(isHtml);
- /** loaded default icon */
- private static ImageIcon defaultIcon;
+ //Do our additional configuration - set up the icon and possibly
+ //do some hacks to make it look focused for TreeTableView
+ configureFrom (ren, tree, expanded, sel, vis);
- /** of icons used (Image, IconImage)*/
- private static final WeakHashMap map = new WeakHashMap ();
-
- /** Loades default icon if not loaded. */
- static ImageIcon getDefaultIcon () {
- if (defaultIcon == null) {
- defaultIcon = new ImageIcon(Utilities.loadImage(DEFAULT_ICON));
- }
-
- return defaultIcon;
+ return result;
}
- /** Finds imager for given resource.
- * @param image image to get
- * @return icon for the image
+ /** This is the only method defined by ListCellRenderer
. We just
+ * reconfigure the Jlabel
each time we're called.
*/
- static ImageIcon getIcon (Image image) {
- Reference ref = (Reference)map.get (image);
-
- ImageIcon icon = ref == null ? null : (ImageIcon)ref.get ();
- if (icon != null) {
- return icon;
- }
-
- icon = new ImageIcon (image);
- map.put (image, new WeakReference (icon));
-
- return icon;
- }
-
- //
- // Renderers
- //
-
-
- private static NodeRenderer.Tree tree = null;
-
- private synchronized static NodeRenderer.Tree getTree () {
- if (tree == null)
- tree = new NodeRenderer.Tree ();
- return tree;
- }
-
- private static NodeRenderer.Pane pane = null;
-
- private synchronized static NodeRenderer.Pane getPane() {
- if (pane == null)
- pane = new NodeRenderer.Pane ();
- return pane;
- }
-
- private static NodeRenderer.List list = null;
-
- private synchronized static NodeRenderer.List getList() {
- if (list == null)
- list = new NodeRenderer.List ();
- return list;
- }
+ public Component getListCellRendererComponent (
+ JList list, Object value, int index, boolean sel,
+ boolean cellHasFocus) {
+ VisualizerNode vis = findVisualizerNode(value);
- /** Tree cell renderer. Accepts only VisualizerNode
values. */
- final static class Tree extends JLabel implements TreeCellRenderer {
- /** generated Serialized Version UID */
- static final long serialVersionUID = -183570483117501696L;
-
- private boolean treeHasFocus = false;
-
- private boolean hasFocus = false;
- private boolean selected = false;
-
- public Tree() {
- }
-
- public boolean isOpaque() {
- return false;
- }
+ String text = vis.getHtmlDisplayName();
+ boolean isHtml = text != null;
+ if (!isHtml) {
+ text = vis.getDisplayName();
+ }
+
+ HtmlRenderer.Renderer ren = HtmlRenderer.sharedRenderer();
+
+ //Get our result value - really it is ren, but this call causes
+ //it to configure itself with the passed values
+ Component result = ren.getListCellRendererComponent(
+ list, text, index, sel, cellHasFocus || value == draggedOver);
+ ren.setHtml(isHtml);
+
+ //Do our additional configuration - set up the icon and possibly
+ //do some hacks to make it look focused for TreeTableView
+ int iconWidth = configureFrom (ren, list, false, sel, vis);
+
+ boolean bigIcons = this.bigIcons || list instanceof ListPane;
+
+ if (bigIcons) {
+ ren.setCentered(true);
+ } else {
+ //Indent elements in a ListView/ChoiceView relative to their position
+ //in the node tree. Only does anything if you've subclassed and
+ //overridden createModel(). Does anybody do that?
+ if (list.getModel() instanceof NodeListModel && ((NodeListModel) list.getModel()).getDepth() > 1) {
+ int indent = iconWidth *
+ NodeListModel.findVisualizerDepth (list.getModel (), vis);
- public void setBackground (Color c) {
- bg = c;
- }
-
- private Color bg = Color.WHITE;
-
- public void paint (java.awt.Graphics g) {
- if (!isSynth && selected) {
- Color c = g.getColor();
- g.setColor (bg);
- g.fillRect (0,0,getWidth(), getHeight());
+ ren.setIndent (indent);
}
- super.paint(g);
}
+ return result;
+ }
- /** @return Rendered cell component */
- public Component getTreeCellRendererComponent(
- JTree tree, Object value,
- boolean sel, boolean expanded,
- boolean leaf, int row, boolean hasFocus
- ) {
- setEnabled(tree.isEnabled());
- // accepts only VisualizerNode
- VisualizerNode vis = (VisualizerNode)value;
+ /** Utility method which performs configuration which is common to all of the renderer
+ * implementations - sets the icon and focus properties on the renderer
+ * from the VisualizerNode.
+ *
+ */
+ private int configureFrom (HtmlRenderer.Renderer ren, Container
+ target, boolean useOpenedIcon, boolean sel, VisualizerNode vis) {
- Image iconImg;
- if (expanded) {
- iconImg = vis.node.getOpenedIcon(BeanInfo.ICON_COLOR_16x16);
- } else {
- iconImg = vis.node.getIcon(BeanInfo.ICON_COLOR_16x16);
- }
+ Icon icon = vis.getIcon(useOpenedIcon, bigIcons);
- // bugfix #28515, check if getIcon contract isn't broken
- if (iconImg == null) {
- String method = expanded ? "getOpenedIcon" : "getIcon"; // NOI18N
- ErrorManager.getDefault ().log (ErrorManager.WARNING, "Node \"" + vis.node.getName () + // NOI18N
- "\" [" +vis.node.getClass().getName()+ "] cannot return null from " + method + "(). See Node." + method + " contract."); // NOI18N
- } else {
- ImageIcon nodeicon = NodeRenderer.getIcon(iconImg);
-
- setIconTextGap (4 - nodeicon.getIconWidth()
- + ( nodeicon.getIconWidth() > 24 ? nodeicon.getIconWidth() : 24 ) );
- setIcon(nodeicon);
- }
-
- setText(vis.getDisplayName ());
-
- // provide "drag under" feedback if DnD operation is active // NOI18N
- if (vis == draggedOver) {
- sel = true;
- }
-
- this.hasFocus = hasFocus;
- selected = sel;
+ if (icon.getIconWidth() > 0) {
+ //Max annotated icon width is 24, so to have all the text and all
+ //the icons come out aligned, set the icon text gap to the difference
+ //plus a two pixel margin
+ ren.setIconTextGap (24 - icon.getIconWidth());
+ } else {
+ //If the icon width is 0, fill the space and add in
+ //the extra two pixels so the node names are aligned (btw, this
+ //does seem to waste a frightful amount of horizontal space in
+ //a tree that can use all it can get)
+ ren.setIndent (26);
+ }
+
+ ren.setIcon (icon);
+
+ //Do the kooky focus dance so the tree that is a renderer for
+ //TreeTableView will paint as though focused even though it's
+ //never onscreen.
+ if (target instanceof JTree) {
+ boolean treeHasFocus = false;
if (sel) {
//Find out who has focus
@@ -302,242 +187,60 @@
KeyboardFocusManager.getCurrentKeyboardFocusManager().
getPermanentFocusOwner();
- treeHasFocus = focusOwner == tree ||
- tree.isAncestorOf(focusOwner) || focusOwner instanceof TreeTable;
+ treeHasFocus = focusOwner == target ||
+ target.isAncestorOf(focusOwner) || focusOwner
+ instanceof TreeTable;
if (!treeHasFocus) {
- TreeTable tt = (TreeTable)SwingUtilities.getAncestorOfClass(TreeTable.class, focusOwner);
+ TreeTable tt = (TreeTable)SwingUtilities.getAncestorOfClass(
+ TreeTable.class, focusOwner);
+
if (tt != null) {
treeHasFocus = focusOwner != null &&
tt.getDefaultRenderer(TreeTableModelAdapter.class)
- == tree;
+ == target;
}
}
- setForeground(treeHasFocus || isSynth ?
- UIManager.getColor("Tree.selectionForeground") :
- getNoFocusSelectionForeground()); //NOI18N
-
- setBackground(treeHasFocus || isSynth ?
- UIManager.getColor("Tree.selectionBackground") :
- getNoFocusSelectionBackground());
-
- } else {
- setForeground(tree.getForeground());
- setBackground(tree.getBackground());
}
-
- return this;
- }
-
- protected void firePropertyChange(String name, Object old, Object nw) {
- // do really nothing!
- }
-
- public void validate() {
-
- }
-
- public void doLayout() {
-
- }
-
- public void revalidate() {
-
- }
-
- public void repaint(long l, int x, int y, int w, int h) {
-
- }
-
- public void repaint() {
-
- }
-
- } // End of class Tree.
-
- /** Hack for broken backgrounds on synth look and feel */
- private static boolean isSynth = UIManager.getLookAndFeel().getClass().getName().indexOf ("com.sun.java.swing.plaf.gtk") != -1;
-
-
- /** Implements a ListCellRenderer
for rendering items
- * of a List
containing Node
s.
- * It displays the node's 16x16 icon and its display name.
- *
- * @author Ian Formanek
- */
- static final class List extends JLabel implements ListCellRenderer {
- /** generated Serialized Version UID */
- static final long serialVersionUID = -8387317362588264203L;
-
- /** Focused Node border. */
- protected static Border focusBorder = UIManager.getColor ("List.focusCellHighlight") != null ? // NOI18N
- BorderFactory.createLineBorder (UIManager.getColor ("List.focusCellHighlight")) : // NOI18N
- // recommended by issue 37335
- LineBorder.createGrayLineBorder ();
-
- public List() {
- setOpaque(true);
- }
-
- /** This is the only method defined by ListCellRenderer. We just
- * reconfigure the Jlabel each time we're called.
- */
- public Component getListCellRendererComponent (
- JList list,
- Object value, // value to display
- int index, // cell index
- boolean isSelected, // is the cell selected
- boolean cellHasFocus) // the list and the cell have the focus
- {
- VisualizerNode vis = (VisualizerNode)value;
- ImageIcon nodeicon = NodeRenderer.getIcon(vis.node.getIcon(BeanInfo.ICON_COLOR_16x16));
- setIcon(nodeicon);
- setText(vis.getDisplayName ());
- if (isSelected) {
- Component focusOwner =
- KeyboardFocusManager.getCurrentKeyboardFocusManager().
- getPermanentFocusOwner();
-
- boolean hasFocus = focusOwner == list || list.isAncestorOf(focusOwner);
-
- setBackground(hasFocus ? list.getSelectionBackground() :
- getNoFocusSelectionBackground());
-
- setForeground(hasFocus ? list.getSelectionForeground() :
- getNoFocusSelectionForeground());
- } else {
- setBackground(list.getBackground());
- setForeground(list.getForeground());
+ if (treeHasFocus) {
+ ren.setParentFocused(true);
}
-
- setIconTextGap (4 - nodeicon.getIconWidth()
- + ( nodeicon.getIconWidth() > 24 ? nodeicon.getIconWidth() : 24 ) );
-
-
- int delta = NodeListModel.findVisualizerDepth (list.getModel (), vis);
-
- Border border = (cellHasFocus || value == draggedOver) ? focusBorder : emptyBorder;
- if (delta > 0) {
- border = BorderFactory.createCompoundBorder (
- BorderFactory.createEmptyBorder (0, nodeicon.getIconWidth() * delta, 0, 0),
- border
- );
- }
- setBorder(border);
-
- return this;
- }
-
- protected void firePropertyChange(String name, Object old, Object nw) {
- // do really nothing!
- }
-
- } // End of class List.
-
-
- /** List cell renderer which renders icon and display name from VisualizerNode
. */
- final static class Pane extends JLabel implements ListCellRenderer {
- /** generated Serialized Version UID */
- static final long serialVersionUID = -5100925551665387243L;
-
- /** Focused Node border. */
- static Border focusBorder = UIManager.getColor ("List.focusCellHighlight") != null ? // NOI18N
- BorderFactory.createLineBorder (UIManager.getColor ("List.focusCellHighlight")) : // NOI18N
- // recommended by issue 37335
- LineBorder.createGrayLineBorder ();
-
- /** Creates a new NetbeansListCellRenderer */
- public Pane () {
- setOpaque(true);
- setVerticalTextPosition(JLabel.BOTTOM);
- setHorizontalAlignment(JLabel.CENTER);
- setHorizontalTextPosition(JLabel.CENTER);
}
+ return icon.getIconWidth() == 0 ? 24 : icon.getIconWidth();
+ }
- /** This is the only method defined by ListCellRenderer. We just
- * reconfigure the Jlabel each time we're called.
- * @param list the JList
- * @param value the value returned by list.getModel().getElementAt(index)
- * @param index the cells index
- * @param isSelected true
if the specified cell was selected
- * @param cellHasFocus true
if the specified cell has the focus
- * @return a component whose paint() method will render the specified value
- */
- public Component getListCellRendererComponent (
- JList list, Object value, int index,
- boolean isSelected, boolean cellHasFocus
- ) {
- VisualizerNode vis = (VisualizerNode)value;
-
- setIcon(NodeRenderer.getIcon(vis.node.getIcon(BeanInfo.ICON_COLOR_32x32)));
- setText(vis.getDisplayName ());
- Component focusOwner =
- KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
-
- boolean hasFocus = focusOwner == list ||
- list.isAncestorOf(focusOwner);
+ /** Utility method to find a visualizer node for the object passed to
+ * any of the cell renderer methods as the value */
+ private static final VisualizerNode findVisualizerNode(Object value) {
+ VisualizerNode vis = (value instanceof Node) ?
+ VisualizerNode.getVisualizer (null, (Node) value) :
+ (VisualizerNode)value;
- if (isSelected){
- setBackground(hasFocus ? list.getSelectionBackground()
- : getNoFocusSelectionBackground());
-
- setForeground(hasFocus ? list.getSelectionForeground() :
- getNoFocusSelectionForeground());
- }
- else {
- setBackground(list.getBackground());
- setForeground(list.getForeground());
+ if (vis == null) {
+ vis = VisualizerNode.EMPTY;
}
- setBorder(cellHasFocus ? focusBorder : emptyBorder);
- return this;
+ return vis;
}
- protected void firePropertyChange(String name, Object old, Object nw) {
- // do really nothing!
- }
+ // ********************
+ // Support for dragging
+ // ********************
- } // End of class Pane.
+ /** Value of the cell with 'drag under' visual feedback */
+ private static VisualizerNode draggedOver;
- private static Color noFocusSelectionBackground=null;
- static Color getNoFocusSelectionBackground() {
- if (noFocusSelectionBackground == null) {
- //allow theme/ui custom definition
- noFocusSelectionBackground =
- UIManager.getColor("nb.explorer.noFocusSelectionBackground"); //NOI18N
- if (noFocusSelectionBackground == null) {
- //try to get standard shadow color
- noFocusSelectionBackground = UIManager.getColor("controlShadow"); //NOI18N
- if (noFocusSelectionBackground == null) {
- //Okay, the look and feel doesn't suport it, punt
- noFocusSelectionBackground = new ColorUIResource(Color.lightGray);
- }
- //Lighten it a bit because disabled text will use controlShadow/
- //gray
- noFocusSelectionBackground = noFocusSelectionBackground.brighter();
- }
- }
- return noFocusSelectionBackground;
+ /** DnD operation enters. Update look and feel to the 'drag under' state.
+ * @param dragged the value of cell which should have 'drag under' visual feedback
+ */
+ static void dragEnter (Object dragged) {
+ draggedOver = (VisualizerNode)dragged;
}
- private static Color noFocusSelectionForeground=null;
- static Color getNoFocusSelectionForeground() {
- if (noFocusSelectionForeground == null) {
- //allow theme/ui custom definition
- noFocusSelectionForeground =
- UIManager.getColor("nb.explorer.noFocusSelectionForeground"); //NOI18N
- if (noFocusSelectionForeground == null) {
- //try to get standard shadow color
- noFocusSelectionForeground = UIManager.getColor("textText"); //NOI18N
- if (noFocusSelectionForeground == null) {
- //Okay, the look and feel doesn't suport it, punt
- noFocusSelectionForeground = Color.BLACK;
- }
- }
- }
- return noFocusSelectionForeground;
+ /** DnD operation exits. Revert to the normal look and feel. */
+ static void dragExit () {
+ draggedOver = null;
}
-
}
Index: openide/src/org/openide/explorer/view/TableSheetCell.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/view/TableSheetCell.java,v
retrieving revision 1.18
diff -u -u -r1.18 TableSheetCell.java
--- openide/src/org/openide/explorer/view/TableSheetCell.java 17 Feb 2004 16:47:49 -0000 1.18
+++ openide/src/org/openide/explorer/view/TableSheetCell.java 28 Apr 2004 17:20:06 -0000
@@ -259,11 +259,11 @@
propPanel.setBackground(tableHasFocus ?
table.getSelectionBackground() :
- NodeRenderer.getNoFocusSelectionBackground());
+ TreeTable.getUnfocusedSelectedBackground());
propPanel.setForeground(tableHasFocus ?
table.getSelectionForeground() :
- NodeRenderer.getNoFocusSelectionForeground());
+ TreeTable.getUnfocusedSelectedForeground());
} else {
propPanel.setBackground(table.getBackground());
@@ -290,7 +290,7 @@
nullPanel.setBackground(tableHasFocus ?
table.getSelectionBackground() :
- NodeRenderer.getNoFocusSelectionBackground());
+ TreeTable.getUnfocusedSelectedBackground());
//XXX may want to handle inverse theme here and use brighter if
//below a threshold. Deferred to centralized color management
//being implemented.
Index: openide/src/org/openide/explorer/view/TreeTable.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/view/TreeTable.java,v
retrieving revision 1.46
diff -u -u -r1.46 TreeTable.java
--- openide/src/org/openide/explorer/view/TreeTable.java 2 Mar 2004 19:06:13 -0000 1.46
+++ openide/src/org/openide/explorer/view/TreeTable.java 28 Apr 2004 17:20:06 -0000
@@ -703,12 +703,16 @@
focusOwner == TreeTable.this
|| TreeTable.this.isAncestorOf(focusOwner);
+ //TODO - it should be possible to simply set the correct
+ //color in prepareRenderer for the tree's cell renderer,
+ //rather than set it for the whole tree. Might fix a
+ //couple problems. -Tim
setBackground(tableHasFocus ?
table.getSelectionBackground() :
- NodeRenderer.getNoFocusSelectionBackground());
+ getUnfocusedSelectedBackground());
setForeground(tableHasFocus ?
table.getSelectionForeground() :
- NodeRenderer.getNoFocusSelectionForeground());
+ getUnfocusedSelectedForeground());
} else {
setBackground(table.getBackground());
setForeground(table.getForeground());
@@ -818,10 +822,10 @@
if (isEditing() && editorComp != null) {
editorComp.setBackground(focused ?
getSelectionBackground() :
- NodeRenderer.getNoFocusSelectionBackground());
+ getUnfocusedSelectedBackground());
editorComp.setForeground(focused ?
getSelectionForeground() :
- NodeRenderer.getNoFocusSelectionForeground());
+ getUnfocusedSelectedForeground());
}
}
@@ -1611,4 +1615,46 @@
}
}
}
+
+ private static Color unfocusedSelBg = null;
+ private static Color unfocusedSelFg = null;
+
+ /** Get the system-wide unfocused selection background color */
+ static Color getUnfocusedSelectedBackground() {
+ if (unfocusedSelBg == null) {
+ //allow theme/ui custom definition
+ unfocusedSelBg =
+ UIManager.getColor("nb.explorer.unfocusedSelBg"); //NOI18N
+ if (unfocusedSelBg == null) {
+ //try to get standard shadow color
+ unfocusedSelBg = UIManager.getColor("controlShadow"); //NOI18N
+ if (unfocusedSelBg == null) {
+ //Okay, the look and feel doesn't suport it, punt
+ unfocusedSelBg = Color.lightGray;
+ }
+ //Lighten it a bit because disabled text will use controlShadow/
+ //gray
+ unfocusedSelBg = unfocusedSelBg.brighter();
+ }
+ }
+ return unfocusedSelBg;
+ }
+
+ /** Get the system-wide unfocused selection foreground color */
+ static Color getUnfocusedSelectedForeground() {
+ if (unfocusedSelFg == null) {
+ //allow theme/ui custom definition
+ unfocusedSelFg =
+ UIManager.getColor("nb.explorer.unfocusedSelFg"); //NOI18N
+ if (unfocusedSelFg == null) {
+ //try to get standard shadow color
+ unfocusedSelFg = UIManager.getColor("textText"); //NOI18N
+ if (unfocusedSelFg == null) {
+ //Okay, the look and feel doesn't suport it, punt
+ unfocusedSelFg = Color.BLACK;
+ }
+ }
+ }
+ return unfocusedSelFg;
+ }
}
Index: openide/src/org/openide/explorer/view/TreeView.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/view/TreeView.java,v
retrieving revision 1.159
diff -u -u -r1.159 TreeView.java
--- openide/src/org/openide/explorer/view/TreeView.java 22 Apr 2004 13:10:26 -0000 1.159
+++ openide/src/org/openide/explorer/view/TreeView.java 28 Apr 2004 17:20:07 -0000
@@ -1106,7 +1106,7 @@
// note: dropTarget is activated in constructor
}
// lazy cell editor init
- tree.setCellEditor(new TreeViewCellEditor(tree, new NodeRenderer.Tree ()));
+ tree.setCellEditor(new TreeViewCellEditor(tree));
tree.setEditable(true);
}
Index: openide/src/org/openide/explorer/view/TreeViewCellEditor.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/view/TreeViewCellEditor.java,v
retrieving revision 1.38
diff -u -u -r1.38 TreeViewCellEditor.java
--- openide/src/org/openide/explorer/view/TreeViewCellEditor.java 22 Apr 2004 13:10:26 -0000 1.38
+++ openide/src/org/openide/explorer/view/TreeViewCellEditor.java 28 Apr 2004 17:20:07 -0000
@@ -44,11 +44,13 @@
/** Construct a cell editor.
* @param tree the tree
- * @param renderer the renderer to use for the cell
*/
- public TreeViewCellEditor(JTree tree, TreeCellRenderer renderer) {
- super(tree, renderer instanceof DefaultTreeCellRenderer ?
- (DefaultTreeCellRenderer) renderer : new DefaultTreeCellRenderer()); //XXX for testing GTK hacks - will be deleted with the HTML mini renderer integration
+ public TreeViewCellEditor(JTree tree) {
+ //Use a dummy DefaultTreeCellEditor - we'll set up the correct
+ //icon when we fetch the editor component (see EOF). Not sure
+ //it's wildly vaulable to subclass DefaultTreeCellEditor here -
+ //we override most everything
+ super(tree, new DefaultTreeCellRenderer());
// deal with selection if already exists
if (tree.getSelectionCount() == 1) {
lastPath = tree.getSelectionPath();
@@ -155,7 +157,7 @@
}
};
- tf.registerKeyboardAction(
+ tf.registerKeyboardAction( //TODO update to use inputMap/actionMap
this,
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true),
JComponent.WHEN_FOCUSED
@@ -264,6 +266,7 @@
protected void prepareForEditing () {
tree.removeMouseMotionListener (this);
+
super.prepareForEditing ();
}
@@ -274,7 +277,7 @@
}
/** Redefined default cell editor to convert nodes to name */
- static class Ed extends DefaultCellEditor {
+ class Ed extends DefaultCellEditor {
/** generated Serialized Version UID */
static final long serialVersionUID = -6373058702842751408L;
@@ -294,6 +297,8 @@
else
delegate.setValue(""); // NOI18N
+ editingIcon = ((VisualizerNode) value).getIcon(expanded, false);
+
((JTextField) editorComponent).selectAll();
return editorComponent;
}
Index: openide/src/org/openide/explorer/view/VisualizerNode.java
===================================================================
RCS file: /cvs/openide/src/org/openide/explorer/view/VisualizerNode.java,v
retrieving revision 1.39
diff -u -u -r1.39 VisualizerNode.java
--- openide/src/org/openide/explorer/view/VisualizerNode.java 20 Jan 2004 15:31:59 -0000 1.39
+++ openide/src/org/openide/explorer/view/VisualizerNode.java 28 Apr 2004 17:20:07 -0000
@@ -13,9 +13,13 @@
package org.openide.explorer.view;
+import java.awt.Image;
+import java.beans.BeanInfo;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.*;
+import javax.swing.Icon;
+import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
import javax.swing.event.EventListenerList;
import javax.swing.tree.TreeNode;
@@ -23,14 +27,16 @@
import org.openide.ErrorManager;
import org.openide.nodes.*;
import org.openide.util.Mutex;
+import org.openide.util.Utilities;
import org.openide.util.enum.QueueEnumeration;
/** Visual representation of one node. Holds necessary information about nodes
* like icon, name, description and also list of its children.
* annotateName()
.
+ * Node.getHtmlDisplayName()
,
+ * filesystems that proxy other filesystems (and so must implement
+ * this interface to supply HTML annotations) should return null if
+ * the filesystem they proxy does not provide an implementation of
+ * HTMLStatus.
+ *
+ * @see org.openide.awt.HtmlRenderer */
+ public String annotateNameHtml (String name, java.util.Set files);
+ }
+
/** Empty status */
private static final Status STATUS_NONE = new Status () {
public String annotateName (String name, java.util.Set files) {
Index: openide/src/org/openide/nodes/FilterNode.java
===================================================================
RCS file: /cvs/openide/src/org/openide/nodes/FilterNode.java,v
retrieving revision 1.88
diff -u -u -r1.88 FilterNode.java
--- openide/src/org/openide/nodes/FilterNode.java 24 Feb 2004 13:34:06 -0000 1.88
+++ openide/src/org/openide/nodes/FilterNode.java 28 Apr 2004 17:20:08 -0000
@@ -22,6 +22,7 @@
import java.util.*;
import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
import org.openide.ErrorManager;
import org.openide.util.datatransfer.NewType;
@@ -643,6 +644,39 @@
}
return retValue;
+ }
+
+ /** Get a display name containing HTML markup. Note: If you subclass
+ * FilterNode and override getDisplayName()
, this method will
+ * always return null unless you override it as well (assuming that if you're
+ * changing the display name, you don't want an HTML display name constructed
+ * from the original node's display name to be what shows up in views of
+ * this node.
+ * @see org.openide.nodes.Node#getHtmlDisplayName
+ * @return An HTML display name, if available, or null if no display name
+ * is available */
+ public String getHtmlDisplayName() {
+ if (overridesGetDisplayName()) {
+ return null;
+ } else {
+ return delegating (DELEGATE_GET_DISPLAY_NAME) ?
+ original.getHtmlDisplayName() : super.getHtmlDisplayName();
+ }
+ }
+
+ private boolean overridesGetDisplayName() {
+ if (getClass() != FilterNode.class) {
+ try {
+ Method m = getClass().getMethod("getDisplayName", null); //NOI18N
+ return m.getDeclaringClass() != FilterNode.class;
+ } catch (NoSuchMethodException nsme) {
+ //can't happen
+ ErrorManager.getDefault().notify(nsme);
+ return true;
+ }
+ } else {
+ return false;
+ }
}
/*
Index: openide/src/org/openide/nodes/Node.java
===================================================================
RCS file: /cvs/openide/src/org/openide/nodes/Node.java,v
retrieving revision 1.81
diff -u -u -r1.81 Node.java
--- openide/src/org/openide/nodes/Node.java 9 Apr 2004 12:32:12 -0000 1.81
+++ openide/src/org/openide/nodes/Node.java 28 Apr 2004 17:20:12 -0000
@@ -655,6 +655,24 @@
}
}
+ /** Return a variant of the display name containing HTML markup
+ * conforming to the limited subset of font-markup HTML supported by
+ * the lightweight HTML renderer org.openide.awt.HtmlRenderer
+ * (font color, bold, italic and strikethrough supported; font
+ * colors can be UIManager color keys if they are prefixed with
+ * a ! character, i.e. <font color=&'controlShadow'>).
+ * Enclosing html tags are not needed.
+ *