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