diff -r de0d87358e2f openide.explorer/src/org/openide/explorer/view/VisualizerNode.java --- a/openide.explorer/src/org/openide/explorer/view/VisualizerNode.java Thu Jun 05 13:48:18 2008 +0200 +++ b/openide.explorer/src/org/openide/explorer/view/VisualizerNode.java Mon Jun 09 11:33:15 2008 +0200 @@ -56,7 +56,6 @@ 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; @@ -206,7 +205,14 @@ if (desc == UNKNOWN) { shortDescription = desc = node.getShortDescription(); } - + if (icon instanceof Image) { + String toolTip = Utilities.getImageToolTip((Image) icon); + if (toolTip.length() > 0) { + StringBuilder str = new StringBuilder(128); + str.append("").append(desc).append("
").append(toolTip).append(""); + desc = str.toString(); + } + } return desc; } @@ -407,7 +413,7 @@ if (Node.PROP_NAME.equals(name) || Node.PROP_DISPLAY_NAME.equals(name) || isIconChange) { if (isIconChange) { //Ditch the cached icon type so the next call to getIcon() will - //recreate the ImageIcon + //recreate the Icon cachedIconType = -1; } @@ -558,7 +564,7 @@ icon = getDefaultIcon(); } else { - icon = new ImageIcon(image); + icon = Utilities.image2Icon(image); } } @@ -577,7 +583,7 @@ /** Loads default icon if not loaded. */ private static Icon getDefaultIcon() { if (defaultIcon == null) { - defaultIcon = new ImageIcon(Utilities.loadImage(DEFAULT_ICON)); + defaultIcon = Utilities.image2Icon(Utilities.loadImage(DEFAULT_ICON)); } return defaultIcon; diff -r de0d87358e2f openide.explorer/test/unit/src/org/openide/explorer/view/VisualizerNodeTest.java --- a/openide.explorer/test/unit/src/org/openide/explorer/view/VisualizerNodeTest.java Thu Jun 05 13:48:18 2008 +0200 +++ b/openide.explorer/test/unit/src/org/openide/explorer/view/VisualizerNodeTest.java Mon Jun 09 11:33:16 2008 +0200 @@ -89,4 +89,16 @@ assertEquals("Index is 1", 1, ta.getIndex(tm)); } + + public void testIconsAreShared() { + AbstractNode a1 = new AbstractNode(Children.LEAF); + VisualizerNode v1 = VisualizerNode.getVisualizer(null, a1); + Icon icon1 = v1.getIcon(false, false); + + AbstractNode a2 = new AbstractNode(Children.LEAF); + VisualizerNode v2 = VisualizerNode.getVisualizer(null, a2); + Icon icon2 = v2.getIcon(false, false); + + assertSame("Icon instances should be same", icon1, icon2); + } } diff -r de0d87358e2f openide.util/apichanges.xml --- a/openide.util/apichanges.xml Thu Jun 05 13:48:18 2008 +0200 +++ b/openide.util/apichanges.xml Mon Jun 09 11:33:16 2008 +0200 @@ -49,6 +49,28 @@ Actions API + + + New methods for image tool tips manipulation + + + + + +

+ Methods for tool tips manipulation: + Image assignImageToolTip(Image image, String text); + String getImageToolTip(Image image); + Image addToImageToolTip(Image image, String text); +

+

+ Method for conversion from Image to Icon: + Icon image2Icon(Image image); +

+
+ + +
Mutex made pluggable diff -r de0d87358e2f openide.util/nbproject/project.properties --- a/openide.util/nbproject/project.properties Thu Jun 05 13:48:18 2008 +0200 +++ b/openide.util/nbproject/project.properties Mon Jun 09 11:33:16 2008 +0200 @@ -41,7 +41,7 @@ javac.source=1.5 module.jar.dir=lib -spec.version.base=7.13.0 +spec.version.base=7.14.0 # For XMLSerializer, needed for XMLUtil.write to work w/ namespaces under JDK 1.4: diff -r de0d87358e2f openide.util/src/org/openide/util/IconManager.java --- a/openide.util/src/org/openide/util/IconManager.java Thu Jun 05 13:48:18 2008 +0200 +++ b/openide.util/src/org/openide/util/IconManager.java Mon Jun 09 11:33:16 2008 +0200 @@ -42,6 +42,7 @@ package org.openide.util; import java.awt.Component; +import java.awt.Graphics; import java.awt.HeadlessException; import java.awt.Image; import java.awt.MediaTracker; @@ -50,10 +51,13 @@ import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ImageObserver; +import java.awt.image.IndexColorModel; +import java.awt.image.WritableRaster; import java.io.IOException; import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.HashSet; +import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Set; @@ -63,18 +67,24 @@ import javax.imageio.ImageReadParam; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JLabel; /** Registers all loaded images into the AbstractNode, so nothing is loaded twice. * * @author Jaroslav Tulach */ final class IconManager extends Object { + /** separator for individual parts of tool tip text */ + static final String TOOLTIP_SEPAR = "
"; // NOI18N /** a value that indicates that the icon does not exists */ private static final ActiveRef NO_ICON = new ActiveRef(null, null, null); private static final Map> cache = new HashMap>(128); private static final Map> localizedCache = new HashMap>(128); private static final Map> compositeCache = new HashMap>(128); + private static final Map> imageToolTipCache = new HashMap>(128); /** Resource paths for which we have had to strip initial slash. * @see "#20072" @@ -325,6 +335,8 @@ "loading icon {0} = {1}", new Object[] {n, result}); name = new String(name).intern(); // NOPMD + result = ToolTipImage.createNew("", result); + cache.put(name, new ActiveRef(result, cache, name)); return result; @@ -338,7 +350,7 @@ } } } - + /** * Method that attempts to find the merged image in the cache first, then * creates the image if it was not found. @@ -383,6 +395,65 @@ return rep; } + + static final Image assignImageToolTip(Image image, String text) { + ToolTipImageKey key = new ToolTipImageKey(image, text); + Image cached; + synchronized (imageToolTipCache) { + ActiveRef r = imageToolTipCache.get(key); + if (r != null) { + cached = r.get(); + if (cached != null) { + return cached; + } + } + cached = ToolTipImage.createNew(text, image); + imageToolTipCache.put(key, new ActiveRef(cached, imageToolTipCache, key)); + return cached; + } + } + + static final String getImageToolTip(Image image) { + if (image instanceof ToolTipImage) { + return ((ToolTipImage) image).toolTipText; + } else { + return ""; + } + } + + static final Image addToImageToolTip(Image image, String text) { + if (image instanceof ToolTipImage) { + ToolTipImage tti = (ToolTipImage) image; + StringBuilder str = new StringBuilder(tti.toolTipText); + if (str.length() > 0 && text.length() > 0) { + str.append(TOOLTIP_SEPAR); + } + str.append(text); + return assignImageToolTip(image, str.toString()); + } else { + return assignImageToolTip(image, text); + } + } + + public static final Icon image2Icon(Image image) { + if (image instanceof ToolTipImage) { + return (Icon) image; + } else { + return new ImageIcon(image); + } + } + + public static final Image icon2Image(Icon icon) { + if (icon instanceof ToolTipImage) { + return (Image) icon; + } else { + ToolTipImage image = new ToolTipImage("", icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics g = image.getGraphics(); + icon.paintIcon(new JLabel(), g, 0, 0); + g.dispose(); + return image; + } + } private static void ensureLoaded(Image image) { if ( @@ -407,7 +478,7 @@ tracker.removeImage(image, id); } } - + private static final Image doMergeImages(Image image1, Image image2, int x, int y) { ensureLoaded(image1); ensureLoaded(image2); @@ -417,8 +488,17 @@ boolean bitmask = (image1 instanceof Transparency) && ((Transparency)image1).getTransparency() != Transparency.TRANSLUCENT && (image2 instanceof Transparency) && ((Transparency)image2).getTransparency() != Transparency.TRANSLUCENT; + StringBuilder str = new StringBuilder(image1 instanceof ToolTipImage ? ((ToolTipImage)image1).toolTipText : ""); + if (image2 instanceof ToolTipImage) { + String toolTip = ((ToolTipImage)image2).toolTipText; + if (str.length() > 0 && toolTip.length() > 0) { + str.append(TOOLTIP_SEPAR); + } + str.append(toolTip); + } + ColorModel model = colorModel(bitmask? Transparency.BITMASK: Transparency.TRANSLUCENT); - java.awt.image.BufferedImage buffImage = new java.awt.image.BufferedImage( + ToolTipImage buffImage = new ToolTipImage(str.toString(), model, model.createCompatibleWritableRaster(w, h), model.isAlphaPremultiplied(), null ); @@ -474,6 +554,7 @@ this.overlayImage = overlay; } + @Override public boolean equals(Object other) { if (!(other instanceof CompositeImageKey)) { return false; @@ -484,6 +565,7 @@ return (x == k.x) && (y == k.y) && (baseImage == k.baseImage) && (overlayImage == k.overlayImage); } + @Override public int hashCode() { int hash = ((x << 3) ^ y) << 4; hash = hash ^ baseImage.hashCode() ^ overlayImage.hashCode(); @@ -491,8 +573,42 @@ return hash; } + @Override public String toString() { return "Composite key for " + baseImage + " + " + overlayImage + " at [" + x + ", " + y + "]"; // NOI18N + } + } + + /** + * Key used for ToolTippedImage + */ + private static class ToolTipImageKey { + Image image; + String str; + + ToolTipImageKey(Image image, String str) { + this.image = image; + this.str = str; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ToolTipImageKey)) { + return false; + } + ToolTipImageKey k = (ToolTipImageKey) other; + return (str.equals(k.str)) && (image == k.image); + } + + @Override + public int hashCode() { + int hash = image.hashCode() ^ str.hashCode(); + return hash; + } + + @Override + public String toString() { + return "ImageStringKey for " + image + " + " + str; // NOI18N } } @@ -514,4 +630,58 @@ } } // end of ActiveRef + + /** + * Image with tool tip text (for icons with badges) + */ + private static class ToolTipImage extends BufferedImage implements Icon { + final String toolTipText; + + public static ToolTipImage createNew(String toolTipText, Image image) { + IconManager.ensureLoaded(image); + boolean bitmask = (image instanceof Transparency) && ((Transparency) image).getTransparency() != Transparency.TRANSLUCENT; + ColorModel model = colorModel(bitmask ? Transparency.BITMASK : Transparency.TRANSLUCENT); + int w = image.getWidth(null); + int h = image.getHeight(null); + ToolTipImage newImage = new ToolTipImage(toolTipText, model, model.createCompatibleWritableRaster(w, h), model.isAlphaPremultiplied(), null); + + java.awt.Graphics g = newImage.createGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + return newImage; + } + + public ToolTipImage(String toolTipText, ColorModel cm, WritableRaster raster, boolean isRasterPremultiplied, Hashtable properties) { + super(cm, raster, isRasterPremultiplied, properties); + this.toolTipText = toolTipText; + } + + public ToolTipImage(String toolTipText, int width, int height, int imageType, IndexColorModel cm) { + super(width, height, imageType, cm); + this.toolTipText = toolTipText; + } + + public ToolTipImage(String toolTipText, int width, int height, int imageType) { + super(width, height, imageType); + this.toolTipText = toolTipText; + } + + public ToolTipImage(String toolTipText, BufferedImage image) { + super(image.getWidth(), image.getHeight(), image.getType()); + this.toolTipText = toolTipText; + } + + + public int getIconHeight() { + return super.getHeight(); + } + + public int getIconWidth() { + return super.getWidth(); + } + + public void paintIcon(Component c, Graphics g, int x, int y) { + g.drawImage(this, x, y, null); + } + } } diff -r de0d87358e2f openide.util/src/org/openide/util/Utilities.java --- a/openide.util/src/org/openide/util/Utilities.java Thu Jun 05 13:48:18 2008 +0200 +++ b/openide.util/src/org/openide/util/Utilities.java Mon Jun 09 11:33:16 2008 +0200 @@ -2606,7 +2606,7 @@ return IconManager.mergeImages(image1, image2, x, y); } - + /** * Loads an image from the specified resource ID. The image is loaded using the "system" classloader registered in * Lookup. @@ -2618,21 +2618,56 @@ } /** + * Assign tool tip text to given image (creates new or returns cached, original remains unmodified) + * Text can contain HTML tags e.g. "<b>my</b> text" + * @param image to which tool tip should be set + * @param tool tip text + * @return Image with attached tool tip + * @since 7.14 + */ + public static final Image assignImageToolTip(Image image, String text) { + return IconManager.assignImageToolTip(image, text); + } + + /** + * Get tool tip text for given image + * @param image which is asked for tool tip text + * @return String containing attached tool tip text + * @since 7.14 + */ + public static final String getImageToolTip(Image image) { + return IconManager.getImageToolTip(image); + } + + /** + * Add text to tool tip for given image (creates new or returns cached, original remains unmodified) + * Text can contain HTML tags e.g. "<b>my</b> text" + * @param text to add to tool tip + * @return Image with attached tool tip + * @since 7.14 + */ + public static final Image addToImageToolTip(Image image, String text) { + return IconManager.addToImageToolTip(image, text); + } + + /** + * Converts given image to a {@link javax.swing.Icon}. + * @param image {@link java.awt.Image} to be converted. + * @return icon corresponding {@link javax.swing.Icon} + * @since 7.14 + */ + public static final Icon image2Icon(Image image) { + return IconManager.image2Icon(image); + } + + /** * Converts given icon to a {@link java.awt.Image}. * * @param icon {@link javax.swing.Icon} to be converted. * @since 7.3 */ public static final Image icon2Image(Icon icon) { - if (icon instanceof ImageIcon) { - return ((ImageIcon) icon).getImage(); - } else { - BufferedImage bImage = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); - Graphics g = bImage.getGraphics(); - icon.paintIcon(new JLabel(), g, 0, 0); - g.dispose(); - return bImage; - } + return IconManager.icon2Image(icon); } /** Builds a popup menu from actions for provided context specified by diff -r de0d87358e2f openide.util/test/unit/src/org/openide/util/IconManagerTest.java --- a/openide.util/test/unit/src/org/openide/util/IconManagerTest.java Thu Jun 05 13:48:18 2008 +0200 +++ b/openide.util/test/unit/src/org/openide/util/IconManagerTest.java Mon Jun 09 11:33:16 2008 +0200 @@ -44,9 +44,8 @@ import java.awt.Image; import java.awt.Transparency; import java.awt.image.BufferedImage; +import javax.swing.Icon; import junit.framework.*; -import java.lang.ref.*; -import java.util.*; /** * @@ -112,5 +111,68 @@ assertEquals(Color.RED, new Color(merged.getRGB(1, 1))); assertEquals(Color.BLUE, new Color(merged.getRGB(10, 10))); } - + + public void testImageToolTip() { + BufferedImage img1 = new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB); + java.awt.Graphics2D g = img1.createGraphics(); + g.setColor(Color.BLUE); + g.fillRect(0, 0, 16, 16); + g.dispose(); + + assertEquals("Tool tip text should be empty", "", IconManager.getImageToolTip(img1)); + + String text = "test"; + Image imgTT1 = IconManager.assignImageToolTip(img1, text); + assertEquals("Should remain empty", "", IconManager.getImageToolTip(img1)); + String str = IconManager.getImageToolTip(imgTT1); + assertEquals("We should get what we set", text, str); + + Image imgTT2 = IconManager.assignImageToolTip(img1, "test"); + assertSame("Instances should be same", imgTT1, imgTT2); + + imgTT2 = IconManager.addToImageToolTip(img1, ""); + imgTT2 = IconManager.addToImageToolTip(imgTT2, "test"); + str = IconManager.getImageToolTip(imgTT2); + String expected = "test"; + assertEquals("Tool tip text should be: " + expected + ", but it is " + str, expected, str); + + imgTT2 = IconManager.addToImageToolTip(imgTT1, "test2"); + str = IconManager.getImageToolTip(imgTT2); + expected = "test" + IconManager.TOOLTIP_SEPAR + "test2"; + assertEquals("Tool tip text should be: " + expected + ", but it is " + str, expected, str); + + BufferedImage img2 = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB); + g = img2.createGraphics(); + g.setColor(Color.RED); + g.fillRect(0, 0, 2, 2); + g.dispose(); + + imgTT1 = IconManager.assignImageToolTip(img1, "Tool tip image1"); + imgTT2 = IconManager.assignImageToolTip(img2, "Tool tip image2"); + Image result = IconManager.mergeImages(imgTT1, imgTT2, 0, 0); + expected = "Tool tip image1" + IconManager.TOOLTIP_SEPAR + "Tool tip image2"; + str = IconManager.getImageToolTip(result); + assertEquals("Tool tip text should be: " + expected + ", but it is " + str, expected, str); + + result = IconManager.mergeImages(imgTT1, img2, 0, 0); + str = IconManager.getImageToolTip(result); + expected = "Tool tip image1"; + assertEquals("Tool tip text should be: " + expected + ", but it is " + str, expected, str); + + result = IconManager.mergeImages(img1, imgTT2, 0, 0); + str = IconManager.getImageToolTip(result); + expected = "Tool tip image2"; + assertEquals("Tool tip text should be: " + expected + ", but it is " + str, expected, str); + + result = IconManager.mergeImages(img1, img2, 0, 0); + str = IconManager.getImageToolTip(result); + expected = ""; + assertEquals("Tool tip text should be empty, but it is " + str, expected, str); + + Icon icon = IconManager.image2Icon(result); + assertSame("Should be same instance", icon, result); + + Image img = IconManager.icon2Image(icon); + assertSame("Should be same instance", icon, img); + } }