- Issue #690 (Add drag and drop of files in the contact list).

- Show an error message when file transfer is not supported by the selected account.
cusax-fix
Yana Stamcheva 17 years ago
parent 0cadd75995
commit 26c28dfbda

@ -152,8 +152,10 @@ service.gui.FILE_RECEIVING_FROM=Receiving file from {0}.
service.gui.FILE_SEND_COMPLETED=File has been successfully sent to {0}.
service.gui.FILE_RECEIVE_COMPLETED=File has been received from {0}.
service.gui.FILE_TRANSFER_CANCELED=File transfer has been canceled.
service.gui.FILE_SEND_FAILED=Failed to send file: {0}.
service.gui.FILE_SEND_REFUSED={0} has refused this file.
service.gui.FILE_TRANSFER_REFUSED=File transfer has been rejected.
service.gui.FILE_TRANSFER_NOT_SUPPORTED=The contact you have selected doesn't support file transfers.
service.gui.FILE_RECEIVE_REFUSED=You have refused the file from {0}.
service.gui.FILE_TRANSFER_PREPARING=Preparing file transfer with {0}. Please wait...
service.gui.FILE_TRANSFER_REQUEST_RECIEVED={0} is sharing a file with you.

@ -200,6 +200,7 @@ public String processMessage(ChatMessage chatMessage)
String contentType = chatMessage.getContentType();
long date = chatMessage.getDate();
String messageType = chatMessage.getMessageType();
String messageTitle = chatMessage.getMessageTitle();
String message = chatMessage.getMessage();
String msgID = "message";
@ -320,8 +321,7 @@ else if (messageType.equals(Constants.ERROR_MESSAGE))
+ "' </IMG>";
chatString += errorIcon
+ GuiActivator.getResources()
.getI18NString("service.gui.MSG_DELIVERY_FAILURE")
+ messageTitle
+ endHeaderTag + "<h5>" + message + "</h5>";
}
else if (messageType.equals(Constants.HISTORY_INCOMING_MESSAGE))

@ -29,6 +29,12 @@ public class ChatMessage
*/
private final String messageType;
/**
* The title of the message. This property is optional and could be used
* to show a title for error messages.
*/
private String messageTitle;
/**
* The content of the message.
*/
@ -61,6 +67,30 @@ public ChatMessage( String contactName,
this.contentType = contentType;
}
/**
* Creates a <tt>ChatMessage</tt> by specifying all parameters of the
* message.
* @param contactName the name of the contact
* @param date the date and time
* @param messageType the type (INCOMING or OUTGOING)
* @param message the content
* @param contentType the content type (e.g. "text", "text/html", etc.)
*/
public ChatMessage( String contactName,
long date,
String messageType,
String messageTitle,
String message,
String contentType)
{
this.contactName = contactName;
this.date = date;
this.messageType = messageType;
this.messageTitle = messageTitle;
this.message = message;
this.contentType = contentType;
}
/**
* Returns the name of the contact sending the message.
*
@ -91,6 +121,16 @@ public String getMessageType()
return messageType;
}
/**
* Returns the title of the message.
*
* @return the title of the message.
*/
public String getMessageTitle()
{
return messageTitle;
}
/**
* Returns the content of the message.
*

@ -574,11 +574,12 @@ else if (o instanceof FileRecord)
* for processing and appends it at the end of the conversationPanel
* document.
*
* @param contactName The name of the contact sending the message.
* @param date The time at which the message is sent or received.
* @param messageType The type of the message. One of OUTGOING_MESSAGE
* or INCOMING_MESSAGE.
* @param message The message text.
* @param contactName the name of the contact sending the message
* @param date the time at which the message is sent or received
* @param messageType the type of the message. One of OUTGOING_MESSAGE
* or INCOMING_MESSAGE
* @param message the message text
* @param contentType the content type
*/
public void addMessage(String contactName, long date,
String messageType, String message, String contentType)
@ -586,6 +587,40 @@ public void addMessage(String contactName, long date,
ChatMessage chatMessage = new ChatMessage(contactName, date,
messageType, message, contentType);
this.addChatMessage(chatMessage);
}
/**
* Passes the message to the contained <code>ChatConversationPanel</code>
* for processing and appends it at the end of the conversationPanel
* document.
*
* @param contactName the name of the contact sending the message
* @param date the time at which the message is sent or received
* @param messageType the type of the message. One of OUTGOING_MESSAGE
* or INCOMING_MESSAGE
* @param title the title of the message
* @param message the message text
* @param contentType the content type
*/
public void addMessage(String contactName, long date,
String messageType, String title, String message, String contentType)
{
ChatMessage chatMessage = new ChatMessage(contactName, date,
messageType, title, message, contentType);
this.addChatMessage(chatMessage);
}
/**
* Passes the message to the contained <code>ChatConversationPanel</code>
* for processing and appends it at the end of the conversationPanel
* document.
*
* @param chatMessage the chat message to add
*/
private void addChatMessage(ChatMessage chatMessage)
{
if (ConfigurationManager.isHistoryShown() && !isHistoryLoaded)
{
synchronized (incomingEventBuffer)
@ -599,7 +634,23 @@ public void addMessage(String contactName, long date,
}
// change the last history message timestamp after we add one.
this.lastHistoryMsgTimestamp = date;
this.lastHistoryMsgTimestamp = chatMessage.getDate();
}
/**
* Adds the given error message to the chat window conversation area.
*
* @param contactName the name of the contact, for which the error occured
* @param message the error message
*/
public void addErrorMessage(String contactName,
String message)
{
this.addMessage(contactName, System.currentTimeMillis(),
Constants.ERROR_MESSAGE,
GuiActivator.getResources()
.getI18NString("service.gui.MSG_DELIVERY_FAILURE"),
message, "text");
}
/**
@ -608,10 +659,14 @@ public void addMessage(String contactName, long date,
* @param contactName the name of the contact, for which the error occured
* @param message the error message
*/
public void addErrorMessage(String contactName, String message)
public void addErrorMessage(String contactName,
String title,
String message)
{
this.addMessage(contactName, System.currentTimeMillis(),
Constants.ERROR_MESSAGE, message, "text");
Constants.ERROR_MESSAGE,
title,
message, "text");
}
/**
@ -914,9 +969,7 @@ public void run()
public void catchException(Throwable ex)
{
logger.error("Failed to send message.", ex);
refreshWriteArea();
logger.error("Failed to send file.", ex);
if (ex instanceof IllegalStateException)
{
@ -949,13 +1002,38 @@ public void sendFile(final File file)
final ChatTransport fileTransferTransport
= findFileTransferChatTransport();
// If there's no operation set we show some "not supported" messages
// and we return.
if (fileTransferTransport == null)
{
logger.error("Failed to send file.");
this.addErrorMessage(
chatSession.getChatName(),
GuiActivator.getResources().getI18NString(
"service.gui.FILE_SEND_FAILED",
new String[]{file.getName()}),
GuiActivator.getResources().getI18NString(
"service.gui.FILE_TRANSFER_NOT_SUPPORTED"));
return;
}
final SendFileConversationComponent fileComponent
= new SendFileConversationComponent(
this,
fileTransferTransport.getDisplayName(),
file);
getChatConversationPanel().addComponent(fileComponent);
if (ConfigurationManager.isHistoryShown() && !isHistoryLoaded)
{
synchronized (incomingEventBuffer)
{
incomingEventBuffer.add(fileComponent);
}
}
else
getChatConversationPanel().addComponent(fileComponent);
this.sendFile(file, fileComponent);
}

@ -15,6 +15,7 @@
import javax.swing.text.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
/**
* A TransferHandler that we use to handle copying, pasting and DnD operations
@ -28,7 +29,7 @@
* @author Yana Stamcheva
*/
public class ChatTransferHandler
extends TransferHandler
extends ExtendedTransferHandler
{
/**
* This class logger.
@ -52,75 +53,6 @@ public ChatTransferHandler(ChatPanel chatPanel)
this.chatPanel = chatPanel;
}
/**
* 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_OR_MOVE;
}
/** 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 support the object containing the details of the transfer, not
* <code>null</code>.
* @param transferFlavors 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[])
{
JTextComponent c = (JTextComponent)comp;
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 (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.
*/
public Transferable createTransferable(JComponent comp)
{
if (comp instanceof JTextPane)
{
return new SelectedTextTransferable((JTextPane) comp);
}
return null;
}
/**
* Handles transfers to the chat panel from the clip board or a
* DND drop operation. The <tt>Transferable</tt> parameter contains the
@ -202,95 +134,4 @@ else if (t.isDataFlavorSupported(DataFlavor.stringFlavor))
}
return false;
}
/**
* 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 clip 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 JTextPane textPane;
public SelectedTextTransferable(JTextPane textPane)
{
this.textPane = textPane;
}
// Returns supported flavors
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[]{DataFlavor.stringFlavor};
}
// Returns true if flavor is supported
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return DataFlavor.stringFlavor.equals(flavor);
}
// Returns Selected Text
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException
{
if (!DataFlavor.stringFlavor.equals(flavor))
{
throw new UnsupportedFlavorException(flavor);
}
return textPane.getSelectedText();
}
}
}

@ -0,0 +1,118 @@
/*
* 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.impl.gui.main.contactlist;
import java.awt.datatransfer.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
/**
* A TransferHandler that we use to handle DnD operations of files in our
* <tt>ContactList</tt>.
*
* @author Yana Stamcheva
*/
public class ContactListTransferHandler
extends ExtendedTransferHandler
{
private static final Logger logger
= Logger.getLogger(ContactListTransferHandler.class);
private final DefaultContactList contactList;
public ContactListTransferHandler(DefaultContactList contactList)
{
this.contactList = contactList;
}
/**
* Handles transfers to the contact list from the clip board or a
* DND drop operation. The <tt>Transferable</tt> parameter contains the
* data that needs to be imported.
* <p>
* @param comp the component to receive the transfer;
* @param transferable the data to import
* @return true if the data was inserted into the component and false
* otherwise
* @see #importData(TransferHandler.TransferSupport)
*/
public boolean importData(JComponent comp, Transferable t)
{
if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
{
try
{
Object o = t.getTransferData(DataFlavor.javaFileListFlavor);
ChatPanel chatPanel = getChatPanel();
if (chatPanel != null)
{
if (o instanceof java.util.Collection)
{
Collection<File> files = (Collection<File>) o;
for(File file: files)
{
if (chatPanel != null)
chatPanel.sendFile(file);
GuiActivator.getUIService().getChatWindowManager()
.openChat(chatPanel, false);
}
// Otherwise fire files dropped event.
return true;
}
}
}
catch (UnsupportedFlavorException e)
{
logger.debug("Failed to drop files.", e);
}
catch (IOException e)
{
logger.debug("Failed to drop files.", e);
}
}
return false;
}
/**
* Returns the <tt>ChatPanel</tt> corresponding to the currently selected
* contact.
*
* @return the <tt>ChatPanel</tt> corresponding to the currently selected
* contact.
*/
private ChatPanel getChatPanel()
{
ChatPanel chatPanel = null;
Object selectedObject = contactList.getSelectedValue();
if (selectedObject != null && selectedObject instanceof MetaContact)
{
MetaContact metaContact = (MetaContact) selectedObject;
// Obtain the corresponding chat panel.
chatPanel
= GuiActivator.getUIService().getChatWindowManager()
.getContactChat(metaContact);
}
return chatPanel;
}
}

@ -36,6 +36,7 @@ public DefaultContactList()
this.getSelectionModel().setSelectionMode(
ListSelectionModel.SINGLE_SELECTION);
this.setTransferHandler(new ContactListTransferHandler(this));
this.setCellRenderer(new ContactListCellRenderer());
}

@ -0,0 +1,191 @@
/*
* 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.datatransfer.*;
import java.io.*;
import javax.swing.*;
import javax.swing.text.*;
/**
* 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
{
/**
* 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_OR_MOVE;
}
/** 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 support the object containing the details of the transfer, not
* <code>null</code>.
* @param transferFlavors 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.
*/
public Transferable createTransferable(JComponent comp)
{
if (comp instanceof JTextPane)
{
return new SelectedTextTransferable((JTextPane) comp);
}
return super.createTransferable(comp);
}
/**
* 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 clip 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 JTextPane textPane;
public SelectedTextTransferable(JTextPane textPane)
{
this.textPane = textPane;
}
// Returns supported flavors
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[]{DataFlavor.stringFlavor};
}
// Returns true if flavor is supported
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return DataFlavor.stringFlavor.equals(flavor);
}
// Returns Selected Text
public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException, IOException
{
if (!DataFlavor.stringFlavor.equals(flavor))
{
throw new UnsupportedFlavorException(flavor);
}
return textPane.getSelectedText();
}
}
}
Loading…
Cancel
Save