Return-Path: Received: from po10.mit.edu (po10.mit.edu [18.7.21.66]) by po10.mit.edu (Cyrus v2.1.5) with LMTP; Tue, 18 Mar 2003 16:17:11 -0500 X-Sieve: CMU Sieve 2.2 Received: from pacific-carrier-annex.mit.edu by po10.mit.edu (8.12.4/4.7) id h2ILH9OZ025742; Tue, 18 Mar 2003 16:17:09 -0500 (EST) Received: from hermes.sun.com (hermes.sun.com [64.124.140.169]) by pacific-carrier-annex.mit.edu (8.9.2/8.9.2) with SMTP id QAA02305 for ; Tue, 18 Mar 2003 16:17:09 -0500 (EST) Date: 18 Mar 2003 11:31:18 -0800 From: "JDC Tech Tips" To: alexp@mit.edu Message-Id: <32061839-426309885@hermes.sun.com> Subject: Core Java Technologies Tech Tips, March 18, 2003 (Dragging Text and Images with Swing, Discovering the Calling Method Name) Mime-Version: 1.0 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit X-Mailer: SunMail 1.0 X-Spam-Score: 0.5 X-Spam-Flag: NO X-Scanned-By: MIMEDefang 2.28 (www . roaringpenguin . com / mimedefang) Core Java Technologies Technical Tips
.
.
Core Java Technologies Technical Tips
.
   View this issue as simple text March 18, 2003    

In this Issue

Welcome to the Core Java Technologies Tech Tips, March 18, 2003. Here you'll get tips on using core Java technologies and APIs, such as those in Java 2 Platform, Standard Edition (J2SE).

This issue covers:

.Dragging Text and Images with Swing
.Discovering the Calling Method Name

These tips were developed using Java 2 SDK, Standard Edition, v 1.4.

This issue of the Core Java Technologies Tech Tips is written by John Zukowski, president of JZ Ventures, Inc.

.
.

DRAGGING TEXT AND IMAGES WITH SWING

One of the more welcome features of J2SE version 1.4 are the enhancements to the Drag and Drop and data transfer facilities. These facilities are defined in the java.awt.dnd package with some help from the java.awt.datatransfer package. These APIs give you the ability to transfer any object (not just strings) between a Java Runtime Environment (JRE) and itself, another JRE, or an application native to the runtime platform. Earlier versions of J2SE required you to control all aspects of the drag gestures, such as what to do when the user click-drags a mouse over a component and drops it over another component. With the newer release of J2SE, a lot of the work is done for you.

The simplest way to add drag and drop support to a program is to use the setDragEnabled method. To drag-enable a Swing component, you simply call the setDragEnabled method, with an argument of true, on the component. The component must be one that supports drag operations. The components that provide this support are JColorChooser, JFileChooser, JList, JTree, JTable, and all the JTextComponent classes except JPasswordField. The following program demonstrates this approach. The program drag-enables a JList and two JTextArea components. You can select an item from the JList and then drop it in the JTextArea, or you can select content from one JTextArea and drop it in the other JTextArea.

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import java.text.*;
    import java.util.*;

    public class Drag1 {
      public static void main(String args[]) {
        JFrame frame = new JFrame("Drag");
        frame.setDefaultCloseOperation(
            JFrame.EXIT_ON_CLOSE);
        Container content = frame.getContentPane();
        JPanel inner = new JPanel(new BorderLayout());
        JTextArea top = new JTextArea(5, 40);
        top.setDragEnabled(true);
        JScrollPane topScroll = new JScrollPane(top);
        inner.add(topScroll, BorderLayout.NORTH);
        JTextArea bottom = new JTextArea(5, 40);
        bottom.setDragEnabled(true);
        JScrollPane bottomScroll = 
            new JScrollPane(bottom);
        inner.add(bottomScroll, BorderLayout.SOUTH);
        content.add(inner, BorderLayout.CENTER);
        DateFormatSymbols symbols = 
            new DateFormatSymbols(Locale.US);
        JList list = new JList(symbols.getWeekdays());
        list.setDragEnabled(true);
        JScrollPane leftScroll = new JScrollPane(list);
        content.add(leftScroll, BorderLayout.WEST);
        frame.pack();
        frame.show();
      }
    }

Drop works here because Swing has arranged for any component with a TransferHandler to automatically receive a drop target. Swing provides default TransferHandler objects to some components so that they support drop "out of the box". These components are JColorChooser and all the JTextComponent classes (including JPasswordField).

While setDragEnabled allows you to turn on and off drag support, the actual mechanism of importing and exporting data is supplied by Swing's TransferHandler class. TransferHandler is responsible for all data transfer into and out of a component. Specifically, TransferHandler handles the export of data for drag, cut, and copy, as well as the import for drop and paste. As mentioned above, Swing provides default TransferHandler implementations for some components. You can replace this default implementation with a TransferHandler of your own design by calling setTransferHandler on the component. This works together with setDragEnabled to allow for easy Drag and Drop customization. (Note that when you replace a TransferHandler, you not only change the behavior of Drag and Drop, but also cut, copy, and paste).

Components that don't support setDragEnabled still support a TransferHandler. But you'll be required to do a little more work to initiate a drag, such as supplying a MouseListener that watches for a drag gesture and calling the TransferHandler's exportAsDrag method.

One type of custom TransferHandler exposes the current setting of a property as a draggable object. To use it, you create a TransferHandler that identifies the property name, and associate it with the start drag gesture you define for that component (such as mousePress). The TransferHandler handles the rest. Here's a program that uses a TransferHandler to drag-enable a text label for a JLabel component. Run the program and drag the label into the JTextField.

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;

    public class Drag2 {
      public static void main(String args[]) {
        JFrame frame = new JFrame("Drag");
        frame.setDefaultCloseOperation(
            JFrame.EXIT_ON_CLOSE);
        Container content = frame.getContentPane();
        JTextField top = new JTextField();
        content.add(top, BorderLayout.NORTH);
        JLabel label = new JLabel("Drag Me");
        label.setTransferHandler(
            new TransferHandler("text"));
        MouseListener listener = new MouseAdapter() {
          public void mousePressed(MouseEvent e) {
            JComponent c = (JComponent)e.getSource();
            TransferHandler th = c.getTransferHandler();
            th.exportAsDrag(c, e, TransferHandler.COPY);
          }
        };
        label.addMouseListener(listener);
        content.add(label, BorderLayout.SOUTH);
        frame.setSize(100, 100);
        frame.show();
      }
    }

How about dragging and dropping images, in other words "transferring" images? That's more complicated than moving text around. To transfer an image, you need to create a TransferHandler object that supports the DataFlavor of imageFlavor. A DataFlavor is an object that represents the data format of an object being transferred, that is, as it would appear on a clipboard during a drag and drop operation. In an image transfer, the transfer is done under a particular data flavor. This allows the receiver to determine what formats the transferable object can move. The receiver can compare that with the formats it supports receiving, and then find the richest format available for the transfer. To do this, you need to create a custom TransferHandler implementation.

To demonstrate transferring images, let's define a TransferHandler object that supports transferring images from a JLabel to or from anywhere.

In the TransferHandler object, you need to define what type of operations are supported. Use the getSourceActions method to do that. The available operations defined in the TransferHandler class are: NONE, COPY, MOVE, and COPY_OR_MOVE. In this example, let's assume that the image shouldn't be removed from the JLabel, so specify COPY as the supported operation:

    public int getSourceActions(JComponent c) {
      return TransferHandler.COPY;
    }

Next, implement the canImport method to check if a given set of flavors can be imported into the potential drop target. To do this, you need to first create an array of supported flavors:

    public static final DataFlavor flavors[] = 
      {DataFlavor.imageFlavor};

Then, in the canImport method, you check if the component drop target supports the flavor. If it can, return true, if not false.

    public boolean canImport(
        JComponent comp, DataFlavor flavor[]) {
      if (!(comp instanceof JLabel)) {
        return false;
      }
      for (int i=0, n=flavor.length; i<n; i++) {
        for (int j=0, m=flavors.length; j<m; j++) {
          if (flavor[i].equals(flavors[j])) {
            return true;
          }
        }
      }
      return false;
    }

Next, define the createTransferable method. This is called when something wants to be placed on the clipboard or used in a drag operation. However, no data is really placed on the clipboard just yet. Instead, a reference to a Transferable object is placed on the clipboard. The Transferable implementation does the actual copy. Notice that the Transferable implementation is an anonymous inner class here. It will simply return the Image just saved as the transferable data.

    public Transferable createTransferable(
        JComponent comp) {

        if (comp instanceof JLabel) {
          JLabel label = (JLabel)comp;
          Icon icon = label.getIcon();
          if (icon instanceof ImageIcon) {
            final Image image = ((
                ImageIcon)icon).getImage();
            final JLabel source = label;
            Transferable transferable = 
                new Transferable() {

              public Object getTransferData(
                  DataFlavor flavor) {
                if (isDataFlavorSupported(flavor)) {
                    return image;
                }
                return null;
              }

              public DataFlavor[] getTransferDataFlavors() {
                return flavors;
              }

              public boolean isDataFlavorSupported(
                  DataFlavor flavor) {
                return flavor.equals(
                    DataFlavor.imageFlavor);
              }
            };
            return transferable;
          }
        }
      return null;
    }

The last part is support for a paste operation. To get an image, you can paste it to replace the existing image on the JLabel. Remember that the Java platform supports GIF, JPEG, and PNG. As long as an image in one of those formats is on the system clipboard, it can be pasted onto the associated JLabel. This is done with the importData method:

    public boolean importData(
        JComponent comp, Transferable t) {
      if (comp instanceof JLabel) {
        JLabel label = (JLabel)comp;
        if (t.isDataFlavorSupported(flavors[0])) {
          try {
            Image image = (
                Image)t.getTransferData(flavors[0]);
            ImageIcon icon = new ImageIcon(image);
            label.setIcon(icon);
            return true;
          } catch (UnsupportedFlavorException ignored) {
          } catch (IOException ignored) {
          }
        }
      }
      return false;
    }

Putting all this together, gives the following ImageSelection class definition:

    import java.awt.*;
    import java.awt.image.*;
    import java.awt.datatransfer.*;
    import java.io.*;
    import javax.swing.*;


    public class ImageSelection extends TransferHandler {


      private static final DataFlavor flavors[] = 
          {DataFlavor.imageFlavor};


      public int getSourceActions(JComponent c) {
        return TransferHandler.COPY;
      }


      public boolean canImport(
          JComponent comp, DataFlavor flavor[]) {
            if (!(comp instanceof JLabel)) {
              return false;
            }
        for (int i=0, n=flavor.length; i<n; i++) {
          for (int j=0, m=flavors.length; j<m; j++) {
            if (flavor[i].equals(flavors[j])) {
              return true;
            }
          }
        }
        return false;
      }


      public Transferable createTransferable(
          JComponent comp) {

        if (comp instanceof JLabel) {
          JLabel label = (JLabel)comp;
          Icon icon = label.getIcon();
          if (icon instanceof ImageIcon) {
            final Image image = ((
                ImageIcon)icon).getImage();
            final JLabel source = label;
            Transferable transferable = 
                new Transferable() {

              public Object getTransferData(
                  DataFlavor flavor) {
                if (isDataFlavorSupported(flavor)) {
                    return image;
                }
                return null;
              }

              public DataFlavor[] getTransferDataFlavors() {
                return flavors;
              }

              public boolean isDataFlavorSupported(
                  DataFlavor flavor) {
                return flavor.equals(
                    DataFlavor.imageFlavor);
              }
            };
            return transferable;
          }
        }
        return null;
      }

      public boolean importData(
          JComponent comp, Transferable t) {
        if (comp instanceof JLabel) {
          JLabel label = (JLabel)comp;
          if (t.isDataFlavorSupported(flavors[0])) {
            try {
              Image image = (
                  Image)t.getTransferData(flavors[0]);
              ImageIcon icon = new ImageIcon(image);
              label.setIcon(icon);
              return true;
            } catch (UnsupportedFlavorException ignored) {
            } catch (IOException ignored) {
            }
          }
        }
        return false;
      }
    }

After the ImageSelection class is defined, you can create a program that supports transferring images. The following program does just that.

    import java.awt.*;
    import java.awt.event.*;
    import java.awt.datatransfer.*;
    import javax.swing.*;

    public class DragImage {

      public static void main(String args[]) {

        JFrame frame = new JFrame("Drag Image");
        frame.setDefaultCloseOperation(
            JFrame.EXIT_ON_CLOSE);
        Container contentPane = frame.getContentPane();

        final Clipboard clipboard =
          frame.getToolkit().getSystemClipboard();

        final JLabel label = new JLabel();
        if (args.length > 0) {
          Icon icon = new ImageIcon(args[0]);
         label.setIcon(icon);
        }
        label.setTransferHandler(new ImageSelection());

        MouseListener mouseListener = 
            new MouseAdapter() {
          public void mousePressed(MouseEvent e) {
            JComponent comp = (JComponent)e.getSource();
            TransferHandler handler = 
                comp.getTransferHandler();
            handler.exportAsDrag(
                comp, e, TransferHandler.COPY);
          }
        };
        label.addMouseListener(mouseListener);

        JScrollPane pane = new JScrollPane(label);
        contentPane.add(pane, BorderLayout.CENTER); 

        JButton copy = new JButton("Copy");
        copy.addActionListener(new ActionListener() {
          public void actionPerformed(ActionEvent e) {
          // fire TransferHandler's built-in copy 
          // action with a new actionEvent having 
          // "label" as the source
          Action copyAction = 
              TransferHandler.getCopyAction();
          copyAction.actionPerformed(
            new ActionEvent(
               label, ActionEvent.ACTION_PERFORMED,
              (String)copyAction.getValue(Action.NAME),
              EventQueue.getMostRecentEventTime(),
              0));
         }
        });

        JButton clear = new JButton("Clear");
        clear.addActionListener(new ActionListener() {
          public void actionPerformed(
              ActionEvent actionEvent) {
            label.setIcon(null);
          }
        });
    
        JButton paste = new JButton("Paste");
        paste.addActionListener(new ActionListener() {
          public void actionPerformed(
              ActionEvent actionEvent) {
            // use TransferHandler's built-in 
            // paste action
            Action pasteAction = 
               TransferHandler.getPasteAction();
            pasteAction.actionPerformed(
               new ActionEvent(label,
              ActionEvent.ACTION_PERFORMED,
              (String)pasteAction.getValue(Action.NAME),
              EventQueue.getMostRecentEventTime(),
              0));
            }
        });

        JPanel p = new JPanel();
        p.add(copy);
        p.add(clear);
        p.add(paste);
        contentPane.add(p, BorderLayout.SOUTH);

        frame.setSize(300, 300);
        frame.show();
      }
    }

Notice how the copy and paste actions are just reusing the built-in behavior of the TransferHandler. By simply passing in the appropriate object to act as the source of the ActionEvent, the TransferHandler will invoke the exportToClipboard and importDate methods on that component, respectively. See the javadoc for the TranferHandler class for more information on these methods. It is always possible to manually do this copy and paste behavior. However, since the system provides the behavior for you, why not reuse it? You just have to create the Action.

Specify an image in the command line when you run the program. That will put an initial image into the JLabel. For example:

 
    java DragImage myimage.gif  

The program supports five different copy/paste operations. The Copy button copies the image on the screen's JLabel to the system clipboard. The Paste button copies the image from the system clipboard to the JLabel. The Clear button clears the current image from the JLabel. The last two operations aren't obvious. One of these is that you can initiate a drag operation from the JLabel and drop it in anywhere that permits dropping images of the flavor supported by the Java runtime. For instance, you can drop an image into Microsoft Excel, but not Word. The other operation that isn't obvious is dropping an image onto the JLabel. When you do that, you'll see the image displayed on the JLabel. Try dragging an image from your browser onto the JLabel, and see what happens.

For more information about the the Drag and Drop enhancements in J2SE release 1.4, see "Drag and Drop".

.
.

DISCOVERING THE CALLING METHOD NAME

A common request for developers working with J2SE prior to version 1.4 was to programmatically determine the current location in a program, that is the current method. Typically, developers made this request for logging purposes. Developers still ask the same question today, that is, with J2SE v 1.4. However getting the answer is much more straightforward.

The old way of finding the current location in a program involved generating an exception, printing the stack trace to an in-memory buffer, and then reading back the trace to find out the specific information you were after. After removing the extraneous characters from the line, you're left with the specific method name, as shown here.

    import java.io.*;

    public class ManualDump {
      public static void main(String args[]) 
          throws IOException {
            Throwable t = new Throwable();
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            t.printStackTrace(pw);
            String input = sw.getBuffer().toString();
            StringReader sr = new StringReader(input);
            BufferedReader br = new BufferedReader(sr);
            br.readLine(); // Exception name
            String line = br.readLine();
            int paren = line.indexOf('(');
            line = line.substring(0, paren);
            int period = line.lastIndexOf('.');
            line = line.substring(period+1);
            System.out.println("Method: " + line);
          }
    }

Running ManualDump displays:

    Method: main     

While the old way still works with J2SE version 1.4, there is a much cleaner way to parse stack dumps. A new feature in J2SE version 1.4 is the Chained Exception Facility. This facility allows you to say that Exception X was the cause of Exception Y. The facility also gives you the ability to go beyond printing a stack trace -- instead, you can get the trace and "print" it yourself. You simply call the new getStackTrace method of Throwable. This returns an array of StackTraceElement objects. A part of the class definition is the getMethodName method. The StackTraceElement at index zero is the current method:

    public class AutoDump {
      public static void main(String args[]) {
        Throwable t = new Throwable();
        StackTraceElement elements[] = t.getStackTrace();
        String method = elements[0].getMethodName();
        System.out.println("Method: " + method);
      }
    }

Running AutoDump displays:

    Method: main  

With getStackTrace you can completely recreate the results of printStackTrace, or generate your own style of output. You are not limited to getting the method name.

You might think that with StackTraceElement you have everything you need, and technically you do. However, there is one more thing worth mentioning. If you are getting the method name for logging purposes, don't forget about the Java Logging APIs. The classes found in the java.util.logging package let you log away, and record what the current method is. More importantly the Java Logging APIs allow you to do that without you having to manually discover the method. These APIs were covered in the October 2002 issue of the Core Java Technologies Tech Tips in the tip titled "Filtering Logged Messages". You can look at the source for the getSourceMethodName method of LogRecord to see code similar to that used in the AutoDump example to fetch the given method name.

.
.
.

Reader Feedback

  Very worth reading    Worth reading    Not worth reading 

If you have other comments or ideas for future technical tips, please type them here:

 

Have a question about Java programming? Use Java Online Support.

.
.

IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/
http://developer.java.sun.com/berkeley_license.html


Comments? Send your feedback on the Core Java Technologies Tech Tips to: jdc-webmaster@sun.com

Subscribe to other Java developer Tech Tips:

- Enterprise Java Technologies Tech Tips. Get tips on using enterprise Java technologies and APIs, such as those in the Java 2 Platform, Enterprise Edition (J2EE).
- Wireless Developer Tech Tips. Get tips on using wireless Java technologies and APIs, such as those in the Java 2 Platform, Micro Edition (J2ME).

To subscribe to these and other JDC publications:
- Go to the JDC Newsletters and Publications page, choose the newsletters you want to subscribe to and click "Update".
- To unsubscribe, go to the subscriptions page, uncheck the appropriate checkbox, and click "Update".


ARCHIVES: You'll find the Core Java Technologies Tech Tips archives at:
http://java.sun.com/jdc/TechTips/index.html


Copyright 2003 Sun Microsystems, Inc. All rights reserved.
4150 Network Circle, Santa Clara, CA 95054 USA.


This document is protected by copyright. For more information, see:
http://java.sun.com/jdc/copyright.html


Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks or registered trademarks (http://www.sun.com/suntrademarks/) of Sun Microsystems, Inc. in the United States and other countries.

Sun Microsystems, Inc.
.
.
Please unsubscribe me from this newsletter.