Question

I'm trying to add a web URL launching capability to the basic SUN (Oracle) Java Swing ImageIcon gallery demo: http://docs.oracle.com/javase/tutorial/uiswing/components/icon.html#example

Requirement The purpose is to create a Swing GUI web link launcher that has the ability to launch a link via the user's default web browser using the JRE 1.6+ Desktop API launcher capability, and open up a different URL depending on which image was last clicked.

Problem The problem I seem to be having is that every time a new ImageIcon is created in the ThumbnailAction constructor, it seems to be not destroying the previous Image and simply painting over top of it. The unwanted side-effect being that the old Image's MouseListeners and metadata are still active, so trying to click to launch a URL actually loads the URL of the previously loaded Image(s) as well as the currently in-focus Image's URL. If you look at the console print logs you can also see the MouseEntered and Exited fire multiple times, once for each image that has been swapped so far. So if the user makes 3 or 4 swaps they will launch that many URLs. Not the desired effect.

If anyone knows what the problem is it would be much appreciated, here are my modifications to the "IconDemoApp.java" file (UPDATED 2012-01-31):

/*
 * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle or the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */ 

package components;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.AbstractAction;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

/**
 * This application is intended to demonstrate the loading of image files into icons
 * for use in a Swing user interface. It creates a toolbar with a thumbnail preview
 * of each image.  Clicking on the thumbnail will show the full image
 * in the main display area.
 *
 * IconDemoApp.java requires the following files: <br>
 * The following files are copyright 2006 spriggs.net and licensed under a
 * Creative Commons License (http://creativecommons.org/licenses/by-sa/3.0/)
 * <br>
 * images/sunw01.jpg <br>
 * images/sunw02.jpg <br>
 * images/sunw03.jpg <br>
 * images/sunw04.jpg <br>
 * images/sunw05.jpg <br>
 *
 * @author Collin Fagan
 * @date 7/25/2007
 * @version 2.0
 */
public class IconDemoApp extends JFrame {

    /**
     * Main entry point to the demo. Loads the Swing elements on the "Event
     * Dispatch Thread".
     *
     * @param args
     */
    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                IconDemoApp app = new IconDemoApp();
                app.setVisible(true);
            }
        });
    }

        /* 
     * MissingIcon
     *  Internal class for how to recover from missing icons.
     *  (displays empty image placeholder with red X and white background)
     */
    class MissingIcon implements Icon {

        private int width = 32;
        private int height = 32;

        private BasicStroke stroke = new BasicStroke(4);

        public void paintIcon(Component c, Graphics g, int x, int y) {
            Graphics2D g2d = (Graphics2D) g.create();

            g2d.setColor(Color.WHITE);
            g2d.fillRect(x +1 ,y + 1,width -2 ,height -2);

            g2d.setColor(Color.BLACK);
            g2d.drawRect(x +1 ,y + 1,width -2 ,height -2);

            g2d.setColor(Color.RED);

            g2d.setStroke(stroke);
            g2d.drawLine(x +10, y + 10, x + width -10, y + height -10);
            g2d.drawLine(x +10, y + height -10, x + width -10, y + 10);

            g2d.dispose();
        }

        public int getIconWidth() {
            return width;
        }

        public int getIconHeight() {
            return height;
        }
    }

    /**
     * Default constructor for the demo.
     */
    public IconDemoApp() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Icon Demo: Please Select an Image");

        // A label for displaying the pictures
        photographLabel.setVerticalTextPosition(JLabel.BOTTOM);
        photographLabel.setHorizontalTextPosition(JLabel.CENTER);
        photographLabel.setHorizontalAlignment(JLabel.CENTER);
        photographLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

        // We add two glue components. Later in process() we will add thumbnail buttons
        // to the toolbar inbetween thease glue compoents. This will center the
        // buttons in the toolbar.
        buttonBar.add(Box.createGlue());
        buttonBar.add(Box.createGlue());

        add(buttonBar, BorderLayout.SOUTH);
        add(photographLabel, BorderLayout.CENTER);

        setSize(400, 300);

        // this centers the frame on the screen
        setLocationRelativeTo(null);

        // start the image loading SwingWorker in a background thread
        loadimages.execute();
    }

    /**
     * SwingWorker class that loads the images a background thread and calls publish
     * when a new one is ready to be displayed.
     *
     * We use Void as the first SwingWroker param as we do not need to return
     * anything from doInBackground().
     */
    private SwingWorker<Void, ThumbnailAction> loadimages = new SwingWorker<Void, ThumbnailAction>() {

        /**
         * Creates full size and thumbnail versions of the target image files.
         */
        @Override
        protected Void doInBackground() throws Exception {
            for (int i = 0; i < titles.length; i++) {
                ImageIcon icon;
                icon = createImageIcon(imagedir + thumbs[i], titles[i]);

                ThumbnailAction thumbAction;
                if(icon != null){

                    ImageIcon thumbnailIcon = new ImageIcon(getScaledImage(icon.getImage(), 32, 32));

                    thumbAction = new ThumbnailAction(icon, thumbnailIcon, titles[i], descs[i], links[i]);

                }else{
                    // the image failed to load for some reason
                    // so load a placeholder instead
                    thumbAction = new ThumbnailAction(placeholderIcon, placeholderIcon, titles[i], descs[i], links[i]);
                }
                publish(thumbAction);
            }
            // unfortunately we must return something, and only null is valid to
            // return when the return type is void.
            return null;
        }

        /**
         * Process all loaded images.
         */
        @Override
        protected void process(List<ThumbnailAction> chunks) {
            for (ThumbnailAction thumbAction : chunks) {
                JButton thumbButton = new JButton(thumbAction);
                // add the new button BEFORE the last glue
                // this centers the buttons in the toolbar
                buttonBar.add(thumbButton, buttonBar.getComponentCount() - 1);
            }
        }
    };

    /**
     * Creates an ImageIcon if the path is valid.
     * @param String - resource path
     * @param String - description of the file
     */
    protected ImageIcon createImageIcon(String path, String description) {
        java.net.URL imgURL = getClass().getResource(path);
        if (imgURL != null) {
            return new ImageIcon(imgURL, description);
        } 
        else {
            System.err.println("Couldn't find file: " + path);
            return null;
        }
    }

    /**
     * Resizes an image using a Graphics2D object backed by a BufferedImage.
     * @param srcImg - source image to scale
     * @param w - desired width
     * @param h - desired height
     * @return - the new resized image
     */
    private Image getScaledImage(Image srcImg, int w, int h) {
        BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = resizedImg.createGraphics();
        g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2.drawImage(srcImg, 0, 0, w, h, null);
        g2.dispose();
        return resizedImg;
    }    

    /**
     * Action class that shows the image specified in it's constructor.
     */
    private class ThumbnailAction extends AbstractAction implements MouseListener {

        private Icon displayPhoto; // The icon if the full image we want to display.

        /**
         * @param Icon - The full size photo to show in the button.
         * @param Icon - The thumbnail to show in the button.
         * @param String - The descriptioon of the icon.
         */
        public ThumbnailAction(Icon photo, Icon thumb, String title, String desc, String link) {
            displayPhoto = photo;

            // The short description becomes the title of the window.
            putValue(SHORT_DESCRIPTION, title);

            // The long description becomes the tooltip of a button.
            putValue(LONG_DESCRIPTION, desc);

            // The DEFAULT action on click is to launch this URL
            putValue(DEFAULT, link);             

            // The LARGE_ICON_KEY is the key for setting the icon when an Action is applied to a button.
            putValue(LARGE_ICON_KEY, thumb);            
        }

        /**
         * Shows the full image in the main area and sets the application title.
         */
        public void actionPerformed(ActionEvent e) {
            photographLabel.setIcon(displayPhoto);
            setTitle("Icon Demo - " + getValue(SHORT_DESCRIPTION).toString());
            photographLabel.setToolTipText("<html>" + getValue(LONG_DESCRIPTION)  + "</html>");

            photographLabel.validate();
            photographLabel.updateUI();
            photographLabel.repaint();

            photographLabel.addMouseListener(this); 
        }

        /**
         * Listen for clicks to send user to a URL or launch an application
         * @param e 
         */            
        @Override
        public void mouseClicked(MouseEvent e) {                
            int clicked = e.getClickCount();
            if (clicked > 0) {
                /*************************************/
                /* Code for launching Browser  BEGIN */
                Desktop desktop = null;
                if (Desktop.isDesktopSupported()) {
                    desktop = Desktop.getDesktop();
                }         
                URI uri = null;
                try {
                    uri = new URI((String)this.getValue(DEFAULT)); //pass the URL from ImageIcon to Browser
                    if (desktop.isSupported(Desktop.Action.BROWSE)) {
                        desktop.browse(uri); //url passed
                    }
                    else {
                        System.out.println("Unable to open default browser to: "+uri);
                    }
                }
                catch(IOException ioe) {
                    ioe.printStackTrace();
                }
                catch(URISyntaxException use) {
                    use.printStackTrace();
                }
                /* Code for launching Browser  END */
                /*************************************/
            }
            else {
                System.out.println("Clicked: " + clicked);
            }
        }        

        @Override
        public void mousePressed(MouseEvent e) {
            System.out.println("Mouse pressed (# of clicks: " + e.getClickCount() + ") " + e);
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            System.out.println("Mouse released (# of clicks: " + e.getClickCount() + ") " + e);
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            System.out.println("Mouse entered: " + e);
        }

        @Override
        public void mouseExited(MouseEvent e) {
            System.out.println("Mouse exited: " + e);
        }        
    }



    private JLabel photographLabel = new JLabel();
    private JToolBar buttonBar = new JToolBar();

    private String imagedir = "images/";

    private MissingIcon placeholderIcon = new MissingIcon();    

    // List of all the image files to load.
    private String[] thumbs = { "sunw01.jpg", "sunw02.jpg", "sunw03.jpg", "sunw04.jpg", "sunw05.jpg"};

    // List of all the descriptions of the image files. These correspond one to one with the image file names
    private String[] titles = { "SunLogo", "Clocktower", "ClocktowerWest", "Mansion", "SunAuditorium" };

    // List of all descriptions (descs) to use as tooltips.
    private String[] descs = { "The Original SUNW Logo", "The Clocktower frontal view", "The Clocktower from the West", "The Mansion on the hill!", "The infamouse Sun campus auditorium"};

    // List of all links to send a user to on click action.
    private String[] links = { "http://example.com/#1", "http://example.com/#2", "http://example.com/#3", "http://example.com/#4", "http://example.com/#5"};

}
Was it helpful?

Solution

Setting the Image is not the problem. But when a Action is performed you add a MouseListener to the photographLabel that you never remove in line 267. A simple hack for this example is to remove all registered Listeners before adding the new one:

for(MouseListener listener : photographLabel.getMouseListeners()) {
   photographLabel.removeMouseListener(listener);
}
photographLabel.addMouseListener(this);  

Now everything works as aspected.

OTHER TIPS

1) I can't generating your issue, because this descriptions is can talking about un_healt GPU drivers

2) but you can test that by reducing secondary issue from quickiest painting use flush() from Buffer, and following steps may help you

create Icon

myIcon.getImage().flush();

put Icon to the JLabel

myLabel.setIcon(myIcon); 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top