/** * $RCSfile: ,v $ * $Revision: $ * $Date: $ * * Copyright (C) 2004-2011 Jive Software. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jivesoftware.spark.util; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.Insets; import java.awt.Label; import java.awt.MediaTracker; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JPopupMenu; import org.jivesoftware.resource.SparkRes; import org.jivesoftware.spark.util.log.Log; /** * <code>GraphicsUtils</code> class defines common user-interface related * utility functions. */ public final class GraphicUtils { private static final Insets HIGHLIGHT_INSETS = new Insets(1, 1, 1, 1); public static final Color SELECTION_COLOR = new java.awt.Color(166, 202, 240); public static final Color TOOLTIP_COLOR = new java.awt.Color(166, 202, 240); protected final static Component component = new Component() { private static final long serialVersionUID = -7556405112141454291L; }; protected final static MediaTracker tracker = new MediaTracker(component); private static Map<String, Image> imageCache = new HashMap<String, Image>(); /** * The default Hand cursor. */ public static final Cursor HAND_CURSOR = new Cursor(Cursor.HAND_CURSOR); /** * The default Text Cursor. */ public static final Cursor DEFAULT_CURSOR = new Cursor( Cursor.DEFAULT_CURSOR); private GraphicUtils() { } /** * Sets the location of the specified window so that it is centered on * screen. * * @param window * The window to be centered. */ public static void centerWindowOnScreen(Window window) { final Dimension screenSize = Toolkit.getDefaultToolkit() .getScreenSize(); final Dimension size = window.getSize(); if (size.height > screenSize.height) { size.height = screenSize.height; } if (size.width > screenSize.width) { size.width = screenSize.width; } window.setLocation((screenSize.width - size.width) / 2, (screenSize.height - size.height) / 2); } /** * Draws a single-line highlight border rectangle. * * @param g * The graphics context to use for drawing. * @param x * The left edge of the border. * @param y * The top edge of the border. * @param width * The width of the border. * @param height * The height of the border. * @param raised * <code>true</code> if the border is to be drawn raised, * <code>false</code> if lowered. * @param shadow * The shadow color for the border. * @param highlight * The highlight color for the border. * @see javax.swing.border.EtchedBorder */ public static void drawHighlightBorder(Graphics g, int x, int y, int width, int height, boolean raised, Color shadow, Color highlight) { final Color oldColor = g.getColor(); g.translate(x, y); g.setColor(raised ? highlight : shadow); g.drawLine(0, 0, width - 2, 0); g.drawLine(0, 1, 0, height - 2); g.setColor(raised ? shadow : highlight); g.drawLine(width - 1, 0, width - 1, height - 1); g.drawLine(0, height - 1, width - 2, height - 1); g.translate(-x, -y); g.setColor(oldColor); } /** * Return the amount of space taken up by a highlight border drawn by * <code>drawHighlightBorder()</code>. * * @return The <code>Insets</code> needed for the highlight border. * @see #drawHighlightBorder */ public static Insets getHighlightBorderInsets() { return HIGHLIGHT_INSETS; } /** * Creates an ImageIcon from an Image * * @param image * {@link Image} * @return {@link ImageIcon} */ public static ImageIcon createImageIcon(Image image) { if (image == null) { return null; } synchronized (tracker) { tracker.addImage(image, 0); try { tracker.waitForID(0, 0); } catch (InterruptedException e) { Log.error("INTERRUPTED while loading Image",e); } tracker.removeImage(image, 0); } return new ImageIcon(image); } /** * Returns a point where the given popup menu should be shown. The point is * calculated by adjusting the X and Y coordinates from the given mouse * event so that the popup menu will not be clipped by the screen * boundaries. * * @param popup * the popup menu * @param event * the mouse event * @return the point where the popup menu should be shown */ public static Point getPopupMenuShowPoint(JPopupMenu popup, MouseEvent event) { Component source = (Component) event.getSource(); Point topLeftSource = source.getLocationOnScreen(); Point ptRet = getPopupMenuShowPoint(popup, topLeftSource.x + event.getX(), topLeftSource.y + event.getY()); ptRet.translate(-topLeftSource.x, -topLeftSource.y); return ptRet; } /** * Returns a point where the given popup menu should be shown. The point is * calculated by adjusting the X and Y coordinates so that the popup menu * will not be clipped by the screen boundaries. * * @param popup * the popup menu * @param x * the x position in screen coordinate * @param y * the y position in screen coordinates * @return the point where the popup menu should be shown in screen * coordinates */ public static Point getPopupMenuShowPoint(JPopupMenu popup, int x, int y) { Dimension sizeMenu = popup.getPreferredSize(); Point bottomRightMenu = new Point(x + sizeMenu.width, y + sizeMenu.height); Rectangle[] screensBounds = getScreenBounds(); int n = screensBounds.length; for (int i = 0; i < n; i++) { Rectangle screenBounds = screensBounds[i]; if (screenBounds.x <= x && x <= (screenBounds.x + screenBounds.width)) { Dimension sizeScreen = screenBounds.getSize(); sizeScreen.height -= 32; // Hack to help prevent menu being // clipped by Windows/Linux/Solaris // Taskbar. int xOffset = 0; if (bottomRightMenu.x > (screenBounds.x + sizeScreen.width)) xOffset = -sizeMenu.width; int yOffset = 0; if (bottomRightMenu.y > (screenBounds.y + sizeScreen.height)) yOffset = sizeScreen.height - bottomRightMenu.y; return new Point(x + xOffset, y + yOffset); } } return new Point(x, y); // ? that would mean that the top left point was // not on any screen. } /** * Centers the window over a component (usually another window). The window * must already have been sized. * * @param window * Window to center. * @param over * Component to center over. */ public static void centerWindowOnComponent(Window window, Component over) { if ((over == null) || !over.isShowing()) { centerWindowOnScreen(window); return; } Point parentLocation = over.getLocationOnScreen(); Dimension parentSize = over.getSize(); Dimension size = window.getSize(); // Center it. int x = parentLocation.x + (parentSize.width - size.width) / 2; int y = parentLocation.y + (parentSize.height - size.height) / 2; // Now, make sure it's onscreen Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); // This doesn't actually work on the Mac, where the screen // doesn't necessarily start at 0,0 if (x + size.width > screenSize.width) x = screenSize.width - size.width; if (x < 0) x = 0; if (y + size.height > screenSize.height) y = screenSize.height - size.height; if (y < 0) y = 0; window.setLocation(x, y); } /** * @param c * Component to check on. * @return returns true if the component of one of its child has the focus */ public static boolean isAncestorOfFocusedComponent(Component c) { if (c.hasFocus()) { return true; } else { if (c instanceof Container) { Container cont = (Container) c; int n = cont.getComponentCount(); for (int i = 0; i < n; i++) { Component child = cont.getComponent(i); if (isAncestorOfFocusedComponent(child)) return true; } } } return false; } /** * Returns the first component in the tree of <code>c</code> that can accept * the focus. * * @param c * the root of the component hierarchy to search * @see #focusComponentOrChild * @deprecated replaced by * {@link #getFocusableComponentOrChild(Component,boolean)} * @return Component that was focused on. */ public static Component getFocusableComponentOrChild(Component c) { return getFocusableComponentOrChild(c, false); } /** * Returns the first component in the tree of <code>c</code> that can accept * the focus. * * @param c * the root of the component hierarchy to search * @param deepest * if <code>deepest</code> is true the method will return the * first and deepest component that can accept the focus. For * example, if both a child and its parent are focusable and * <code>deepest</code> is true, the child is returned. * @see #focusComponentOrChild * @return Component that was focused on. */ public static Component getFocusableComponentOrChild(Component c, boolean deepest) { if (c != null && c.isEnabled() && c.isVisible()) { if (c instanceof Container) { Container cont = (Container) c; if (!deepest) { // first one is a good one if (c instanceof JComponent) { JComponent jc = (JComponent) c; if (jc.isRequestFocusEnabled()) { return jc; } } } int n = cont.getComponentCount(); for (int i = 0; i < n; i++) { Component child = cont.getComponent(i); Component focused = getFocusableComponentOrChild(child, deepest); if (focused != null) { return focused; } } if (c instanceof JComponent) { if (deepest) { JComponent jc = (JComponent) c; if (jc.isRequestFocusEnabled()) { return jc; } } } else { return c; } } } return null; } /** * Puts the focus on the first component in the tree of <code>c</code> that * can accept the focus. * * @see #getFocusableComponentOrChild * @param c * Component to focus on. * @return Component that was focused on. */ public static Component focusComponentOrChild(Component c) { return focusComponentOrChild(c, false); } /** * Puts the focus on the first component in the tree of <code>c</code> that * can accept the focus. * * @param c * the root of the component hierarchy to search * @param deepest * if <code>deepest</code> is true the method will focus the * first and deepest component that can accept the focus. For * example, if both a child and its parent are focusable and * <code>deepest</code> is true, the child is focused. * @see #getFocusableComponentOrChild * @return Component that was focused on. */ public static Component focusComponentOrChild(Component c, boolean deepest) { final Component focusable = getFocusableComponentOrChild(c, deepest); if (focusable != null) { focusable.requestFocus(); } return focusable; } /** * Loads an {@link Image} named <code>imageName</code> as a resource * relative to the Class <code>cls</code>. If the <code>Image</code> can not * be loaded, then <code>null</code> is returned. Images loaded here will be * added to an internal cache based upon the full {@link URL} to their * location. * <p/> * <em>This method replaces legacy code from JDeveloper 3.x and earlier.</em> * * @see Class#getResource(String) * @see Toolkit#createImage(URL) * @param imageName * Name of the resource to load. * @param cls * Class to pull resource from. * @return Image loaded from resource. */ public static Image loadFromResource(String imageName, Class<?> cls) { try { final URL url = cls.getResource(imageName); if (url == null) { return null; } Image image = imageCache.get(url.toString()); if (image == null) { image = Toolkit.getDefaultToolkit().createImage(url); imageCache.put(url.toString(), image); } return image; } catch (Exception e) { Log.error(e); } return null; } /** * Returns the ScreenBounds * * @return Array of {@link Rectangle}'s */ public static Rectangle[] getScreenBounds() { GraphicsEnvironment graphicsEnvironment = GraphicsEnvironment .getLocalGraphicsEnvironment(); final GraphicsDevice[] screenDevices = graphicsEnvironment .getScreenDevices(); Rectangle[] screenBounds = new Rectangle[screenDevices.length]; for (int i = 0; i < screenDevices.length; i++) { GraphicsDevice screenDevice = screenDevices[i]; final GraphicsConfiguration defaultConfiguration = screenDevice .getDefaultConfiguration(); screenBounds[i] = defaultConfiguration.getBounds(); } return screenBounds; } /** * Makes all Compontens the same Size * * @param comps */ public static void makeSameSize(JComponent... comps) { if (comps.length == 0) { return; } int max = 0; for (JComponent comp1 : comps) { int w = comp1.getPreferredSize().width; max = w > max ? w : max; } Dimension dim = new Dimension(max, comps[0].getPreferredSize().height); for (JComponent comp : comps) { comp.setPreferredSize(dim); } } /** * Return the hexidecimal color from a java.awt.Color * * @param c * Color to convert. * @return hexadecimal string */ public static String toHTMLColor(Color c) { int color = c.getRGB(); color |= 0xff000000; String s = Integer.toHexString(color); return s.substring(2); } /** * Creates a Tooltip with specified width * * @param text * the Text appearing in the Tooltip * @param width * the width of the Tooltip * @return HTML-String displaying the Tooltip */ public static String createToolTip(String text, int width) { final String htmlColor = toHTMLColor(TOOLTIP_COLOR); return "<html><table width=" + width + " bgColor=" + htmlColor + "><tr><td>" + text + "</td></tr></table></table>"; } /** * Creates a Tooltop * * @param text * , the Text appearing in the Tooltip * @return HTML-String displaying the Tooltip */ public static String createToolTip(String text) { final String htmlColor = toHTMLColor(TOOLTIP_COLOR); return "<html><table bgColor=" + htmlColor + "><tr><td>" + text + "</td></tr></table></table>"; } /** * Highlights Words * * @param text * , the Text containing Words that should be Highlighted * @param query * , the Words that should be Highlighted * @return HTML-String, with Highlighted Words */ public static String getHighlightedWords(String text, String query) { final StringTokenizer tkn = new StringTokenizer(query, " "); int tokenCount = tkn.countTokens(); String[] words = new String[tokenCount]; for (int j = 0; j < tokenCount; j++) { String queryWord = tkn.nextToken(); words[j] = queryWord; } String highlightedWords; try { highlightedWords = StringUtils.highlightWords(text, words, "<font style=background-color:yellow;font-weight:bold;>", "</font>"); } catch (Exception e) { highlightedWords = text; } return highlightedWords; } /** * Creates an {@link ImageIcon} with a Shadow * * @param buf * , the {@link Image} where a Shadow will be applied * @return {@link ImageIcon} with shadow */ public static ImageIcon createShadowPicture(Image buf) { buf = removeTransparency(buf); BufferedImage splash; JLabel label = new JLabel(); int width = buf.getWidth(null); int height = buf.getHeight(null); int extra = 4; splash = new BufferedImage(width + extra, height + extra, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = (Graphics2D) splash.getGraphics(); BufferedImage shadow = new BufferedImage(width + extra, height + extra, BufferedImage.TYPE_INT_ARGB); Graphics g = shadow.getGraphics(); g.setColor(new Color(0.0f, 0.0f, 0.0f, 0.3f)); g.fillRoundRect(0, 0, width, height, 12, 12); g2.drawImage(shadow, getBlurOp(7), 0, 0); g2.drawImage(buf, 0, 0, label); return new ImageIcon(splash); } /** * removes the Transparency of an {@link Image} * * @param image * @return {@link BufferedImage} without Transparency */ public static BufferedImage removeTransparency(Image image) { int w = image.getWidth(null); int h = image.getHeight(null); BufferedImage bi2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); Graphics2D g = bi2.createGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, w, h); g.drawImage(image, 0, 0, null); g.dispose(); return bi2; } /** * Converts a {@link BufferedImage} to {@link Image} * * @param bufferedImage * , the {@link BufferedImage} * @return {@link Image} */ public static Image toImage(BufferedImage bufferedImage) { return Toolkit.getDefaultToolkit().createImage( bufferedImage.getSource()); } /** * * @param size * @return */ private static ConvolveOp getBlurOp(int size) { float[] data = new float[size * size]; float value = 1 / (float) (size * size); for (int i = 0; i < data.length; i++) { data[i] = value; } return new ConvolveOp(new Kernel(size, size, data)); } /** * Converting an {@link Image} into a {@link BufferedImage} Transparency * some Images gets lost * * @param im * @return {@link BufferedImage} * @throws InterruptedException * @throws {@link IOException} */ public static BufferedImage convert(Image im) throws InterruptedException, IOException { //load(im); BufferedImage bi = new BufferedImage(im.getWidth(null), im.getHeight(null), BufferedImage.TYPE_INT_ARGB_PRE); Graphics bg = bi.getGraphics(); bg.drawImage(im, 0, 0, null); bg.dispose(); return bi; } /** * Loading an image via a MediaTracker * * @param image * @throws InterruptedException * @throws IOException */ public static void load(Image image) throws InterruptedException, IOException { MediaTracker tracker = new MediaTracker(new Label()); // any component // will do tracker.addImage(image, 0); tracker.waitForID(0); if (tracker.isErrorID(0)) throw new IOException("error loading image"); } /** * Gets the Bytes from an Image using an FileInputStream * * @param file * , input {@link File} * @return byte[] */ public static byte[] getBytesFromImage(File file) { FileInputStream fileInputStream = null; try { fileInputStream = new FileInputStream(file); byte[] data = new byte[(int) file.length()]; fileInputStream.read(data); fileInputStream.close(); return data; } catch (IOException e) { if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e1) { } } return null; } } /** * Returns a scaled down image if the height or width is smaller than the * image size. * * @param icon * the image icon. * @param newHeight * the preferred height. * @param newWidth * the preferred width. * @return the icon. */ public static ImageIcon scaleImageIcon(ImageIcon icon, int newHeight, int newWidth) { Image img = icon.getImage(); int height = icon.getIconHeight(); int width = icon.getIconWidth(); if (height > newHeight) { height = newHeight; } if (width > newWidth) { width = newWidth; } img = img.getScaledInstance(width, height, Image.SCALE_SMOOTH); return new ImageIcon(img); } /** * Returns a scaled down image if the height or width is smaller than the * image size. * * @param icon * the image icon. * @param newHeight * the preferred height. * @param newWidth * the preferred width. * @return the icon. */ public static ImageIcon scale(ImageIcon icon, int newHeight, int newWidth) { Image img = icon.getImage(); int height = icon.getIconHeight(); int width = icon.getIconWidth(); boolean scaleHeight = height * newWidth > width * newHeight; if (height > newHeight) { // Too tall if (width <= newWidth || scaleHeight) { // Width is okay or height is limiting factor due to aspect // ratio height = newHeight; width = -1; } else { // Width is limiting factor due to aspect ratio height = -1; width = newWidth; } } else if (width > newWidth) { // Too wide and height is okay height = -1; width = newWidth; } else if (scaleHeight) { // Height is limiting factor due to aspect ratio height = newHeight; width = -1; } else { // Width is limiting factor due to aspect ratio height = -1; width = newWidth; } img = img.getScaledInstance(width, height, Image.SCALE_SMOOTH); return new ImageIcon(img); } /** * Returns the native icon, if one exists for the filetype, otherwise * returns a default document icon. * * @param file * the file to check icon type. * @return the native icon, otherwise default document icon. */ public static Icon getIcon(File file) { try { sun.awt.shell.ShellFolder sf = sun.awt.shell.ShellFolder .getShellFolder(file); // Get large icon return new ImageIcon(sf.getIcon(true), sf.getFolderType()); } catch (Exception e) { try { return new JFileChooser().getIcon(file); } catch (Exception e1) { // Do nothing. } } return SparkRes.getImageIcon(SparkRes.DOCUMENT_INFO_32x32); } /** * Converts a File holding an Image into a Buffered Image * * @param file * {@link File} * @return {@link BufferedImage} */ public static BufferedImage getBufferedImage(File file) { // Why wasn't this using it's code that pulled from the file? Hrm. Icon icon = SparkRes.getImageIcon(SparkRes.DOCUMENT_INFO_32x32); BufferedImage bi = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.OPAQUE); Graphics bg = bi.getGraphics(); ImageIcon i = (ImageIcon) icon; bg.drawImage(i.getImage(), 0, 0, null); bg.dispose(); return bi; } /** * Scale an image to fit in a square of the given size, preserving aspect * ratio. * * @param icon * @param newSize * @return */ public static ImageIcon fitToSquare(ImageIcon icon, int newSize) { if (newSize <= 0) { return icon; } final int oldWidth = icon.getIconWidth(); final int oldHeight = icon.getIconHeight(); int newWidth; int newHeight; if (oldHeight >= oldWidth) { newWidth = (int) ((float) oldWidth * newSize / oldHeight); newHeight = newSize; } else { newWidth = newSize; newHeight = (int) ((float) oldHeight * newSize / oldWidth); } final Image img = icon.getImage().getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); return new ImageIcon(img); } // public static void centerWindowOnScreen(Runnable runnable) { // // This method is never used // // } }