001 /* 002 * $Id: JXImagePanel.java,v 1.13 2006/04/26 17:10:52 joshy Exp $ 003 * 004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, 005 * Santa Clara, California 95054, U.S.A. All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 020 */ 021 022 package org.jdesktop.swingx; 023 import java.awt.Cursor; 024 import java.awt.Dimension; 025 import java.awt.Graphics; 026 import java.awt.Graphics2D; 027 import java.awt.Image; 028 import java.awt.Rectangle; 029 import java.awt.event.MouseAdapter; 030 import java.awt.event.MouseEvent; 031 import java.io.File; 032 import java.net.URL; 033 import java.util.logging.Level; 034 import java.util.logging.Logger; 035 import javax.imageio.ImageIO; 036 037 import javax.swing.ImageIcon; 038 import javax.swing.JFileChooser; 039 import javax.swing.JLabel; 040 import javax.swing.SwingUtilities; 041 042 043 /** 044 * <p>A panel that draws an image. The standard (and currently only supported) 045 * mode is to draw the specified image starting at position 0,0 in the 046 * panel. The component&s preferred size is based on the image, unless 047 * explicitly set by the user.</p> 048 * 049 * <p>In the future, the JXImagePanel will also support tiling of images, 050 * scaling, resizing, cropping, segways etc.</p> 051 * 052 * <p>This component also supports allowing the user to set the image. If the 053 * <code>JXImagePanel</code> is editable, then when the user clicks on the 054 * <code>JXImagePanel</code> a FileChooser is shown allowing the user to pick 055 * some other image to use within the <code>JXImagePanel</code>.</p> 056 * 057 * <p>Images to be displayed can be set based on URL, Image, etc. 058 * 059 * @author rbair 060 */ 061 public class JXImagePanel extends JXPanel { 062 public static enum Style {CENTERED, TILED, SCALED}; 063 private static final Logger LOG = Logger.getLogger(JXImagePanel.class 064 .getName()); 065 /** 066 * Text informing the user that clicking on this component will allow them to set the image 067 */ 068 private static final String TEXT = "<html><i><b>Click here<br>to set the image</b></i></html>"; 069 /** 070 * The image to draw 071 */ 072 private Image img; 073 /** 074 * If true, then the image can be changed. Perhaps a better name is 075 * "readOnly", but editable was chosen to be more consistent 076 * with other Swing components. 077 */ 078 private boolean editable = false; 079 /** 080 * The mouse handler that is used if the component is editable 081 */ 082 private MouseHandler mhandler = new MouseHandler(); 083 /** 084 * If not null, then the user has explicitly set the preferred size of 085 * this component, and this should be honored 086 */ 087 private Dimension preferredSize; 088 /** 089 * Specifies how to draw the image, i.e. what kind of Style to use 090 * when drawing 091 */ 092 private Style style = Style.CENTERED; 093 094 public JXImagePanel() { 095 } 096 097 public JXImagePanel(URL imageUrl) { 098 try { 099 setImage(ImageIO.read(imageUrl)); 100 } catch (Exception e) { 101 //TODO need convert to something meaningful 102 LOG.log(Level.WARNING, "", e); 103 } 104 } 105 106 /** 107 * Sets the image to use for the background of this panel. This image is 108 * painted whether the panel is opaque or translucent. 109 * @param image if null, clears the image. Otherwise, this will set the 110 * image to be painted. If the preferred size has not been explicitly set, 111 * then the image dimensions will alter the preferred size of the panel. 112 */ 113 public void setImage(Image image) { 114 if (image != img) { 115 Image oldImage = img; 116 img = image; 117 firePropertyChange("image", oldImage, img); 118 invalidate(); 119 repaint(); 120 } 121 } 122 123 /** 124 * @return the image used for painting the background of this panel 125 */ 126 public Image getImage() { 127 return img; 128 } 129 130 /** 131 * @param editable 132 */ 133 public void setEditable(boolean editable) { 134 if (editable != this.editable) { 135 //if it was editable, remove the mouse handler 136 if (this.editable) { 137 removeMouseListener(mhandler); 138 } 139 this.editable = editable; 140 //if it is now editable, add the mouse handler 141 if (this.editable) { 142 addMouseListener(new MouseHandler()); 143 } 144 setToolTipText(editable ? TEXT : ""); 145 firePropertyChange("editable", !editable, editable); 146 repaint(); 147 } 148 } 149 150 /** 151 * @return whether the image for this panel can be changed or not via 152 * the UI. setImage may still be called, even if <code>isEditable</code> 153 * returns false. 154 */ 155 public boolean isEditable() { 156 return editable; 157 } 158 159 /** 160 * Sets what style to use when painting the image 161 * 162 * @param s 163 */ 164 public void setStyle(Style s) { 165 if (style != s) { 166 Style oldStyle = style; 167 style = s; 168 firePropertyChange("style", oldStyle, s); 169 repaint(); 170 } 171 } 172 173 /** 174 * @return the Style used for drawing the image (CENTERED, TILED, etc). 175 */ 176 public Style getStyle() { 177 return style; 178 } 179 180 public void setPreferredSize(Dimension pref) { 181 preferredSize = pref; 182 super.setPreferredSize(pref); 183 } 184 185 public Dimension getPreferredSize() { 186 if (preferredSize == null && img != null) { 187 //it has not been explicitly set, so return the width/height of the image 188 int width = img.getWidth(null); 189 int height = img.getHeight(null); 190 if (width == -1 || height == -1) { 191 return super.getPreferredSize(); 192 } 193 return new Dimension(width, height); 194 } else { 195 return super.getPreferredSize(); 196 } 197 } 198 199 /** 200 * Overriden to paint the image on the panel 201 */ 202 protected void paintComponent(Graphics g) { 203 super.paintComponent(g); 204 // Insets insets = getInsets(); 205 // g.fillRect(insets.left, insets.top, getWidth() - insets.right - insets.left, getHeight() - insets.bottom - insets.top); 206 Graphics2D g2 = (Graphics2D)g; 207 if (img != null) { 208 int imgWidth = img.getWidth(null); 209 int imgHeight = img.getHeight(null); 210 if (imgWidth == -1 || imgHeight == -1) { 211 //image hasn't completed loading, return 212 return; 213 } 214 215 switch (style) { 216 case CENTERED: 217 Rectangle clipRect = g2.getClipBounds(); 218 int imageX = (getWidth() - imgWidth) / 2; 219 int imageY = (getHeight() - imgHeight) / 2; 220 Rectangle r = SwingUtilities.computeIntersection(imageX, imageY, imgWidth, imgHeight, clipRect); 221 if (r.x == 0 && r.y == 0 && (r.width == 0 || r.height == 0)) { 222 return; 223 } 224 //I have my new clipping rectangle "r" in clipRect space. 225 //It is therefore the new clipRect. 226 clipRect = r; 227 //since I have the intersection, all I need to do is adjust the 228 //x & y values for the image 229 int txClipX = clipRect.x - imageX; 230 int txClipY = clipRect.y - imageY; 231 int txClipW = clipRect.width; 232 int txClipH = clipRect.height; 233 234 g2.drawImage(img, clipRect.x, clipRect.y, clipRect.x + clipRect.width, clipRect.y + clipRect.height, 235 txClipX, txClipY, txClipX + txClipW, txClipY + txClipH, null); 236 break; 237 case TILED: 238 case SCALED: 239 g2.drawImage(img, 0, 0, getWidth(), getHeight(), null); 240 break; 241 default: 242 LOG.fine("unimplemented"); 243 g2.drawImage(img, 0, 0, this); 244 break; 245 } 246 } 247 } 248 249 /** 250 * Handles click events on the component 251 */ 252 private class MouseHandler extends MouseAdapter { 253 private Cursor oldCursor; 254 private JFileChooser chooser; 255 256 public void mouseClicked(MouseEvent evt) { 257 if (chooser == null) { 258 chooser = new JFileChooser(); 259 } 260 int retVal = chooser.showOpenDialog(JXImagePanel.this); 261 if (retVal == JFileChooser.APPROVE_OPTION) { 262 File file = chooser.getSelectedFile(); 263 try { 264 setImage(new ImageIcon(file.toURI().toURL()).getImage()); 265 } catch (Exception ex) { 266 } 267 } 268 } 269 270 public void mouseEntered(MouseEvent evt) { 271 if(evt.getSource() instanceof JLabel) { 272 JLabel label = (JLabel)evt.getSource(); 273 if (oldCursor == null) { 274 oldCursor = label.getCursor(); 275 label.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 276 } 277 } 278 } 279 280 public void mouseExited(MouseEvent evt) { 281 JLabel label = (JLabel)evt.getSource(); 282 if (oldCursor != null) { 283 label.setCursor(oldCursor); 284 oldCursor = null; 285 } 286 } 287 } 288 } 289