mirror of https://github.com/sipwise/jitsi.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
475 lines
16 KiB
475 lines
16 KiB
/*
|
|
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
|
|
*
|
|
* Distributable under LGPL license.
|
|
* See terms of license at gnu.org.
|
|
*/
|
|
package net.java.sip.communicator.util.swing;
|
|
|
|
import java.awt.*;
|
|
import java.awt.datatransfer.*;
|
|
import java.awt.dnd.*;
|
|
import java.awt.event.*;
|
|
import java.awt.geom.*;
|
|
import java.awt.image.*;
|
|
import java.io.*;
|
|
|
|
import javax.swing.*;
|
|
import javax.swing.text.*;
|
|
|
|
import net.java.sip.communicator.service.contactlist.*;
|
|
import net.java.sip.communicator.util.*;
|
|
|
|
/**
|
|
* A TransferHandler that we use to handle copying, pasting and DnD operations.
|
|
* The string handler is heavily inspired by Sun's
|
|
* <tt>DefaultTransferHandler</tt> with the main difference being that
|
|
* we only accept pasting of plain text. We do this in order to avoid html
|
|
* support problems that appear when pasting formatted text into our editable
|
|
* area.
|
|
*
|
|
* @author Emil Ivov
|
|
* @author Yana Stamcheva
|
|
*/
|
|
public class ExtendedTransferHandler
|
|
extends TransferHandler
|
|
{
|
|
/**
|
|
* The data flavor used when transferring <tt>MetaContact</tt>s.
|
|
*/
|
|
protected static final DataFlavor metaContactDataFlavor
|
|
= new DataFlavor(MetaContact.class, "MetaContact");
|
|
|
|
/**
|
|
* The drag icon that is the visual representation of the contained
|
|
* <tt>Transferable</tt>.
|
|
*/
|
|
protected static final ImageIcon dragIcon = UtilActivator.getResources()
|
|
.getImage("service.gui.icons.DRAG_ICON");
|
|
|
|
/**
|
|
* Returns the type of transfer actions supported by the source;
|
|
* any bitwise-OR combination of <tt>COPY</tt>, <tt>MOVE</tt>
|
|
* and <tt>LINK</tt>.
|
|
* <p>
|
|
* Some models are not mutable, so a transfer operation of <tt>MOVE</tt>
|
|
* should not be advertised in that case. Returning <tt>NONE</tt>
|
|
* disables transfers from the component.
|
|
*
|
|
* @param c the component holding the data to be transferred;
|
|
* provided to enable sharing of <code>TransferHandler</code>s
|
|
* @return {@code COPY} if the transfer property can be found,
|
|
* otherwise returns <code>NONE</code>
|
|
*/
|
|
public int getSourceActions(JComponent c)
|
|
{
|
|
return TransferHandler.COPY;
|
|
}
|
|
|
|
/**
|
|
* Indicates whether a component will accept an import of the given
|
|
* set of data flavors prior to actually attempting to import it. We return
|
|
* <tt>true</tt> to indicate that the transfer with at least one of the
|
|
* given flavors would work and <tt>false</tt> to reject the transfer.
|
|
* <p>
|
|
* @param comp component
|
|
* @param flavor the data formats available
|
|
* @return true if the data can be inserted into the component, false
|
|
* otherwise
|
|
* @throws NullPointerException if <code>support</code> is {@code null}
|
|
*/
|
|
public boolean canImport(JComponent comp, DataFlavor flavor[])
|
|
{
|
|
for (int i = 0, n = flavor.length; i < n; i++)
|
|
{
|
|
if (flavor[i].equals(DataFlavor.javaFileListFlavor))
|
|
{
|
|
return true;
|
|
}
|
|
else if (flavor[i].equals(DataFlavor.stringFlavor))
|
|
{
|
|
if (comp instanceof JTextComponent)
|
|
{
|
|
JTextComponent c = (JTextComponent)comp;
|
|
|
|
if (c.isEditable() && c.isEnabled())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Creates a transferable for text pane components in order to enable drag
|
|
* and drop of text.
|
|
* @param component the component for which to create a
|
|
* <tt>Transferable</tt>
|
|
* @return the created <tt>Transferable</tt>
|
|
*/
|
|
public Transferable createTransferable(JComponent component)
|
|
{
|
|
if (component instanceof JTextPane
|
|
|| component instanceof JTextField)
|
|
{
|
|
return new SelectedTextTransferable((JTextComponent) component);
|
|
}
|
|
|
|
return super.createTransferable(component);
|
|
}
|
|
|
|
/**
|
|
* Handles transport (cut and copy) from the chat panel to
|
|
* <tt>clipboard</tt>. This method will only transfer plain text and would
|
|
* explicitly ignore any formatting.
|
|
* <p>
|
|
* @param comp the component holding the data to be transferred;
|
|
* provided to enable sharing of <code>TransferHandler</code>s
|
|
* @param clipboard the clipboard to transfer the data into
|
|
* @param action the transfer action requested; this should
|
|
* be a value of either <code>COPY</code> or <code>MOVE</code>;
|
|
* the operation performed is the intersection of the transfer
|
|
* capabilities given by getSourceActions and the requested action;
|
|
* the intersection may result in an action of <code>NONE</code>
|
|
* if the requested action isn't supported
|
|
* @throws IllegalStateException if the clipboard is currently unavailable
|
|
* @see Clipboard#setContents(Transferable, ClipboardOwner)
|
|
*/
|
|
public void exportToClipboard(JComponent comp,
|
|
Clipboard clipboard,
|
|
int action)
|
|
throws IllegalStateException
|
|
{
|
|
if (comp instanceof JTextComponent)
|
|
{
|
|
JTextComponent textComponent = (JTextComponent)comp;
|
|
int startIndex = textComponent.getSelectionStart();
|
|
int endIndex = textComponent.getSelectionEnd();
|
|
if (startIndex != endIndex)
|
|
{
|
|
try
|
|
{
|
|
Document doc = textComponent.getDocument();
|
|
String srcData = doc.getText(startIndex,
|
|
endIndex - startIndex);
|
|
StringSelection contents = new StringSelection(srcData);
|
|
|
|
// this may throw an IllegalStateException,
|
|
// but it will be caught and handled in the
|
|
// action that invoked this method
|
|
clipboard.setContents(contents, null);
|
|
|
|
if (action == TransferHandler.MOVE)
|
|
{
|
|
doc.remove(startIndex, endIndex - startIndex);
|
|
}
|
|
}
|
|
catch (BadLocationException ble)
|
|
{
|
|
//we simply ignore
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Transferable for text pane components that enables drag and drop of text.
|
|
*/
|
|
public class SelectedTextTransferable implements Transferable
|
|
{
|
|
private JTextComponent textComponent;
|
|
|
|
/**
|
|
* Creates an instance of <tt>SelectedTextTransferable</tt>.
|
|
* @param component the text component
|
|
*/
|
|
public SelectedTextTransferable(JTextComponent component)
|
|
{
|
|
this.textComponent = component;
|
|
}
|
|
|
|
/**
|
|
* Returns supported flavors.
|
|
* @return an array of supported flavors
|
|
*/
|
|
public DataFlavor[] getTransferDataFlavors()
|
|
{
|
|
return new DataFlavor[]{DataFlavor.stringFlavor};
|
|
}
|
|
|
|
/**
|
|
* Returns <tt>true</tt> if the given <tt>flavor</tt> is supported,
|
|
* otherwise returns <tt>false</tt>.
|
|
* @param flavor the data flavor to verify
|
|
* @return <tt>true</tt> if the given <tt>flavor</tt> is supported,
|
|
* otherwise returns <tt>false</tt>
|
|
*/
|
|
public boolean isDataFlavorSupported(DataFlavor flavor)
|
|
{
|
|
return DataFlavor.stringFlavor.equals(flavor);
|
|
}
|
|
|
|
/**
|
|
* Returns the selected text.
|
|
* @param flavor the flavor
|
|
* @return the selected text
|
|
* @exception IOException if the data is no longer available in the
|
|
* requested flavor.
|
|
* @exception UnsupportedFlavorException if the requested data flavor
|
|
* is not supported.
|
|
*/
|
|
public Object getTransferData(DataFlavor flavor)
|
|
throws UnsupportedFlavorException,
|
|
IOException
|
|
{
|
|
if (!DataFlavor.stringFlavor.equals(flavor))
|
|
{
|
|
throw new UnsupportedFlavorException(flavor);
|
|
}
|
|
|
|
return textComponent.getSelectedText();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Overrides <tt>TransferHandler.getVisualRepresentation(Transferable t)</tt>
|
|
* in order to return a custom drag icon.
|
|
* <p>
|
|
* The default parent implementation of this method returns null.
|
|
*
|
|
* @param t the data to be transferred; this value is expected to have been
|
|
* created by the <code>createTransferable</code> method
|
|
* @return the icon to show when dragging
|
|
*/
|
|
public Icon getVisualRepresentation(Transferable t)
|
|
{
|
|
Icon icon = null;
|
|
if (t.isDataFlavorSupported(DataFlavor.stringFlavor))
|
|
{
|
|
String text = null;
|
|
try
|
|
{
|
|
text = (String) t.getTransferData(DataFlavor.stringFlavor);
|
|
}
|
|
catch (UnsupportedFlavorException e) {}
|
|
catch (IOException e) {}
|
|
|
|
if (text != null)
|
|
{
|
|
Rectangle2D bounds = GuiUtils.getStringBounds(text);
|
|
BufferedImage image = new BufferedImage(
|
|
(int) Math.ceil(bounds.getWidth()),
|
|
(int) Math.ceil(bounds.getHeight()),
|
|
BufferedImage.TYPE_INT_ARGB);
|
|
|
|
Graphics g = image.getGraphics();
|
|
AntialiasingManager.activateAntialiasing(g);
|
|
g.setColor(Color.BLACK);
|
|
// Don't know why if we draw the string on y = 0 it doesn't
|
|
// appear in the visible area.
|
|
g.drawString(text, 0, 10);
|
|
|
|
icon = new ImageIcon(image);
|
|
}
|
|
}
|
|
|
|
return icon;
|
|
}
|
|
|
|
// Patch for bug 4816922 "No way to set drag icon:
|
|
// TransferHandler.getVisualRepresentation() is not used".
|
|
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4816922
|
|
// The following workaround comes from bug comments section!
|
|
private static SwingDragGestureRecognizer recognizer = null;
|
|
|
|
private static class SwingDragGestureRecognizer
|
|
extends DragGestureRecognizer
|
|
{
|
|
SwingDragGestureRecognizer(DragGestureListener dgl)
|
|
{
|
|
super(DragSource.getDefaultDragSource(), null, NONE, dgl);
|
|
}
|
|
|
|
void gestured(JComponent c, MouseEvent e, int srcActions, int action)
|
|
{
|
|
setComponent(c);
|
|
setSourceActions(srcActions);
|
|
appendEvent(e);
|
|
fireDragGestureRecognized(action, e.getPoint());
|
|
}
|
|
|
|
/**
|
|
* Registers this DragGestureRecognizer's Listeners with the Component.
|
|
*/
|
|
protected void registerListeners() {}
|
|
|
|
/**
|
|
* Unregister this DragGestureRecognizer's Listeners with the Component.
|
|
* <p/>
|
|
* Subclasses must override this method.
|
|
*/
|
|
protected void unregisterListeners() {}
|
|
}
|
|
|
|
/**
|
|
* Overrides <tt>TransferHandler.exportAsDrag</tt> method in order to call
|
|
* our own <tt>SwingDragGestureRecognizer</tt>, which takes care of the
|
|
* visual representation icon.
|
|
*
|
|
* @param comp the component holding the data to be transferred; this
|
|
* argument is provided to enable sharing of <code>TransferHandler</code>s
|
|
* by multiple components
|
|
* @param e the event that triggered the transfer
|
|
* @param action the transfer action initially requested; this should
|
|
* be a value of either <code>COPY</code> or <code>MOVE</code>;
|
|
* the value may be changed during the course of the drag operation
|
|
*/
|
|
public void exportAsDrag(JComponent comp, InputEvent e, int action)
|
|
{
|
|
int srcActions = getSourceActions(comp);
|
|
int dragAction = srcActions & action;
|
|
|
|
// only mouse events supported for drag operations
|
|
if (! (e instanceof MouseEvent))
|
|
action = NONE;
|
|
|
|
if (action != NONE && !GraphicsEnvironment.isHeadless())
|
|
{
|
|
if (recognizer == null)
|
|
{
|
|
recognizer = new SwingDragGestureRecognizer(new DragHandler());
|
|
}
|
|
recognizer.gestured(comp, (MouseEvent) e, srcActions, dragAction);
|
|
}
|
|
else
|
|
{
|
|
exportDone(comp, null, NONE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is the default drag handler for drag and drop operations that
|
|
* use the <code>TransferHandler</code>.
|
|
*/
|
|
private static class DragHandler
|
|
implements DragGestureListener,
|
|
DragSourceListener
|
|
{
|
|
private boolean scrolls;
|
|
|
|
// --- DragGestureListener methods -----------------------------------
|
|
|
|
/**
|
|
* A Drag gesture has been recognized.
|
|
* @param dge the <tt>DragGestureEvent</tt> that notified us
|
|
*/
|
|
public void dragGestureRecognized(DragGestureEvent dge)
|
|
{
|
|
JComponent c = (JComponent) dge.getComponent();
|
|
ExtendedTransferHandler th
|
|
= (ExtendedTransferHandler) c.getTransferHandler();
|
|
|
|
Transferable t = th.createTransferable(c);
|
|
if (t != null)
|
|
{
|
|
scrolls = c.getAutoscrolls();
|
|
c.setAutoscrolls(false);
|
|
try
|
|
{
|
|
Image img = null;
|
|
Icon icn = th.getVisualRepresentation(t);
|
|
|
|
if (icn != null)
|
|
{
|
|
if (icn instanceof ImageIcon)
|
|
{
|
|
img = ((ImageIcon) icn).getImage();
|
|
}
|
|
else
|
|
{
|
|
img = new BufferedImage(icn.getIconWidth(),
|
|
icn.getIconHeight(),
|
|
BufferedImage.TYPE_4BYTE_ABGR);
|
|
Graphics g = img.getGraphics();
|
|
icn.paintIcon(c, g, 0, 0);
|
|
}
|
|
}
|
|
if (img == null)
|
|
{
|
|
dge.startDrag(null, t, this);
|
|
}
|
|
else
|
|
{
|
|
dge.startDrag(null, img,
|
|
new Point(0, -1 * img.getHeight(null)), t, this);
|
|
}
|
|
|
|
return;
|
|
}
|
|
catch (RuntimeException re)
|
|
{
|
|
c.setAutoscrolls(scrolls);
|
|
}
|
|
}
|
|
|
|
th.exportDone(c, t, NONE);
|
|
}
|
|
|
|
// --- DragSourceListener methods -----------------------------------
|
|
|
|
/**
|
|
* As the hotspot enters a platform dependent drop site.
|
|
* @param e the <tt>DragSourceDragEvent</tt> containing the details of
|
|
* the drag
|
|
*/
|
|
public void dragEnter(DragSourceDragEvent e)
|
|
{}
|
|
|
|
/**
|
|
* As the hotspot moves over a platform dependent drop site.
|
|
* @param e the <tt>DragSourceDragEvent</tt> containing the details of
|
|
* the drag
|
|
*/
|
|
public void dragOver(DragSourceDragEvent e)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* As the hotspot exits a platform dependent drop site.
|
|
* @param e the <tt>DragSourceDragEvent</tt> containing the details of
|
|
* the drag
|
|
*/
|
|
public void dragExit(DragSourceEvent e)
|
|
{}
|
|
|
|
/**
|
|
* As the operation completes.
|
|
* @param e the <tt>DragSourceDragEvent</tt> containing the details of
|
|
* the drag
|
|
*/
|
|
public void dragDropEnd(DragSourceDropEvent e)
|
|
{
|
|
DragSourceContext dsc = e.getDragSourceContext();
|
|
JComponent c = (JComponent) dsc.getComponent();
|
|
|
|
if (e.getDropSuccess())
|
|
{
|
|
((ExtendedTransferHandler) c.getTransferHandler())
|
|
.exportDone(c, dsc.getTransferable(), e.getDropAction());
|
|
}
|
|
else
|
|
{
|
|
((ExtendedTransferHandler) c.getTransferHandler())
|
|
.exportDone(c, dsc.getTransferable(), NONE);
|
|
}
|
|
c.setAutoscrolls(scrolls);
|
|
}
|
|
|
|
public void dropActionChanged(DragSourceDragEvent dsde) {}
|
|
}
|
|
}
|