diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties index 1f48d4f89..80a178035 100644 --- a/resources/languages/resources.properties +++ b/resources/languages/resources.properties @@ -107,7 +107,8 @@ service.gui.CONNECTION_FAILED_MSG=Connection failed for the following account: U service.gui.CONNECTION_EXPIRED_MSG=You are currently disconnected from the {0} server. service.gui.CONNECTION_REQUIRED_TO_JOIN=You have to be connected in order to join a chat room. Please connect and then try again. service.gui.CONNECTION_REQUIRED_TO_LEAVE=You have to be connected in order to leave a chat room. -service.gui.CONTACT_NOT_SUPPORTING_TELEPHONY=The choosen {0} contact doesn''t support telephony. +service.gui.CONTACT_NOT_SUPPORTING_TELEPHONY=The chosen {0} contact doesn''t support telephony. +service.gui.CONTACT_NOT_SUPPORTING_CHAT_CONF=The chosen {0} contact doesn''t support chat conferencing. service.gui.CONTACT_PAUSED_TYPING={0} paused typing the message service.gui.CONTACT_TYPING={0} is typing a message service.gui.CONTACT_TYPING_STATE_STALE=typing state not updated diff --git a/src/net/java/sip/communicator/impl/gui/lookandfeel/SIPCommCallComboBoxUI.java b/src/net/java/sip/communicator/impl/gui/lookandfeel/SIPCommCallComboBoxUI.java index b8ac425b4..561f18ac9 100644 --- a/src/net/java/sip/communicator/impl/gui/lookandfeel/SIPCommCallComboBoxUI.java +++ b/src/net/java/sip/communicator/impl/gui/lookandfeel/SIPCommCallComboBoxUI.java @@ -12,8 +12,19 @@ import javax.swing.plaf.*; import javax.swing.plaf.basic.*; +/** + * A custom implementation of the SIPCommComboBoxUI specially designed + * for the call combo box. + * @author Yana Stamcheva + */ public class SIPCommCallComboBoxUI extends SIPCommComboBoxUI { + /** + * Creates an instance of the SIPCommCallComboBoxUI for the given + * component. + * @param c the component for which we create the UI + * @return an instance of the SIPCommCallComboBoxUI + */ public static ComponentUI createUI(JComponent c) { return new SIPCommCallComboBoxUI(); @@ -32,7 +43,7 @@ protected ComboPopup createPopup() return popup; } - + private static class SIPCommComboPopup extends BasicComboPopup { private static final long serialVersionUID = 0L; @@ -46,18 +57,21 @@ public SIPCommComboPopup(JComboBox combo) * Makes the popup visible if it is hidden and makes it hidden if it is * visible. */ - protected void togglePopup() { - if ( isVisible() ) { + protected void togglePopup() + { + if ( isVisible() ) + { hide(); } - else { + else + { setListSelection(comboBox.getSelectedIndex()); Point location = getPopupLocation(); show( comboBox, location.x, location.y ); } } - + /** * Configures the popup portion of the combo box. This method is called * when the UI class is created. @@ -71,7 +85,7 @@ protected void configurePopup() { setDoubleBuffered( true ); setFocusable( false ); } - + /** * Sets the list selection index to the selectedIndex. This * method is used to synchronize the list selection with the @@ -79,28 +93,31 @@ protected void configurePopup() { * * @param selectedIndex the index to set the list */ - private void setListSelection(int selectedIndex) { - if ( selectedIndex == -1 ) { + private void setListSelection(int selectedIndex) + { + if ( selectedIndex == -1 ) list.clearSelection(); - } - else { + else + { list.setSelectedIndex( selectedIndex ); - list.ensureIndexIsVisible( selectedIndex ); + list.ensureIndexIsVisible( selectedIndex ); } } - + /** * Calculates the upper left location of the Popup. + * @return the Point indicating the location of the popup */ - private Point getPopupLocation() { + private Point getPopupLocation() + { Dimension popupSize = comboBox.getSize(); Insets insets = getInsets(); - // reduce the width of the scrollpane by the insets so that the popup - // is the same width as the combo box. + // reduce the width of the scrollpane by the insets so that the + // popup is the same width as the combo box. int popupHeight = getPopupHeightForRowCount( comboBox.getMaximumRowCount()); - + popupSize.setSize(popupSize.width - (insets.right + insets.left), popupHeight); Rectangle popupBounds = computePopupBounds( @@ -108,16 +125,16 @@ private Point getPopupLocation() { comboBox.getEditor().getEditorComponent().getBounds().y - popupHeight - 4, popupSize.width, popupSize.height); - + Dimension scrollSize = popupBounds.getSize(); Point popupLocation = popupBounds.getLocation(); - + scroller.setMaximumSize( scrollSize ); scroller.setPreferredSize( scrollSize ); scroller.setMinimumSize( scrollSize ); - + list.revalidate(); - + return popupLocation; } } diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallComboBox.java b/src/net/java/sip/communicator/impl/gui/main/call/CallComboBox.java index 0c1ec32be..6520495a1 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallComboBox.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallComboBox.java @@ -17,6 +17,7 @@ import net.java.sip.communicator.impl.gui.main.contactlist.*; import net.java.sip.communicator.service.callhistory.*; import net.java.sip.communicator.service.contactlist.*; +import net.java.sip.communicator.util.swing.*; /** * The CallComboBox is a history editable combo box that is @@ -53,8 +54,8 @@ public CallComboBox(MainCallPanel parentCallPanel) JTextField textField = (JTextField) this.getEditor().getEditorComponent(); + textField.setTransferHandler(new ExtendedTransferHandler()); textField.getDocument().addDocumentListener(this); - textField.getActionMap().put("createCall", new CreateCallAction()); textField.getInputMap().put( KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "createCall"); diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java index 890a60489..d87cec9b7 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java @@ -44,7 +44,7 @@ public class CallDialog private final Container contentPane = getContentPane(); - private Component callPanel = null; + private JComponent callPanel = null; private final HoldButton holdButton; @@ -154,7 +154,6 @@ public CallDialog(Call call) this.callDurationTimer = new Timer(1000, new CallTimerListener()); this.callDurationTimer.setRepeats(true); - } /** @@ -541,12 +540,7 @@ public void actionPerformed(ActionEvent e) { if (isLastConference) { - if (call.getCallPeerCount() > 1) - { - ((ConferenceCallPanel) callPanel) - .removeCallPeerPanel(peer); - } - else + if (call.getCallPeerCount() == 1) { contentPane.remove(callPanel); CallPeer singlePeer = call.getCallPeers().next(); @@ -559,6 +553,11 @@ public void actionPerformed(ActionEvent e) isLastConference = false; } + else if (call.getCallPeerCount() > 1) + { + ((ConferenceCallPanel) callPanel) + .removeCallPeerPanel(peer); + } if (contentPane.isVisible()) pack(); diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java b/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java new file mode 100644 index 000000000..66c289ea2 --- /dev/null +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java @@ -0,0 +1,183 @@ +/* + * 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.call; + +import java.awt.datatransfer.*; +import java.awt.im.*; +import java.io.*; +import java.util.*; + +import javax.swing.*; + +import net.java.sip.communicator.impl.gui.*; +import net.java.sip.communicator.impl.gui.customcontrols.*; +import net.java.sip.communicator.service.contactlist.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.swing.*; + +/** + * A TransferHandler that we use to handle dropping of MetaContacts or + * simple string addresses to an existing Call. Dropping of a such data + * in the CallDialog would result in the creation of a call conference. + * + * @author Yana Stamcheva + */ +public class CallTransferHandler + extends ExtendedTransferHandler +{ + private static final Logger logger + = Logger.getLogger(CallTransferHandler.class); + + private final Call call; + + /** + * Creates an instance of CallTransferHandler by specifying the + * call, to which dragged callees will be added. + * @param call the call to which the dragged callees will be added + */ + public CallTransferHandler(Call call) + { + this.call = call; + } + + /** + * Indicates whether a component will accept an import of the given + * set of data flavors prior to actually attempting to import it. We return + * true to indicate that the transfer with at least one of the + * given flavors would work and false to reject the transfer. + *
+ * @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 support 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.stringFlavor)
+ || flavor[i].equals(metaContactDataFlavor))
+ {
+ if (comp instanceof JPanel)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Handles transfers to the chat panel from the clip board or a
+ * DND drop operation. The Transferable parameter contains the
+ * data that needs to be imported.
+ *
+ * @param comp the component to receive the transfer;
+ * @param t the data to import
+ * @return true if the data was inserted into the component and false
+ * otherwise
+ */
+ public boolean importData(JComponent comp, Transferable t)
+ {
+ if (t.isDataFlavorSupported(metaContactDataFlavor))
+ {
+ Object o = null;
+
+ try
+ {
+ o = t.getTransferData(metaContactDataFlavor);
+ }
+ catch (UnsupportedFlavorException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop meta contact.", e);
+ }
+ catch (IOException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop meta contact.", e);
+ }
+
+ if (o instanceof MetaContact)
+ {
+ Iterator
+ * @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
+ * @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
+ * 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
+ * Workaround provided by simon@tardell.se on 29-DEC-2002 for bug 4521075
+ * http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=a13e98ab2364524506eb91505565?bug_id=4521075
+ * "Drag gesture in JAVA different from Windows". The bug is also noticed
+ * on Mac Leopard.
+ *
+ * @param firstIndex the first selected index
+ * @param lastIndex the last selected index
+ * @param isAdjusting true if multiple changes are being made
+ */
+ protected void fireSelectionValueChanged(int firstIndex, int lastIndex,
+ boolean isAdjusting)
+ {
+ if (cachedMouseEvent != null)
+ {
+ super.processMouseEvent(new MouseEvent(
+ (Component) cachedMouseEvent.getSource(),
+ cachedMouseEvent.getID(),
+ cachedMouseEvent.getWhen(),
+ cachedMouseEvent.getModifiers(),
+ cachedMouseEvent.getX(),
+ cachedMouseEvent.getY(),
+ cachedMouseEvent.getClickCount(),
+ cachedMouseEvent.isPopupTrigger()));
+
+ cachedMouseEvent = null;
+ }
+ super.fireSelectionValueChanged(firstIndex, lastIndex, isAdjusting);
+ }
+
+ /**
+ * Caches the incoming mouse event before passing it to the parent
+ * implementation of processMouseEvent.
+ *
+ * Workaround provided by simon@tardell.se on 29-DEC-2002 for bug 4521075
+ * http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=a13e98ab2364524506eb91505565?bug_id=4521075
+ * "Drag gesture in JAVA different from Windows". The bug is also noticed
+ * on Mac Leopard.
+ * @param event the MouseEvent to process
+ */
+ protected void processMouseEvent(MouseEvent event)
+ {
+ if ((event.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0)
+ cachedMouseEvent= event;
+ super.processMouseEvent(event);
+ }
}
diff --git a/src/net/java/sip/communicator/util/GuiUtils.java b/src/net/java/sip/communicator/util/GuiUtils.java
index 6fe6da03f..07f3c1de2 100644
--- a/src/net/java/sip/communicator/util/GuiUtils.java
+++ b/src/net/java/sip/communicator/util/GuiUtils.java
@@ -7,6 +7,8 @@
package net.java.sip.communicator.util;
import java.awt.*;
+import java.awt.font.*;
+import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
@@ -53,7 +55,7 @@ public static String replaceSpecialRegExpChars(String text)
{
return text.replaceAll("([.()^&$*|])", "\\\\$1");
}
-
+
/**
* Returns the width in pixels of a text.
* @param c the component where the text is contained
@@ -66,6 +68,43 @@ public static int getStringWidth(Component c, String text)
.getFontMetrics(c.getFont()), text);
}
+ /**
+ * Returns the bounds of the given string.
+ * @param text the string to measure
+ * @return the bounds of the given string
+ */
+ public static Rectangle2D getStringBounds(String text)
+ {
+ Font font = UIManager.getFont("Label.font");
+
+ FontRenderContext frc = new FontRenderContext(null, true, false);
+
+ TextLayout layout = new TextLayout(text, font, frc);
+
+ return layout.getBounds();
+ }
+
+ /**
+ * Counts occurrences of the needle character in the given
+ * text.
+ * @param text the text in which we search
+ * @param needle the character we're looking for
+ * @return the count of occurrences of the needle chat in the
+ * given text
+ */
+ public static int countOccurrences(String text, char needle)
+ {
+ int count = 0;
+ for (char c : text.toCharArray())
+ {
+ if (c == needle)
+ {
+ ++count;
+ }
+ }
+ return count;
+ }
+
/**
* Compares the two dates. The comparison is based only on the day, month
* and year values. Returns 0 if the two dates are equals, a value < 0 if
@@ -173,6 +212,13 @@ public static String formatDate(final long date)
return strBuf.toString();
}
+ /**
+ * Formats the given date as: Month DD, YYYY and appends it to the given
+ * dateStrBuf string buffer.
+ * @param date the date to format
+ * @param dateStrBuf the StringBuffer, where to append the
+ * formatted date
+ */
public static void formatDate(long date, StringBuffer dateStrBuf)
{
c1.setTimeInMillis(date);
@@ -296,6 +342,8 @@ private static void formatTime(int time, StringBuffer timeStrBuf)
/**
* Formats the given long to X hour, Y min, Z sec.
+ * @param millis the time in milliseconds to format
+ * @return the formatted seconds
*/
public static String formatSeconds(long millis)
{
diff --git a/src/net/java/sip/communicator/util/swing/ExtendedTransferHandler.java b/src/net/java/sip/communicator/util/swing/ExtendedTransferHandler.java
index 0d79c63a8..3aaba072e 100644
--- a/src/net/java/sip/communicator/util/swing/ExtendedTransferHandler.java
+++ b/src/net/java/sip/communicator/util/swing/ExtendedTransferHandler.java
@@ -6,12 +6,20 @@
*/
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
@@ -26,6 +34,19 @@
public class ExtendedTransferHandler
extends TransferHandler
{
+ /**
+ * The data flavor used when transferring MetaContacts.
+ */
+ protected static final DataFlavor metaContactDataFlavor
+ = new DataFlavor(MetaContact.class, "MetaContact");
+
+ /**
+ * The drag icon that is the visual representation of the contained
+ * Transferable.
+ */
+ 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 COPY, MOVE
@@ -42,11 +63,12 @@ public class ExtendedTransferHandler
*/
public int getSourceActions(JComponent c)
{
- return TransferHandler.COPY_OR_MOVE;
+ 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
+ /**
+ * Indicates whether a component will accept an import of the given
+ * set of data flavors prior to actually attempting to import it. We return
* true to indicate that the transfer with at least one of the
* given flavors would work and false to reject the transfer.
*
@@ -79,22 +101,25 @@ else if (flavor[i].equals(DataFlavor.stringFlavor))
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
+ * Transferable
+ * @return the created Transferable
*/
- public Transferable createTransferable(JComponent comp)
+ public Transferable createTransferable(JComponent component)
{
- if (comp instanceof JTextPane)
+ if (component instanceof JTextPane
+ || component instanceof JTextField)
{
- return new SelectedTextTransferable((JTextPane) comp);
+ return new SelectedTextTransferable((JTextComponent) component);
}
- return super.createTransferable(comp);
+ return super.createTransferable(component);
}
/**
@@ -131,7 +156,7 @@ public void exportToClipboard(JComponent comp,
Document doc = textComponent.getDocument();
String srcData = doc.getText(startIndex,
endIndex - startIndex);
- StringSelection contents =new StringSelection(srcData);
+ StringSelection contents = new StringSelection(srcData);
// this may throw an IllegalStateException,
// but it will be caught and handled in the
@@ -156,35 +181,294 @@ public void exportToClipboard(JComponent comp,
*/
public class SelectedTextTransferable implements Transferable
{
- private JTextPane textPane;
+ private JTextComponent textComponent;
- public SelectedTextTransferable(JTextPane textPane)
+ /**
+ * Creates an instance of SelectedTextTransferable.
+ * @param component the text component
+ */
+ public SelectedTextTransferable(JTextComponent component)
{
- this.textPane = textPane;
+ this.textComponent = component;
}
- // Returns supported flavors
+ /**
+ * Returns supported flavors.
+ * @return an array of supported flavors
+ */
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[]{DataFlavor.stringFlavor};
}
- // Returns true if flavor is supported
+ /**
+ * Returns true if the given flavor is supported,
+ * otherwise returns false.
+ * @param flavor the data flavor to verify
+ * @return true if the given flavor is supported,
+ * otherwise returns false
+ */
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return DataFlavor.stringFlavor.equals(flavor);
}
- // Returns Selected Text
+ /**
+ * 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
+ throws UnsupportedFlavorException,
+ IOException
{
if (!DataFlavor.stringFlavor.equals(flavor))
{
throw new UnsupportedFlavorException(flavor);
}
- return textPane.getSelectedText();
+ return textComponent.getSelectedText();
+ }
+ }
+
+ /**
+ * Overrides TransferHandler.getVisualRepresentation(Transferable t)
+ * in order to return a custom drag icon.
+ *
+ * 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 support is {@code null}
+ */
+ public boolean canImport(JComponent comp, DataFlavor flavor[])
+ {
+ for (int i = 0, n = flavor.length; i < n; i++)
+ {
+ if (flavor[i].equals(metaContactDataFlavor))
+ {
+ return true;
+ }
+ }
+
+ return super.canImport(comp, flavor);
+ }
+
/**
* Handles transfers to the chat panel from the clip board or a
* DND drop operation. The Transferable parameter contains the
@@ -62,7 +91,6 @@ public ChatTransferHandler(ChatPanel chatPanel)
* @param t the data to import
* @return true if the data was inserted into the component and false
* otherwise
- * @see #importData(TransferHandler.TransferSupport)
*/
@SuppressWarnings("unchecked") //the case is taken care of
public boolean importData(JComponent comp, Transferable t)
@@ -95,6 +123,58 @@ public boolean importData(JComponent comp, Transferable t)
logger.debug("Failed to drop files.", e);
}
}
+ else if (t.isDataFlavorSupported(metaContactDataFlavor))
+ {
+ Object o = null;
+
+ try
+ {
+ o = t.getTransferData(metaContactDataFlavor);
+ }
+ catch (UnsupportedFlavorException e)
+ {
+ logger.debug("Failed to drop meta contact.", e);
+ }
+ catch (IOException e)
+ {
+ logger.debug("Failed to drop meta contact.", e);
+ }
+
+ if (o instanceof MetaContact)
+ {
+ MetaContact metaContact = (MetaContact) o;
+
+ ChatTransport currentChatTransport
+ = chatPanel.getChatSession().getCurrentChatTransport();
+
+ Iteratorsupport is {@code null}
+ */
+ public boolean canImport(JComponent comp, DataFlavor flavor[])
+ {
+ for (int i = 0, n = flavor.length; i < n; i++)
+ {
+ if (flavor[i].equals(metaContactDataFlavor))
+ {
+ return true;
+ }
+ }
+
+ return super.canImport(comp, flavor);
+ }
+
/**
* Handles transfers to the contact list from the clip board or a
* DND drop operation. The Transferable parameter contains the
@@ -83,15 +133,113 @@ public boolean importData(JComponent comp, Transferable t)
GuiActivator.getUIService().getChatWindowManager()
.openChat(chatPanel, false);
}
-
- // Otherwise fire files dropped event.
return true;
}
}
}
+ else if (t.isDataFlavorSupported(metaContactDataFlavor))
+ {
+ Object o = null;
+
+ try
+ {
+ o = t.getTransferData(metaContactDataFlavor);
+ }
+ catch (UnsupportedFlavorException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop meta contact.", e);
+ }
+ catch (IOException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop meta contact.", e);
+ }
+
+ if (o instanceof MetaContact
+ && comp instanceof ContactList)
+ {
+ MetaContact transferredContact = (MetaContact) o;
+ ContactList list = (ContactList) comp;
+
+ Object dest = list.getSelectedValue();
+
+ if (transferredContact != null)
+ {
+ if (dest instanceof MetaContact)
+ {
+ MetaContact destContact = (MetaContact) dest;
+ if (transferredContact != destContact)
+ {
+ list.moveMetaContactToMetaContact(
+ transferredContact, destContact);
+ }
+ return true;
+ }
+ else if (dest instanceof MetaContactGroup)
+ {
+ MetaContactGroup destGroup = (MetaContactGroup) dest;
+
+ if (transferredContact.getParentMetaContactGroup()
+ != destGroup)
+ {
+ list.moveMetaContactToGroup(
+ transferredContact, destGroup);
+ }
+ return true;
+ }
+ }
+ }
+ }
return false;
}
+ /**
+ * Overrides TransferHandler.getVisualRepresentation(Transferable t)
+ * in order to return a custom drag icon.
+ * createTransferable method
+ * @return the icon to show when dragging
+ */
+ public Icon getVisualRepresentation(Transferable t)
+ {
+ ContactListCellRenderer renderer = null;
+
+ if (t instanceof ContactListTransferable)
+ {
+ ContactListTransferable transferable = ((ContactListTransferable) t);
+
+ try
+ {
+ renderer = (ContactListCellRenderer)
+ contactList.getCellRenderer()
+ .getListCellRendererComponent(
+ contactList,
+ transferable.getTransferData(metaContactDataFlavor),
+ transferable.getTransferIndex(), false, false);
+ }
+ catch (UnsupportedFlavorException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug(
+ "Unsupported flavor while" +
+ " obtaining transfer data.", e);
+ }
+ catch (IOException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug(
+ "The data for the request flavor" +
+ " is no longer available.", e);
+ }
+ }
+
+ return renderer;
+ }
+
/**
* Returns the ChatPanel corresponding to the currently selected
* contact.
@@ -109,13 +257,91 @@ private ChatPanel getChatPanel()
MetaContact metaContact = (MetaContact) selectedObject;
// Obtain the corresponding chat panel.
- chatPanel
- = GuiActivator
- .getUIService()
- .getChatWindowManager()
+ chatPanel = GuiActivator.getUIService().getChatWindowManager()
.getContactChat(metaContact, true);
}
return chatPanel;
}
+
+ /**
+ * Transferable for JList that enables drag and drop of contacts.
+ */
+ public class ContactListTransferable implements Transferable
+ {
+ private int transferredIndex;
+
+ private Object transferredObject;
+
+ /**
+ * Creates an instance of ContactListTransferable.
+ * @param index the index of the transferred object in the list
+ * @param o the transferred list object
+ */
+ public ContactListTransferable(int index, Object o)
+ {
+ this.transferredIndex = index;
+ this.transferredObject = o;
+ }
+
+ /**
+ * Returns supported flavors.
+ * @return an array of supported flavors
+ */
+ public DataFlavor[] getTransferDataFlavors()
+ {
+ return new DataFlavor[] { metaContactDataFlavor,
+ DataFlavor.stringFlavor};
+ }
+
+ /**
+ * Returns true if the given flavor is supported,
+ * otherwise returns false.
+ * @param flavor the data flavor to verify
+ * @return true if the given flavor is supported,
+ * otherwise returns false
+ */
+ public boolean isDataFlavorSupported(DataFlavor flavor)
+ {
+ return metaContactDataFlavor.equals(flavor)
+ || DataFlavor.stringFlavor.equals(flavor);
+ }
+
+ /**
+ * Returns the selected text.
+ * @param flavor the flavor
+ * @return the selected text
+ * @exception UnsupportedFlavorException if the requested data flavor
+ * is not supported.
+ * @exception IOException if the data is no longer available in the
+ * requested flavor.
+ */
+ public Object getTransferData(DataFlavor flavor)
+ throws UnsupportedFlavorException,
+ IOException
+ {
+ if (metaContactDataFlavor.equals(flavor))
+ {
+ return transferredObject;
+ }
+ else if (DataFlavor.stringFlavor.equals(flavor))
+ {
+ if (transferredObject instanceof MetaContact)
+ return ((MetaContact) transferredObject).getDisplayName();
+ }
+ else
+ throw new UnsupportedFlavorException(flavor);
+
+ return null;
+ }
+
+ /**
+ * Returns the index of the transferred list cell.
+ * @return the index of the transferred list cell
+ */
+ public int getTransferIndex()
+ {
+ return transferredIndex;
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactRightButtonMenu.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactRightButtonMenu.java
index 6d4636cbf..0c9808f99 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactRightButtonMenu.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactRightButtonMenu.java
@@ -593,24 +593,7 @@ else if (itemName.startsWith(moveToPrefix))
= mainFrame.getGroupByID(
itemName.substring(moveToPrefix.length()));
- try
- {
- if(group != null)
- {
- mainFrame.getContactList().
- moveMetaContact(contactItem, group);
- }
- }
- catch (Exception ex)
- {
- new ErrorDialog(
- mainFrame,
- GuiActivator.getResources().getI18NString(
- "service.gui.MOVE_TO_GROUP"),
- GuiActivator.getResources().getI18NString(
- "service.gui.MOVE_CONTACT_ERROR"),
- ex).showDialog();
- }
+ guiContactList.moveMetaContactToGroup(contactItem, group);
}
else if (itemName.startsWith(removeContactPrefix))
{
@@ -820,12 +803,11 @@ public void groupSelected(ContactListEvent evt)
if(moveAllContacts)
{
- mainFrame.getContactList()
- .moveMetaContact(contactItem, sourceGroup);
+ guiContactList.moveMetaContactToGroup(contactItem, sourceGroup);
}
else if(contactToMove != null)
{
- new MoveSubcontactThread(sourceGroup).start();
+ guiContactList.moveContactToGroup(contactToMove, sourceGroup);
}
guiContactList.setDisableOpenClose(false);
@@ -869,7 +851,8 @@ private void moveContact(MetaContact toMetaContact)
ErrorDialog.WARNING)
.showDialog();
}
- else {
+ else
+ {
guiContactList.removeExcContactListListener(this);
// FIXME: unset the special cursor after a subcontact has been moved
@@ -878,71 +861,13 @@ private void moveContact(MetaContact toMetaContact)
if(moveAllContacts)
{
- new MoveAllSubcontactsThread(toMetaContact).start();
+ guiContactList.moveMetaContactToMetaContact(
+ contactItem, toMetaContact);
}
else if(contactToMove != null)
{
- new MoveSubcontactThread(toMetaContact).start();
- }
- }
- }
-
- /**
- * Moves the previously chosen contact in the given meta group or meta
- * contact.
- */
- private class MoveSubcontactThread extends Thread
- {
- private MetaContact metaContact;
-
- private MetaContactGroup metaGroup;
-
- public MoveSubcontactThread(MetaContact metaContact)
- {
- this.metaContact = metaContact;
- }
-
- public MoveSubcontactThread(MetaContactGroup metaGroup)
- {
- this.metaGroup = metaGroup;
- }
-
- public void run()
- {
- if(metaContact != null)
- {
- mainFrame.getContactList()
- .moveContact(contactToMove, metaContact);
- }
- else {
- mainFrame.getContactList()
- .moveContact(contactToMove, metaGroup);
- }
- }
- }
-
- /**
- * Moves all sub-contacts contained in the previously selected meta contact
- * in the given meta contact.
- */
- private class MoveAllSubcontactsThread extends Thread
- {
- private MetaContact metaContact;
-
- public MoveAllSubcontactsThread(MetaContact metaContact)
- {
- this.metaContact = metaContact;
- }
-
- public void run()
- {
- IteratorcreateTransferable 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.
+ * TransferHandlers
+ * 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 COPY or MOVE;
+ * 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 TransferHandler.
+ */
+ private static class DragHandler
+ implements DragGestureListener,
+ DragSourceListener
+ {
+ private boolean scrolls;
+
+ // --- DragGestureListener methods -----------------------------------
+
+ /**
+ * A Drag gesture has been recognized.
+ * @param dge the DragGestureEvent 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 DragSourceDragEvent containing the details of
+ * the drag
+ */
+ public void dragEnter(DragSourceDragEvent e)
+ {}
+
+ /**
+ * As the hotspot moves over a platform dependent drop site.
+ * @param e the DragSourceDragEvent containing the details of
+ * the drag
+ */
+ public void dragOver(DragSourceDragEvent e)
+ {
+ }
+
+ /**
+ * As the hotspot exits a platform dependent drop site.
+ * @param e the DragSourceDragEvent containing the details of
+ * the drag
+ */
+ public void dragExit(DragSourceEvent e)
+ {}
+
+ /**
+ * As the operation completes.
+ * @param e the DragSourceDragEvent 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) {}
+ }
}
diff --git a/src/net/java/sip/communicator/util/util.manifest.mf b/src/net/java/sip/communicator/util/util.manifest.mf
index f63e2a08c..594943a78 100644
--- a/src/net/java/sip/communicator/util/util.manifest.mf
+++ b/src/net/java/sip/communicator/util/util.manifest.mf
@@ -28,6 +28,7 @@ Import-Package: org.xml.sax,
net.java.sip.communicator.service.resources,
net.java.sip.communicator.service.keybindings,
net.java.sip.communicator.service.configuration,
+ net.java.sip.communicator.service.contactlist,
sun.awt.shell
Export-Package: net.java.sip.communicator.util.xml,
net.java.sip.communicator.util.swing.plaf,