Package net.suberic.pooka.gui.dnd

Source Code of net.suberic.pooka.gui.dnd.MultipleTransferHandler

//The contents of this file are subject to the Mozilla Public License Version 1.1
//(the "License"); you may not use this file except in compliance with the
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
//Software distributed under the License is distributed on an "AS IS" basis,
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
//for the specific language governing rights and
//limitations under the License.
//
//The Original Code is "The Columba Project"
//
//The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
//
//All Rights Reserved.
package net.suberic.pooka.gui.dnd;

import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.event.InputEvent;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.TransferHandler;


/**
* A transfer handler that is composed from many transfer handlers.
* When setting a TransferHandler on a component, the new transfer handler
* is the only one used in a Drag and Drop action. Most of the times, this is
* fine but sometimes you want to preserve the behaviour of the component's
* default transfer handler. You only want to implement one specific data flavor
* to the component, not redoing those feature already implemented.
* This class lets a component to have several
* transfer handlers instead of one.
* <p>
* For example, the JTextArea has a transfer handler that supports draging and dropping
* strings into it by default. Say that you want to add support of DnD of a file into the
* text. Instead of mimicing behaviour of DnD of strings, you will only implement a
* transfer handler that supports the file list data flavor. Add these two transfer handler
* to this class and set this as the component transferhandler and everything is solved.
* <p>
* This chain of resposibility object is only good for adding new data flavors when importing, it will
* not give extra features when dragging from the component to another component. When starting
* a drag, the first transfer handler is used as the source handler. This is important to have
* the transfer handler with the most export support implemented, otherwise the component will
* lose its DnD export features. The default handler is always the first in the list, but can also
* be set by the corresponding method.
* <p>
* The behind the scenes implementation tries to cohere with the design pattern "Chain of responsibility".
* The actual implementation is internally a list that is ordered, ie TransferHandlers that
* are added first, is check first. If there are two transfer handlers that supports the same
* type of data flavors, the first one in the list is the one used.
* <p>
* Note that this transfer handler will put an overhead for each transferhandler.
* @author redsolo
*/
public class MultipleTransferHandler extends TransferHandler {

  private List transferHandlers = new ArrayList();
 
  private TransferHandler dragSourceHandler;

  /**
   * Adds the transfer handler to this handler.
   * @param handler the new handler.
   */
  public void addTransferHandler(TransferHandler handler) {
    if (!transferHandlers.contains(handler)) {
      transferHandlers.add(handler);
    }
    if (dragSourceHandler == null) {
      dragSourceHandler = handler;
    }
  }

  /**
   * Removes the transfer handler from this handler.
   * @param handler the handler.
   */
  public void removeTransferHandler(TransferHandler handler) {
    transferHandlers.remove(handler);
    if (handler == dragSourceHandler) {
      if (transferHandlers.size() > 0) {
  dragSourceHandler = (TransferHandler) transferHandlers.get(0);
      } else {
  dragSourceHandler = null;
      }
    }
  }

  /**
   * Returns a list with all transfer handlers.
   * @return a list with all transfer handlers.
   */
  public List getTransferHandlers() {
    return (List) ((ArrayList) transferHandlers).clone();
  }
 
  /**
   * Sets the transfer handler to use as the drag source.
   * The default implementation is to use the first transfer handler in the list.
   * @param newDragHandler the new drag source transfer handler.
   */
  public void setDragSourceTransferHandler(TransferHandler newDragHandler) {
    dragSourceHandler = newDragHandler;
  }
 
  /**
   * Returns the transfer handler when dragging from the component.
   * @return the transfer handler when dragging from the component.
   */
  public TransferHandler getDragSourceTransferHandler() {
    if (dragSourceHandler == null) {
      if (transferHandlers.size() == 0) {
  throw new IllegalStateException("The multiple transfer handler must have at least one TransferHandler.");
      }
      dragSourceHandler = (TransferHandler) transferHandlers.get(0);
    }
    return dragSourceHandler;
  }

  // ---------- Methods that handles importing of objects. ----------
 
  /** {@inheritDoc} */
  public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
    boolean canImport = false;
    for (Iterator iterator = transferHandlers.iterator(); (iterator.hasNext()) && (!canImport);) {
      TransferHandler handler = (TransferHandler) iterator.next();
      canImport = handler.canImport(comp, transferFlavors);
    }
    return canImport;
  }
 
  /** {@inheritDoc} */
  public boolean importData(JComponent comp, Transferable t) {
    boolean wasImported = false;
   
    for (Iterator iterator = transferHandlers.iterator(); (iterator.hasNext()) && (!wasImported);) {
      TransferHandler handler = (TransferHandler) iterator.next();
      if (handler.canImport(comp, t.getTransferDataFlavors())) {
  wasImported = handler.importData(comp, t);
      }
    }
    return wasImported;
  }
 
  // ---------- Methods that handles exporting of objects. ----------
 
  /** {@inheritDoc} */
  public int getSourceActions(JComponent c) {
    return getDragSourceTransferHandler().getSourceActions(c);
  }
 
  /** {@inheritDoc} */
  public void exportAsDrag(JComponent comp, InputEvent e, int action) {
    getDragSourceTransferHandler().exportAsDrag(comp, e, action);
  }
 
  /** {@inheritDoc} */
  public void exportToClipboard(JComponent comp, Clipboard clip, int action) {
    getDragSourceTransferHandler().exportToClipboard(comp, clip, action);
  }
 
  /** {@inheritDoc} */
  public Icon getVisualRepresentation(Transferable t) {
    return getDragSourceTransferHandler().getVisualRepresentation(t);
  }

  /** {@inheritDoc} */
  protected Transferable createTransferable(JComponent c) {
    return (Transferable) ignoreProtectedAccess("createTransferable",
            new Class[] {JComponent.class},
            new Object[]{c});
  }
 
  /** {@inheritDoc} */
  protected void exportDone(JComponent source, Transferable data, int action) {
    ignoreProtectedAccess("exportDone",
        new Class[] {JComponent.class, Transferable.class, int.class},
        new Object[]{source, data, new Integer(action)});
  }
 
  /**
   * Calls the method and ignores the protected access modifier on the TransferHandler class.
   * This is necessary due to the fact the needed methods have protected access in the TransferHandler
   * class. The Swing framework bypass this "feature" by having the cooperating classes in the same package.
   * And since package access gives access before the protected modifier, then they dont have any
   * problem doing this. But for other developers, there is no other way than to cheat using
   * the reflection API.
   * <p>
   * The method calls the specified methodname on the default drag source TransferHandler, bypassing
   * the protected access modifier.
   * @param methodName the name of the method to call.
   * @param methodArguments the type of arguments that the method has.
   * @param arguments the arguments to send when calling the method.
   * @return an Object if the method returns one. If the method doesnt return an object, null is returned.
   */
  private Object ignoreProtectedAccess(String methodName, Class[] methodArguments, Object[] arguments) {
    Object object = null;
    try {
      Method method = TransferHandler.class.getDeclaredMethod(methodName, methodArguments);
      method.setAccessible(true);
      object = (Transferable) method.invoke(getDragSourceTransferHandler(), arguments);
    } catch (SecurityException se) {
    } catch (NoSuchMethodException nsme) {
    } catch (IllegalArgumentException iae) {
    } catch (IllegalAccessException iace) {
    } catch (InvocationTargetException ite) {
    }
    return object;
  }
}
TOP

Related Classes of net.suberic.pooka.gui.dnd.MultipleTransferHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.