001/* 002 * $Id: JXImageView.java 4153 2012-02-02 14:17:49Z kleopatra $ 003 * 004 * Copyright 2006 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 022package org.jdesktop.swingx; 023 024import java.awt.Color; 025import java.awt.Component; 026import java.awt.Cursor; 027import java.awt.Dialog; 028import java.awt.FileDialog; 029import java.awt.Frame; 030import java.awt.Graphics; 031import java.awt.Graphics2D; 032import java.awt.Image; 033import java.awt.Paint; 034import java.awt.Point; 035import java.awt.RenderingHints; 036import java.awt.Window; 037import java.awt.datatransfer.DataFlavor; 038import java.awt.datatransfer.Transferable; 039import java.awt.datatransfer.UnsupportedFlavorException; 040import java.awt.event.ActionEvent; 041import java.awt.event.InputEvent; 042import java.awt.event.MouseEvent; 043import java.awt.geom.AffineTransform; 044import java.awt.geom.Point2D; 045import java.awt.image.AffineTransformOp; 046import java.awt.image.BufferedImage; 047import java.awt.image.BufferedImageOp; 048import java.io.File; 049import java.io.IOException; 050import java.net.URL; 051import java.util.ArrayList; 052import java.util.List; 053import java.util.logging.Logger; 054 055import javax.imageio.ImageIO; 056import javax.swing.AbstractAction; 057import javax.swing.Action; 058import javax.swing.JComponent; 059import javax.swing.SwingUtilities; 060import javax.swing.TransferHandler; 061import javax.swing.event.MouseInputAdapter; 062 063import org.jdesktop.beans.JavaBean; 064import org.jdesktop.swingx.error.ErrorListener; 065import org.jdesktop.swingx.error.ErrorSupport; 066import org.jdesktop.swingx.painter.MattePainter; 067import org.jdesktop.swingx.util.GraphicsUtilities; 068import org.jdesktop.swingx.util.PaintUtils; 069 070/** 071 * <p>A panel which shows an image centered. The user can drag an image into the 072 * panel from other applications and move the image around within the view. 073 * The JXImageView has built in actions for scaling, rotating, opening a new 074 * image, and saving. These actions can be obtained using the relevant get*Action() 075 * methods. 076 *</p> 077 * 078 * <p>TODO: has dashed rect and text indicating you should drag there.</p> 079 * 080 * 081 * <p>If the user drags more than one photo at a time into the JXImageView only 082 * the first photo will be loaded and shown. Any errors generated internally, 083 * such as dragging in a list of files which are not images, will be reported 084 * to any attached {@link org.jdesktop.swingx.error.ErrorListener} added by the 085 * <CODE>{@link #addErrorListener}()</CODE> method.</p> 086 * 087 * @author Joshua Marinacci joshua.marinacci@sun.com 088 */ 089@JavaBean 090public class JXImageView extends JXPanel { 091 092 private Logger log = Logger.getLogger(JXImageView.class.getName()); 093 /* ======= instance variables ========= */ 094 // the image this view will show 095 private Image image; 096 // the url of the image, if available 097 private URL imageURL; 098 099 // support for error listeners 100 private ErrorSupport errorSupport = new ErrorSupport(this); 101 102 // location to draw image. if null then draw in the center 103 private Point2D imageLocation; 104 // the scale for drawing the image 105 private double scale = 1.0; 106 // controls whether the user can move images around 107 private boolean editable = true; 108 // the handler for moving the image around within the panel 109 private MoveHandler moveHandler = new MoveHandler(this); 110 // controls the drag part of drag and drop 111 private boolean dragEnabled = false; 112 // controls the filename of the dropped file 113 private String exportName = "UntitledImage"; 114 // controls the format and filename extension of the dropped file 115 private String exportFormat = "png"; 116 117 /** Creates a new instance of JXImageView */ 118 public JXImageView() { 119 // fix for: java.net/jira/browse/SWINGX-1479 120 setBackgroundPainter(new MattePainter(PaintUtils.getCheckerPaint(Color.white,new Color(250,250,250),50))); 121 setEditable(true); 122 } 123 124 125 126 /* ========= properties ========= */ 127 /** 128 * Gets the current image location. This location can be changed programmatically 129 * or by the user dragging the image within the JXImageView. 130 * @return the current image location 131 */ 132 public Point2D getImageLocation() { 133 return imageLocation; 134 } 135 136 /** 137 * Set the current image location. 138 * @param imageLocation The new image location. 139 */ 140 public void setImageLocation(Point2D imageLocation) { 141 Point2D old = getImageLocation(); 142 this.imageLocation = imageLocation; 143 firePropertyChange("imageLocation", old, getImageLocation()); 144 repaint(); 145 } 146 147 /** 148 * Gets the currently set image, or null if no image is set. 149 * @return the currently set image, or null if no image is set. 150 */ 151 public Image getImage() { 152 return image; 153 } 154 155 /** 156 * Sets the current image. Can set null if there should be no image show. 157 * @param image the new image to set, or null. 158 */ 159 public void setImage(Image image) { 160 Image oldImage = getImage(); 161 this.image = image; 162 setImageLocation(null); 163 setScale(1.0); 164 firePropertyChange("image",oldImage,image); 165 repaint(); 166 } 167 168 /** 169 * Set the current image to an image pointed to by this URL. 170 * @param url a URL pointing to an image, or null 171 * @throws java.io.IOException thrown if the image cannot be loaded 172 */ 173 public void setImage(URL url) throws IOException { 174 setImageURL(url); 175 //setImage(ImageIO.read(url)); 176 } 177 178 /** 179 * Set the current image to an image pointed to by this File. 180 * @param file a File pointing to an image 181 * @throws java.io.IOException thrown if the image cannot be loaded 182 */ 183 public void setImage(File file) throws IOException { 184 setImageURL(file.toURI().toURL()); 185 } 186 187 /** 188 * Gets the current image scale . When the scale is set to 1.0 189 * then one image pixel = one screen pixel. When scale < 1.0 the draw image 190 * will be smaller than it's real size. When scale > 1.0 the drawn image will 191 * be larger than it's real size. 1.0 is the default value. 192 * @return the current image scale 193 */ 194 public double getScale() { 195 return scale; 196 } 197 198 /** 199 * Sets the current image scale . When the scale is set to 1.0 200 * then one image pixel = one screen pixel. When scale < 1.0 the draw image 201 * will be smaller than it's real size. When scale > 1.0 the drawn image will 202 * be larger than it's real size. 1.0 is the default value. 203 * @param scale the new image scale 204 */ 205 public void setScale(double scale) { 206 double oldScale = this.scale; 207 this.scale = scale; 208 this.firePropertyChange("scale",oldScale,scale); 209 repaint(); 210 } 211 212 /** 213 * Returns whether or not the user can drag images. 214 * @return whether or not the user can drag images 215 */ 216 public boolean isEditable() { 217 return editable; 218 } 219 220 /** 221 * Sets whether or not the user can drag images. When set to true the user can 222 * drag the photo around with their mouse. Also the cursor will be set to the 223 * 'hand' cursor. When set to false the user cannot drag photos around 224 * and the cursor will be set to the default. 225 * @param editable whether or not the user can drag images 226 */ 227 public void setEditable(boolean editable) { 228 boolean old = isEditable(); 229 this.editable = editable; 230 if(editable) { 231 addMouseMotionListener(moveHandler); 232 addMouseListener(moveHandler); 233 this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 234 try { 235 this.setTransferHandler(new DnDHandler()); 236 } catch (ClassNotFoundException ex) { 237 ex.printStackTrace(); 238 fireError(ex); 239 } 240 } else { 241 removeMouseMotionListener(moveHandler); 242 removeMouseListener(moveHandler); 243 this.setCursor(Cursor.getDefaultCursor()); 244 setTransferHandler(null); 245 } 246 firePropertyChange("editable", old, isEditable()); 247 } 248 249 /** 250 * Sets the <CODE>dragEnabled</CODE> property, which determines whether or not 251 * the user can drag images out of the image view and into other components or 252 * application. Note: <B>setting 253 * this to true will disable the ability to move the image around within the 254 * well.</B>, though it will not change the <b>editable</b> property directly. 255 * @param dragEnabled the value to set the dragEnabled property to. 256 */ 257 public void setDragEnabled(boolean dragEnabled) { 258 boolean old = isDragEnabled(); 259 this.dragEnabled = dragEnabled; 260 firePropertyChange("dragEnabled", old, isDragEnabled()); 261 } 262 263 /** 264 * Gets the current value of the <CODE>dragEnabled</CODE> property. 265 * @return the current value of the <CODE>dragEnabled</CODE> property 266 */ 267 public boolean isDragEnabled() { 268 return dragEnabled; 269 } 270 271 /** 272 * Adds an ErrorListener to the list of listeners to be notified 273 * of ErrorEvents 274 * @param el an ErrorListener to add 275 */ 276 public void addErrorListener(ErrorListener el) { 277 errorSupport.addErrorListener(el); 278 } 279 280 /** 281 * Remove an ErrorListener from the list of listeners to be notified of ErrorEvents. 282 * @param el an ErrorListener to remove 283 */ 284 public void removeErrorListener(ErrorListener el) { 285 errorSupport.removeErrorListener(el); 286 } 287 288 /** 289 * Send a new ErrorEvent to all registered ErrorListeners 290 * @param throwable the Error or Exception which was thrown 291 */ 292 protected void fireError(Throwable throwable) { 293 errorSupport.fireErrorEvent(throwable); 294 } 295 296 297 private static FileDialog getSafeFileDialog(Component comp) { 298 Window win = SwingUtilities.windowForComponent(comp); 299 if(win instanceof Dialog) { 300 return new FileDialog((Dialog)win); 301 } 302 if(win instanceof Frame) { 303 return new FileDialog((Frame)win); 304 } 305 return null; 306 } 307 308 // an action which will open a file chooser and load the selected image 309 // if any. 310 /** 311 * Returns an Action which will open a file chooser, ask the user for an image file 312 * then load the image into the view. If the load fails an error will be fired 313 * to all registered ErrorListeners 314 * @return the action 315 * @see ErrorListener 316 * @deprecated see SwingX issue 990 317 */ 318 @Deprecated 319 public Action getOpenAction() { 320 Action action = new AbstractAction() { 321 @Override 322 public void actionPerformed(ActionEvent actionEvent) { 323 FileDialog fd = getSafeFileDialog(JXImageView.this); 324 fd.setMode(FileDialog.LOAD); 325 fd.setVisible(true); 326 if(fd.getFile() != null) { 327 try { 328 setImage(new File(fd.getDirectory(),fd.getFile())); 329 } catch (IOException ex) { 330 fireError(ex); 331 } 332 } 333 /* 334 JFileChooser chooser = new JFileChooser(); 335 chooser.showOpenDialog(JXImageView.this); 336 File file = chooser.getSelectedFile(); 337 if(file != null) { 338 try { 339 setImage(file); 340 } catch (IOException ex) { 341 log.fine(ex.getMessage()); 342 ex.printStackTrace(); 343 fireError(ex); 344 } 345 } 346 */ 347 } 348 }; 349 action.putValue(Action.NAME,"Open"); 350 return action; 351 } 352 353 // an action that will open a file chooser then save the current image to 354 // the selected file, if any. 355 /** 356 * Returns an Action which will open a file chooser, ask the user for an image file 357 * then save the image from the view. If the save fails an error will be fired 358 * to all registered ErrorListeners 359 * @return an Action 360 * @deprecated see SwingX issue 990 361 */ 362 @Deprecated 363 public Action getSaveAction() { 364 Action action = new AbstractAction() { 365 @Override 366 public void actionPerformed(ActionEvent evt) { 367 Image img = getImage(); 368 BufferedImage dst = new BufferedImage( 369 img.getWidth(null), 370 img.getHeight(null), 371 BufferedImage.TYPE_INT_ARGB); 372 Graphics2D g = (Graphics2D)dst.getGraphics(); 373 374 try { 375 // smooth scaling 376 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 377 RenderingHints.VALUE_INTERPOLATION_BICUBIC); 378 g.drawImage(img, 0, 0, null); 379 } finally { 380 g.dispose(); 381 } 382 FileDialog fd = new FileDialog((Frame)SwingUtilities.windowForComponent(JXImageView.this)); 383 fd.setMode(FileDialog.SAVE); 384 fd.setVisible(true); 385 if(fd.getFile() != null) { 386 try { 387 ImageIO.write(dst,"png",new File(fd.getDirectory(),fd.getFile())); 388 } catch (IOException ex) { 389 fireError(ex); 390 } 391 } 392 /* 393 JFileChooser chooser = new JFileChooser(); 394 chooser.showSaveDialog(JXImageView.this); 395 File file = chooser.getSelectedFile(); 396 if(file != null) { 397 try { 398 ImageIO.write(dst,"png",file); 399 } catch (IOException ex) { 400 log.fine(ex.getMessage()); 401 ex.printStackTrace(); 402 fireError(ex); 403 } 404 } 405 */ 406 } 407 }; 408 409 action.putValue(Action.NAME,"Save"); 410 return action; 411 } 412 413 /** 414 * Get an action which will rotate the currently selected image clockwise. 415 * @return an action 416 * @deprecated see SwingX issue 990 417 */ 418 @Deprecated 419 public Action getRotateClockwiseAction() { 420 Action action = new AbstractAction() { 421 @Override 422 public void actionPerformed(ActionEvent evt) { 423 Image img = getImage(); 424 BufferedImage src = new BufferedImage( 425 img.getWidth(null), 426 img.getHeight(null), 427 BufferedImage.TYPE_INT_ARGB); 428 BufferedImage dst = new BufferedImage( 429 img.getHeight(null), 430 img.getWidth(null), 431 BufferedImage.TYPE_INT_ARGB); 432 Graphics2D g = (Graphics2D)src.getGraphics(); 433 434 try { 435 // smooth scaling 436 g.drawImage(img, 0, 0, null); 437 } finally { 438 g.dispose(); 439 } 440 441 AffineTransform trans = AffineTransform.getRotateInstance(Math.PI/2,0,0); 442 trans.translate(0,-src.getHeight()); 443 BufferedImageOp op = new AffineTransformOp(trans, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); 444 op.filter(src,dst); 445 setImage(dst); 446 } 447 }; 448 action.putValue(Action.NAME,"Rotate Clockwise"); 449 return action; 450 } 451 452 /** 453 * Gets an action which will rotate the current image counter clockwise. 454 * @return an Action 455 * @deprecated see SwingX issue 990 456 */ 457 @Deprecated 458 public Action getRotateCounterClockwiseAction() { 459 Action action = new AbstractAction() { 460 @Override 461 public void actionPerformed(ActionEvent evt) { 462 Image img = getImage(); 463 BufferedImage src = new BufferedImage( 464 img.getWidth(null), 465 img.getHeight(null), 466 BufferedImage.TYPE_INT_ARGB); 467 BufferedImage dst = new BufferedImage( 468 img.getHeight(null), 469 img.getWidth(null), 470 BufferedImage.TYPE_INT_ARGB); 471 Graphics2D g = (Graphics2D)src.getGraphics(); 472 473 try { 474 // smooth scaling 475 g.drawImage(img, 0, 0, null); 476 } finally { 477 g.dispose(); 478 } 479 AffineTransform trans = AffineTransform.getRotateInstance(-Math.PI/2,0,0); 480 trans.translate(-src.getWidth(),0); 481 BufferedImageOp op = new AffineTransformOp(trans, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); 482 op.filter(src,dst); 483 setImage(dst); 484 } 485 }; 486 action.putValue(Action.NAME, "Rotate CounterClockwise"); 487 return action; 488 } 489 490 /** 491 * Gets an action which will zoom the current image out by a factor of 2. 492 * @return an action 493 * @deprecated see SwingX issue 990 494 */ 495 @Deprecated 496 public Action getZoomOutAction() { 497 Action action = new AbstractAction() { 498 @Override 499 public void actionPerformed(ActionEvent actionEvent) { 500 setScale(getScale()*0.5); 501 } 502 }; 503 action.putValue(Action.NAME,"Zoom Out"); 504 return action; 505 } 506 507 /** 508 * Gets an action which will zoom the current image in by a factor of 2 509 * @return an action 510 * @deprecated see SwingX issue 990 511 */ 512 @Deprecated 513 public Action getZoomInAction() { 514 Action action = new AbstractAction() { 515 @Override 516 public void actionPerformed(ActionEvent actionEvent) { 517 setScale(getScale()*2); 518 } 519 }; 520 action.putValue(Action.NAME,"Zoom In"); 521 return action; 522 } 523 /* === overriden methods === */ 524 525 /** 526 * Implementation detail. 527 * @param g 528 */ 529 @Override 530 protected void paintComponent(Graphics g) { 531 super.paintComponent(g); 532 if(getImage() != null) { 533 Point2D center = new Point2D.Double(getWidth()/2,getHeight()/2); 534 if(getImageLocation() != null) { 535 center = getImageLocation(); 536 } 537 Point2D loc = new Point2D.Double(); 538 double width = getImage().getWidth(null)*getScale(); 539 double height = getImage().getHeight(null)*getScale(); 540 loc.setLocation(center.getX()-width/2, center.getY()-height/2); 541 g.drawImage(getImage(), (int)loc.getX(), (int)loc.getY(), 542 (int)width,(int)height, 543 null); 544 } 545 } 546 547 548 /* === Internal helper classes === */ 549 550 private class MoveHandler extends MouseInputAdapter { 551 private JXImageView panel; 552 private Point prev = null; 553 private Point start = null; 554 public MoveHandler(JXImageView panel) { 555 this.panel = panel; 556 } 557 558 @Override 559 public void mousePressed(MouseEvent evt) { 560 prev = evt.getPoint(); 561 start = prev; 562 } 563 564 @Override 565 public void mouseDragged(MouseEvent evt) { 566 Point curr = evt.getPoint(); 567 568 if(isDragEnabled()) { 569 //log.fine("testing drag enabled: " + curr + " " + start); 570 //log.fine("distance = " + curr.distance(start)); 571 if(curr.distance(start) > 5) { 572 JXImageView.this.log.fine("starting the drag: "); 573 panel.getTransferHandler().exportAsDrag((JComponent)evt.getSource(),evt,TransferHandler.COPY); 574 return; 575 } 576 } 577 578 int offx = curr.x - prev.x; 579 int offy = curr.y - prev.y; 580 Point2D offset = getImageLocation(); 581 if (offset == null) { 582 if (image != null) { 583 offset = new Point2D.Double(getWidth() / 2, getHeight() / 2); 584 } else { 585 offset = new Point2D.Double(0, 0); 586 } 587 } 588 offset = new Point2D.Double(offset.getX() + offx, offset.getY() + offy); 589 setImageLocation(offset); 590 prev = curr; 591 repaint(); 592 } 593 594 @Override 595 public void mouseReleased(MouseEvent evt) { 596 prev = null; 597 } 598 } 599 600 private class DnDHandler extends TransferHandler { 601 DataFlavor urlFlavor; 602 603 public DnDHandler() throws ClassNotFoundException { 604 urlFlavor = new DataFlavor("application/x-java-url;class=java.net.URL"); 605 } 606 607 @Override 608 public void exportAsDrag(JComponent c, InputEvent evt, int action) { 609 //log.fine("exportting as drag"); 610 super.exportAsDrag(c,evt,action); 611 } 612 @Override 613 public int getSourceActions(JComponent c) { 614 //log.fine("get source actions: " + c); 615 return COPY; 616 } 617 @Override 618 protected void exportDone(JComponent source, Transferable data, int action) { 619 //log.fine("exportDone: " + source + " " + data + " " +action); 620 } 621 622 @Override 623 public boolean canImport(JComponent c, DataFlavor[] flavors) { 624 //log.fine("canImport:" + c); 625 for (int i = 0; i < flavors.length; i++) { 626 //log.fine("testing: "+flavors[i]); 627 if (DataFlavor.javaFileListFlavor.equals(flavors[i])) { 628 return true; 629 } 630 if (DataFlavor.imageFlavor.equals(flavors[i])) { 631 return true; 632 } 633 if (urlFlavor.match(flavors[i])) { 634 return true; 635 } 636 637 } 638 return false; 639 } 640 641 @Override 642 protected Transferable createTransferable(JComponent c) { 643 JXImageView view = (JXImageView)c; 644 return new ImageTransferable(view.getImage(), 645 view.getExportName(), view.getExportFormat()); 646 } 647 648 @Override 649 @SuppressWarnings("unchecked") 650 public boolean importData(JComponent comp, Transferable t) { 651 if (canImport(comp, t.getTransferDataFlavors())) { 652 try { 653 if(t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { 654 List<File> files = (List<File>) t.getTransferData(DataFlavor.javaFileListFlavor); 655 //log.fine("doing file list flavor"); 656 if (files.size() > 0) { 657 File file = files.get(0); 658 //log.fine("readingt hte image: " + file.getCanonicalPath()); 659 /*Iterator it = ImageIO.getImageReaders(new FileInputStream(file)); 660 while(it.hasNext()) { 661 log.fine("can read: " + it.next()); 662 }*/ 663 setImageString(file.toURI().toURL().toString()); 664 //BufferedImage img = ImageIO.read(file.toURI().toURL()); 665 //setImage(img); 666 return true; 667 } 668 } 669 //log.fine("doing a uri list"); 670 Object obj = t.getTransferData(urlFlavor); 671 //log.fine("obj = " + obj + " " + obj.getClass().getPackage() + " " 672 // + obj.getClass().getName()); 673 if(obj instanceof URL) { 674 setImageString(((URL)obj).toString()); 675 } 676 return true; 677 } catch (Exception ex) { 678 log .severe(ex.getMessage()); 679 ex.printStackTrace(); 680 fireError(ex); 681 } 682 } 683 return false; 684 } 685 686 } 687 688 689 private class ImageTransferable implements Transferable { 690 private Image img; 691 private List<File> files; 692 private String exportName, exportFormat; 693 public ImageTransferable(Image img, String exportName, String exportFormat) { 694 this.img = img; 695 this.exportName = exportName; 696 this.exportFormat = exportFormat; 697 } 698 699 @Override 700 public DataFlavor[] getTransferDataFlavors() { 701 return new DataFlavor[] { DataFlavor.imageFlavor, 702 DataFlavor.javaFileListFlavor }; 703 } 704 705 @Override 706 public boolean isDataFlavorSupported(DataFlavor flavor) { 707 if(flavor == DataFlavor.imageFlavor) { 708 return true; 709 } 710 return flavor == DataFlavor.javaFileListFlavor; 711 } 712 713 @Override 714 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { 715 //log.fine("doing get trans data: " + flavor); 716 if(flavor == DataFlavor.imageFlavor) { 717 return img; 718 } 719 if(flavor == DataFlavor.javaFileListFlavor) { 720 if(files == null) { 721 files = new ArrayList<File>(); 722 File file = File.createTempFile(exportName,"."+exportFormat); 723 //log.fine("writing to: " + file); 724 ImageIO.write(GraphicsUtilities.convertToBufferedImage(img),exportFormat,file); 725 files.add(file); 726 } 727 //log.fine("returning: " + files); 728 return files; 729 } 730 return null; 731 } 732 } 733 734 public String getExportName() { 735 return exportName; 736 } 737 738 public void setExportName(String exportName) { 739 String old = getExportName(); 740 this.exportName = exportName; 741 firePropertyChange("exportName", old, getExportName()); 742 } 743 744 public String getExportFormat() { 745 return exportFormat; 746 } 747 748 public void setExportFormat(String exportFormat) { 749 String old = getExportFormat(); 750 this.exportFormat = exportFormat; 751 firePropertyChange("exportFormat", old, getExportFormat()); 752 } 753 754 public URL getImageURL() { 755 return imageURL; 756 } 757 758 public void setImageURL(URL imageURL) throws IOException { 759 URL old = getImageURL(); 760 this.imageURL = imageURL; 761 firePropertyChange("imageURL", old, getImageURL()); 762 setImage(ImageIO.read(getImageURL())); 763 } 764 765 /** Returns the current image's URL (if available) as a string. 766 * If the image has no URL, or if there is no image, then this 767 * method will return null. 768 * @return the url of the image as a string 769 */ 770 public String getImageString() { 771 if(getImageURL() == null) { 772 return null; 773 } 774 return getImageURL().toString(); 775 } 776 777 /** Sets the current image using a string. This string <b>must</b> 778 * contain a valid URL. 779 * @param url string of a URL 780 * @throws java.io.IOException thrown if the URL does not parse 781 */ 782 public void setImageString(String url) throws IOException { 783 String old = getImageString(); 784 setImageURL(new URL(url)); 785 firePropertyChange("imageString", old, url); 786 } 787 788}