[sip-comm-dev] Drag and drop


#1

Hi,

The way how contacts are added to a conference chat room is a bit inconvenient at the moment (push to "Invite others to the chat" button opens a dialog, where one has to specify an account ID of a contact and a reason). I think that adding contacts by dragging them to the chat's contacts panel is a bit more intuitive (and maybe specify a reason in the dialog).
Attached patch file contains the implementation that enables this drag and drop functionality (at the moment the reason is not asked, an invitation with empty reason is sent).
Also the patch contains the implementation of a generic message filter that I sent first time in December.
Maybe someone could take a look at these two things and give some feedback (if something should be changed/improved I am eager to do this).

Best,
Keio Kraaner

···

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net


#2

Forgot to attach the patch.

Keio Kraaner wrote:

SIP Communicator patch.txt (40.5 KB)

···

Hi,

The way how contacts are added to a conference chat room is a bit inconvenient at the moment (push to "Invite others to the chat" button opens a dialog, where one has to specify an account ID of a contact and a reason). I think that adding contacts by dragging them to the chat's contacts panel is a bit more intuitive (and maybe specify a reason in the dialog).
Attached patch file contains the implementation that enables this drag and drop functionality (at the moment the reason is not asked, an invitation with empty reason is sent).
Also the patch contains the implementation of a generic message filter that I sent first time in December.
Maybe someone could take a look at these two things and give some feedback (if something should be changed/improved I am eager to do this).

Best,
Keio Kraaner

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net


#3

Hello Keio,

Sorry for not answering earlier, was quite busy lately but hope that
things would be going more smoothly from now on.

The way it sounds, i really like your patch, I'll try to review and
commit it during the weekend. I'll let you know if I stumble upon any
problems ... and I guess I'll also let you know if I don't :wink: .

Thanks!
Emil

Keio Kraaner написа:

···

Forgot to attach the patch.

Keio Kraaner wrote:

Hi,

The way how contacts are added to a conference chat room is a bit
inconvenient at the moment (push to "Invite others to the chat" button
opens a dialog, where one has to specify an account ID of a contact
and a reason). I think that adding contacts by dragging them to the
chat's contacts panel is a bit more intuitive (and maybe specify a
reason in the dialog).
Attached patch file contains the implementation that enables this drag
and drop functionality (at the moment the reason is not asked, an
invitation with empty reason is sent).
Also the patch contains the implementation of a generic message filter
that I sent first time in December.
Maybe someone could take a look at these two things and give some
feedback (if something should be changed/improved I am eager to do this).

Best,
Keio Kraaner

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net

------------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net


#4

Hi,
updated the patch file:
* fixed a bug - DnD did not work after the chat window was closed and reopened;
* resolved conflicts with some newer changes (those changes did not allow to applyto the patch correctly any more).

Best,
Keio

SIP Communicator patch.txt (41.3 KB)

···

----- Original Message -----

From: "Keio Kraaner" <keio@ut.ee>

To: <dev@sip-communicator.dev.java.net>
Sent: Tuesday, February 26, 2008 12:23 PM
Subject: Re: [sip-comm-dev] Drag and drop

Forgot to attach the patch.

Keio Kraaner wrote:

Hi,

The way how contacts are added to a conference chat room is a bit
inconvenient at the moment (push to "Invite others to the chat" button
opens a dialog, where one has to specify an account ID of a contact
and a reason). I think that adding contacts by dragging them to the
chat's contacts panel is a bit more intuitive (and maybe specify a
reason in the dialog).
Attached patch file contains the implementation that enables this drag
and drop functionality (at the moment the reason is not asked, an
invitation with empty reason is sent).
Also the patch contains the implementation of a generic message filter
that I sent first time in December.
Maybe someone could take a look at these two things and give some
feedback (if something should be changed/improved I am eager to do this).

Best,
Keio Kraaner

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net

--------------------------------------------------------------------------------

Index: src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java

--- src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java (revision 3320)
+++ src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java (working copy)
@@ -102,4 +102,20 @@
     * <tt>false</tt> otherwise.
     */
    public boolean isContentTypeSupported(String contentType);
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter);
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if a event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter);
}
Index: src/net/java/sip/communicator/service/protocol/event/EventFilter.java

--- src/net/java/sip/communicator/service/protocol/event/EventFilter.java (revision 0)
+++ src/net/java/sip/communicator/service/protocol/event/EventFilter.java (revision 0)
@@ -0,0 +1,28 @@
+/*
+ * 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.service.protocol.event;
+
+import java.util.*;
+
+/**
+ * An event filter that decides if an event should be filtered out or not.
+ * For instance, maybe some type of received messages should not be
+ * shown in the chat windows.
+ *
+ * @author Keio Kraaner
+ */
+public interface EventFilter
+{
+ /**
+ * Checks if an event should be filtered out or processed.
+ *
+ * @param msg The event that should be checked
+ * @return TRUE if the event was filtered out, otherwise FALSE.
+ */
+ public boolean filterEvent(EventObject msg);
+}
Index: src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java

--- src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java (working copy)
@@ -47,6 +47,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceYahooImpl yahooProvider = null;
@@ -303,6 +308,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -593,4 +613,35 @@

        return linkBuffer.toString();
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java

--- src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java (working copy)
@@ -69,6 +69,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceJabberImpl jabberProvider = null;
@@ -323,6 +328,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -588,4 +608,35 @@
            return true;
        }
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java

--- src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java (working copy)
@@ -289,4 +289,14 @@
        else
           return false;
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java

--- src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java (working copy)
@@ -453,4 +453,13 @@
       }
    }

+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/mock/MockBasicInstantMessaging.java

--- src/net/java/sip/communicator/impl/protocol/mock/MockBasicInstantMessaging.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/mock/MockBasicInstantMessaging.java (working copy)
@@ -187,4 +187,14 @@
            listener.messageReceived(msgReceivedEvt);
        }
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
\ No newline at end of file
Index: src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java

--- src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java (working copy)
@@ -10,6 +10,7 @@
import java.net.*;
import java.text.*;
import java.util.*;
+
import javax.sip.*;
import javax.sip.address.*;
import javax.sip.header.*;
@@ -38,6 +39,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceSipImpl sipProvider = null;
@@ -588,6 +594,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+
        Iterator listeners = null;
        synchronized (this.messageListeners)
        {
@@ -1048,5 +1069,36 @@
            }
        }
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}

Index: src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java

--- src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java (working copy)
@@ -46,6 +46,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The icq provider that created us.
     */
    private ProtocolProviderServiceIcqImpl icqProvider = null;
@@ -467,6 +472,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -769,4 +789,35 @@

        }
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java

--- src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java (working copy)
@@ -36,6 +36,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceMsnImpl msnProvider = null;
@@ -259,6 +264,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -382,5 +402,36 @@
                                                      MsnContact contact)
        {
        }
- }
+ }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java

--- src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java (revision 3469)
+++ src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java (working copy)
@@ -389,5 +389,15 @@
    {
        return MessageSSHImpl.contentType.equals(contentType);
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }

}
Index: src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicInstantMessagingGibberishImpl.java

--- src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicInstantMessagingGibberishImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicInstantMessagingGibberishImpl.java (working copy)
@@ -282,4 +282,14 @@
        else
           return false;
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
Index: src/net/java/sip/communicator/impl/gui/utils/ContactListDraggable.java

--- src/net/java/sip/communicator/impl/gui/utils/ContactListDraggable.java (revision 3432)
+++ src/net/java/sip/communicator/impl/gui/utils/ContactListDraggable.java (working copy)
@@ -66,11 +66,15 @@
     * A set of 2 cursors indicating if the dragged element is currently
     * located over the contactlist or not.
     */
- private static Hashtable cursors = new Hashtable(2);
+ private static Hashtable<String, Cursor> cursors = new Hashtable<String, Cursor>(3);
+ public static Cursor getCursor(String type)
+ {
+ return cursors.get(type);
+ }
    static {
        try
        {
- cursors.put("valid", Cursor.getSystemCustomCursor("MoveDrop.32x32"));
+ cursors.put("move", Cursor.getSystemCustomCursor("MoveDrop.32x32"));
        }
        catch (Exception ex)
        {
@@ -88,6 +92,16 @@
                "will use Cursor.WAIT_CURSOR instead", ex);
            cursors.put("invalid", Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        }
+ try
+ {
+ cursors.put("copy", Cursor.getSystemCustomCursor("CopyDrop.32x32"));
+ }
+ catch (Exception ex)
+ {
+ logger.debug("Cursor \"CopyDrop.32x32\" isn't available " +
+ "will use Cursor.CROSSHAIR_CURSOR instead", ex);
+ cursors.put("copy", Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
+ }
    }

    /**
@@ -237,11 +251,11 @@
        Point p = SwingUtilities.convertPoint(this, location, contactList);
        if (contactList.contains(p))
        {
- setCursor((Cursor) cursors.get("valid"));
+ setCursor(getCursor("move"));
        }
        else
        {
- setCursor((Cursor) cursors.get("invalid"));
+ setCursor(getCursor("invalid"));
        }
    }
}
Index: src/net/java/sip/communicator/impl/gui/main/chat/ChatContactListPanel.java

--- src/net/java/sip/communicator/impl/gui/main/chat/ChatContactListPanel.java (revision 3432)
+++ src/net/java/sip/communicator/impl/gui/main/chat/ChatContactListPanel.java (working copy)
@@ -36,7 +36,7 @@

    private JScrollPane contactsScrollPane = new JScrollPane();

- private JPanel mainPanel = new JPanel(new BorderLayout());
+ private JPanel mainPanel;

    private SIPCommButton addToChatButton = new SIPCommButton(
        ImageLoader.getImage(ImageLoader.ADD_TO_CHAT_BUTTON),
@@ -57,6 +57,12 @@
        super(new BorderLayout(5, 5));

        this.chatPanel = chat;
+
+ if(chatPanel instanceof ConferenceChatPanel)
+ {
+ mainPanel = new ConferenceChatContactsPanel((ConferenceChatPanel)chatPanel, new BorderLayout());
+ }
+ else mainPanel = new JPanel(new BorderLayout());

        this.contactsPanel.setLayout(
            new BoxLayout(contactsPanel, BoxLayout.Y_AXIS));
Index: src/net/java/sip/communicator/impl/gui/main/chat/ChatWindow.java

--- src/net/java/sip/communicator/impl/gui/main/chat/ChatWindow.java (revision 3462)
+++ src/net/java/sip/communicator/impl/gui/main/chat/ChatWindow.java (working copy)
@@ -16,6 +16,9 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.customcontrols.*;
import net.java.sip.communicator.impl.gui.customcontrols.events.*;
+import net.java.sip.communicator.impl.gui.dnd.DnDCoordinator;
+import net.java.sip.communicator.impl.gui.dnd.DnDFrame;
+import net.java.sip.communicator.impl.gui.dnd.DnDTarget;
import net.java.sip.communicator.impl.gui.main.*;
import net.java.sip.communicator.impl.gui.main.MainFrame.*;
import net.java.sip.communicator.impl.gui.main.chat.menus.*;
@@ -41,7 +44,8 @@
public class ChatWindow
    extends SIPCommFrame
    implements ExportedWindow,
- PluginComponentListener
+ PluginComponentListener,
+ DnDFrame, WindowFocusListener
{
    private Logger logger = Logger.getLogger(ChatWindow.class.getName());

@@ -60,6 +64,11 @@
    public ChatWindow(MainFrame mainFrame)
    {
        this.mainFrame = mainFrame;
+
+ dndCoordinator = mainFrame.getDnDCoordinator();
+ dndCoordinator.registerFrame(this);
+ enableEvents(AWTEvent.WINDOW_FOCUS_EVENT_MASK);
+ addWindowFocusListener(this);

        this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

@@ -721,4 +730,87 @@
            g.drawImage(backgroundImage, 0, 0, null);
        }
    }
+
+ private DnDCoordinator dndCoordinator;
+
+ /**
+ * Vector of objects of type <tt>DnDTarget</tt> that are interested in dnd events.
+ */
+ private Vector<DnDTarget> dndTargets = new Vector<DnDTarget>();
+ /**
+ * Registers a DnDTarget.
+ * If you don't register, you won't get dnd events.
+ */
+ public void registerDnDTarget(DnDTarget target)
+ {
+ //System.out.println("Registered a DnDTaget: "+ target.getClass().toString());
+ dndTargets.addElement(target);
+ }
+ /**
+ * Unregisters the DnDTarget.
+ * Dnd events will no longer be sent.
+ */
+ public void unregisterDnDTarget(DnDTarget target)
+ {
+ //System.out.println("Unregistered a DnDTaget: "+ target.getClass().toString());
+ dndTargets.removeElement(target);
+ }
+
+ /**
+ * If the specified point is over a target which can accept the dragged
+ * object, then return that target, otherwise return null. The source and
+ * obj parameters are to help the target decide whether it wants the
+ * offered object.
+ *
+ * We go through the list of registered DnDTargets and see if any of them
+ * are both under the specified point and want to accept the dragged object.
+ */
+ public DnDTarget findTarget(Point absPos, Object source, Object obj)
+ {
+ for (DnDTarget target: dndTargets)
+ if (target.inside(absPos) && target.dndAccept(source, obj))
+ return target;
+ return null;
+ }
+
+ /**
+ * Return true if the specified point is over this chat frame.
+ * "Over this frame" means title bars, scroll bars, etc.
+ */
+ public boolean inside(Point absPos)
+ {
+ return getBounds().contains(absPos);
+ }
+
+ /**
+ * Overwrites the setVisible method in order to notify the
+ * DnD coorinator that this chat window is on top of other all frames.
+ */
+ public void setVisible(boolean isVisible)
+ {
+ super.setVisible(isVisible);
+ if (isVisible) dndCoordinator.setTopFrame(this);
+ }
+
+ /**
+ * Overwrites the dispose method in order to unregister
+ * this chat window from DnD coordinator.
+ * DnD events are no longer sent to this chat window.
+ */
+ public void dispose()
+ {
+ dndCoordinator.unregisterFrame(this);
+ super.dispose();
+ }
+
+ public void windowGainedFocus(WindowEvent arg0)
+ {
+ //System.out.println("focusGained: "+ this.getClass().toString());
+ dndCoordinator.setTopFrame(this);
+ }
+
+ public void windowLostFocus(WindowEvent arg0)
+ {
+ //System.out.println("focusLost: "+ this.getClass().toString());
+ }
}
Index: src/net/java/sip/communicator/impl/gui/main/chat/conference/MultiUserChatManager.java

--- src/net/java/sip/communicator/impl/gui/main/chat/conference/MultiUserChatManager.java (revision 3432)
+++ src/net/java/sip/communicator/impl/gui/main/chat/conference/MultiUserChatManager.java (working copy)
@@ -67,7 +67,10 @@
    }

    public void invitationRejected(ChatRoomInvitationRejectedEvent evt)
- {
+ {
+ chatWindowManager.getMultiChat(evt.getChatRoom()).
+ processMessage(evt.getInvitee(), evt.getTimestamp(),
+ Constants.ACTION_MESSAGE, "Rejected invitation: "+evt.getReason(), ChatConversationPanel.TEXT_CONTENT_TYPE);
    }

    /**
Index: src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatContactsPanel.java

--- src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatContactsPanel.java (revision 0)
+++ src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatContactsPanel.java (revision 0)
@@ -0,0 +1,174 @@
+package net.java.sip.communicator.impl.gui.main.chat.conference;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.LayoutManager;
+import java.awt.Point;
+import java.util.Date;
+
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import net.java.sip.communicator.impl.gui.dnd.DnDTarget;
+import net.java.sip.communicator.impl.gui.main.chat.ChatConversationPanel;
+import net.java.sip.communicator.impl.gui.main.contactlist.ContactList;
+import net.java.sip.communicator.impl.gui.utils.Constants;
+import net.java.sip.communicator.impl.gui.utils.ContactListDraggable;
+import net.java.sip.communicator.service.protocol.ChatRoom;
+import net.java.sip.communicator.service.protocol.Contact;
+
+/**
+ * The <tt>ConferenceChatContactsPanel</tt> is the panel in the
+ * <tt>ChatContactListPanel</tt> that contains a list of
+ * <tt>ChatContactPanel</tt>s if a chat is a conference.
+ * The <tt>ConferenceChatContactsPanel</tt> enables to add participants
+ * to the conference with drag-and-drop.
+ *
+ * @author Keio Kraaner
+ */
+@SuppressWarnings("serial")
+public class ConferenceChatContactsPanel extends JPanel
+ implements DnDTarget
+{
+ private ConferenceChatPanel conferenceChatPanel;
+
+ public ConferenceChatContactsPanel(ConferenceChatPanel conferenceChatPanel, LayoutManager layout)
+ {
+ super(layout);
+ this.conferenceChatPanel = conferenceChatPanel;
+ conferenceChatPanel.getChatWindow().registerDnDTarget(this);
+ }
+
+ public boolean inside(Point absPos)
+ {
+ return contains(relPos(absPos));
+ }
+
+ public Point relPos(Point absPos)
+ {
+ Point p = (Point) absPos.clone();
+ SwingUtilities.convertPointFromScreen(p, this);
+ return p;
+ }
+
+ public boolean dndAccept(Object src, Object obj)
+ {
+ if (src instanceof ContactList && obj instanceof ContactListDraggable)
+ return true;
+ return false;
+ }
+
+ public void dndDrop(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ Object o = conferenceChatPanel.getChatIdentifier();
+ if (!(o instanceof ChatRoomWrapper)) return;
+ ChatRoomWrapper chatRoomWrapper = (ChatRoomWrapper) o;
+ ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
+ if (chatRoom == null) return;
+ Contact contact;
+ if (draggable.getContact() != null)
+ contact = draggable.getContact();
+ else contact = draggable.getMetaContact().getDefaultContact();
+ chatRoom.invite(contact.getAddress(), "");
+ conferenceChatPanel.processMessage(contact.getDisplayName(),
+ new Date(System.currentTimeMillis()),
+ Constants.ACTION_MESSAGE,
+ "Sending invitation to join",
+ ChatConversationPanel.TEXT_CONTENT_TYPE);
+
+ draggable = null;
+ setCursor(Cursor.getDefaultCursor());
+ repaint();
+ }
+
+ public void dndCancel(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ draggable = null;
+ setCursor(Cursor.getDefaultCursor());
+ repaint();
+ }
+
+ private ContactListDraggable draggable = null;
+ private Point draggablePos;
+ public void dndEnter(Point relPos, Object src, Object obj)
+ {
+ if (!(obj instanceof ContactListDraggable)) return;
+
+ draggable = (ContactListDraggable) obj;
+ draggablePos = relPos;
+ repaint();
+ }
+
+ public void dndExit(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ draggable = null;
+ setCursor(Cursor.getDefaultCursor());
+ repaint();
+ }
+
+ public void dndMove(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ if (relPos.equals(draggablePos)) return;
+
+ draggablePos = relPos;
+ repaint();
+ }
+
+ public void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+ drawDraggable(g);
+ }
+
+ private void drawDraggable(Graphics g)
+ {
+ if (draggable == null || draggablePos == null) return;
+
+ // make the dragged element "transparent" with a coef. of 0.8
+ // and paint it
+ Graphics2D g2 = (Graphics2D) g;
+ g2.setComposite(AlphaComposite.
+ getInstance(AlphaComposite.SRC_OVER, 0.8f));
+
+ g2.drawImage(
+ draggable.getImage(),
+ (int) (draggablePos.getX() - draggable.getImage().getWidth(null) / 2),
+ (int) (draggablePos.getY() - draggable.getImage().getHeight(null) / 2),
+ null);
+
+ g2.setColor(Color.GRAY);
+ g2.drawRect((int) (draggablePos.getX() - draggable.getImage().getWidth(null) / 2),
+ (int) (draggablePos.getY() - draggable.getImage().getHeight(null) / 2),
+ draggable.getImage().getWidth(null),
+ draggable.getImage().getHeight(null));
+ if (contains(draggablePos))
+ {
+ setCursor(ContactListDraggable.getCursor("copy"));
+ }
+ else
+ {
+ setCursor(ContactListDraggable.getCursor("invalid"));
+ }
+ }
+
+ /**
+ * Overwrites the dispose method in order to unregister
+ * this DnD target from the chat window.
+ * DnD events are no longer sent to this target.
+ */
+ public void dispose()
+ {
+ conferenceChatPanel.getChatWindow().unregisterDnDTarget(this);
+ }
+}
Index: src/net/java/sip/communicator/impl/gui/main/MainFrame.java

--- src/net/java/sip/communicator/impl/gui/main/MainFrame.java (revision 3432)
+++ src/net/java/sip/communicator/impl/gui/main/MainFrame.java (working copy)
@@ -17,6 +17,7 @@

import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.customcontrols.*;
+import net.java.sip.communicator.impl.gui.dnd.DnDCoordinator;
import net.java.sip.communicator.impl.gui.i18n.*;
import net.java.sip.communicator.impl.gui.main.call.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
@@ -85,6 +86,9 @@
        providerContactHandlers
            = new Hashtable<ProtocolProviderService, >();

+ private DnDCoordinator dndCoordinator = new DnDCoordinator();
+ public DnDCoordinator getDnDCoordinator() { return dndCoordinator; }
+
    /**
     * Creates an instance of <tt>MainFrame</tt>.
     */
Index: src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java

--- src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java (revision 3432)
+++ src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java (working copy)
@@ -108,6 +108,8 @@
                {
                    draggedElement.setVisible(false);
                    draggedElement = null;
+
+ ContactList.this.mainFrame.getDnDCoordinator().cancelDnD();
                }
            }
        });
@@ -121,6 +123,8 @@
                    {
                        draggedElement.setVisible(false);
                        draggedElement = null;
+
+ ContactList.this.mainFrame.getDnDCoordinator().cancelDnD();
                    }
                }
            }
@@ -788,6 +792,10 @@

            p = SwingUtilities.convertPoint(e.getComponent(), p, draggedElement);
            draggedElement.setLocation(p);
+
+ Point eventAbsPos = (Point) e.getPoint().clone();
+ SwingUtilities.convertPointToScreen(eventAbsPos, e.getComponent());
+ mainFrame.getDnDCoordinator().startDnD(eventAbsPos, this, draggedElement);
        }
    }

@@ -806,6 +814,10 @@
            p = SwingUtilities.convertPoint(e.getComponent(), p, draggedElement);
            draggedElement.setLocation(p);
            draggedElement.repaint();
+
+ Point eventAbsPos = (Point) e.getPoint().clone();
+ SwingUtilities.convertPointToScreen(eventAbsPos, e.getComponent());
+ mainFrame.getDnDCoordinator().updateDnD(eventAbsPos);
        }
    }

@@ -823,6 +835,8 @@
        ContactListModel listModel = (ContactListModel) this.getModel();

        Object dest = listModel.getElementAt(selectedIndex);
+ if (!contains(e.getPoint())) dest = null;
+
        if (draggedElement != null)
        {
            if (dest instanceof MetaContact)
@@ -895,6 +909,8 @@

            draggedElement.setVisible(false);
            draggedElement = null;
+
+ mainFrame.getDnDCoordinator().finishDnD();
        }
    }

Index: src/net/java/sip/communicator/impl/gui/dnd/DnDFrame.java

--- src/net/java/sip/communicator/impl/gui/dnd/DnDFrame.java (revision 0)
+++ src/net/java/sip/communicator/impl/gui/dnd/DnDFrame.java (revision 0)
@@ -0,0 +1,23 @@
+
+package net.java.sip.communicator.impl.gui.dnd;
+
+import java.awt.Point;
+
+/**
+ * Every Frame that wants to get DnD events has to implement this interface.
+ */
+public interface DnDFrame
+{
+ /**
+ * Return true if the specified point is over this frame.
+ * "Over this frame" means title bars, scroll bars, etc.
+ */
+ public boolean inside(Point absPos);
+
+ /**
+ * If the specified point is over a target which can accept the dragged
+ * object, then return that target, otherwise return null. The source and
+ * obj parameters are to help the target decide whether it wants the object.
+ */
+ public DnDTarget findTarget(Point absPos, Object source, Object obj);
+}
Index: src/net/java/sip/communicator/impl/gui/dnd/DnDCoordinator.java

--- src/net/java/sip/communicator/impl/gui/dnd/DnDCoordinator.java (revision 0)
+++ src/net/java/sip/communicator/impl/gui/dnd/DnDCoordinator.java (revision 0)
@@ -0,0 +1,146 @@
+
+package net.java.sip.communicator.impl.gui.dnd;
+
+/**
+ * <p>This class coordinates the mouse events involved in a drag and drop
+ * transaction. The source where dragging starts should call startDnD, updateDnD, and finishDnD
+ * or cancelDnD. This class will then call appropriate target methods.
+ * The reason this class is necessary is because when
+ * the mouse starts dragging in one Component and then moves over a different
+ * Component, the first Component is the one that gets all the mouse messages.
+ * That first Component is the source, and the second is the target. The
+ * job of this class is to shuttle the mouse events from the source to the target,
+ * translating the mouse events on the way.
+ *
+ * <p><b>Note</b>: this class assumes only one DnD operation can happen at a time
+ * (after all, there's only one mouse)
+ */
+
+import java.awt.Point;
+import java.util.Vector;
+
+public class DnDCoordinator
+{
+ private Vector<DnDFrame> frames = new Vector<DnDFrame>();
+ private Object src;
+ private Object draggable;
+ private DnDTarget currentTarget; // target the mouse is currently over
+ private Point currentPos; // position of mouse relative to target
+
+ /**
+ * Register a Frame as DnD-capable. If you don't register the Frame, it
+ * won't be notified of DnD events.
+ */
+ public void registerFrame(DnDFrame frame)
+ {
+ if (!frames.contains(frame))
+ {
+ //System.out.println("Registered a DnDFrame: "+ frame.getClass().toString());
+ frames.addElement(frame);
+ }
+ /** Unregister a Frame as DnD-capable. Dnd events will no longer be sent. */
+ public void unregisterFrame(DnDFrame frame)
+ {
+ if (frames.contains(frame))
+ {
+ //System.out.println("Unregistered a DnDFrame: "+ frame.getClass().toString());
+ frames.removeElement(frame);
+ }
+ /** Call this when a Frame is moved to the top (eg when it gets the focus) */
+ public void setTopFrame(DnDFrame frame)
+ {
+ if (!frames.contains(frame)) return;
+ frames.removeElement(frame);
+ frames.insertElementAt(frame, 0);
+ }
+
+ /**
+ * Call this to start a DnD operation.
+ *
+ * @param absPos The position of the mouse when the DnD operation started
+ * @param src Where the object is being dragged from. May be null.
+ * @param obj The thing that's being dragged. May be null.
+ */
+ public void startDnD(Point absPos, Object src, Object draggable)
+ {
+ this.src = src;
+ this.draggable = draggable;
+ findTarget(absPos);
+ if (currentTarget != null)
+ currentTarget.dndEnter(currentPos, src, draggable);
+ }
+ /**
+ * Call this when the mouse moves during a DnD operation.
+ *
+ * @param newAbsPos the new position of the mouse
+ */
+ public void updateDnD(Point newAbsPos)
+ {
+ DnDTarget oldTarget = currentTarget;
+ findTarget(newAbsPos);
+ // if we're still over the same target, it's a move()
+ if (currentTarget == oldTarget)
+ {
+ if (currentTarget != null)
+ currentTarget.dndMove(currentPos, src, draggable);
+ }
+ // otherwise we exit() old target and enter() new one
+ else
+ {
+ if (oldTarget != null)
+ oldTarget.dndExit(currentPos, src, draggable);
+ if (currentTarget != null)
+ currentTarget.dndEnter(currentPos, src, draggable);
+ }
+ /**
+ * Call this when the DnD operation is finished successfully (eg when the
+ * mouse button is released).
+ */
+ public void finishDnD()
+ {
+ if (currentTarget != null)
+ currentTarget.dndDrop(currentPos, src, draggable);
+ src = null;
+ currentTarget = null;
+ }
+ /**
+ * Call this if the user aborts the DnD operation without dropping the object
+ * (eg by hitting the ESC key)
+ */
+ public void cancelDnD()
+ {
+ if (currentTarget != null)
+ currentTarget.dndCancel(currentPos, src, draggable);
+ src = null;
+ currentTarget = null;
+ }
+
+ private void findTarget(Point absPos)
+ {
+ // check for a target in every frame we know about
+ for (DnDFrame frame: frames)
+ {
+ if (frame.inside(absPos))
+ {
+ DnDTarget target = frame.findTarget(absPos, src, draggable);
+ if (target != null)
+ {
+ // hooray! we found a target
+ currentTarget = target;
+ currentPos = target.relPos(absPos);
+ return;
+ }
+ // we're over a frame, but not over a target
+ currentTarget = null;
+ currentPos = null;
+ return;
+ }
+ // we're not over a frame that supports DnD
+ currentTarget = null;
+ currentPos = null;
+ }
+}
Index: src/net/java/sip/communicator/impl/gui/dnd/DnDTarget.java

--- src/net/java/sip/communicator/impl/gui/dnd/DnDTarget.java (revision 0)
+++ src/net/java/sip/communicator/impl/gui/dnd/DnDTarget.java (revision 0)
@@ -0,0 +1,52 @@
+
+package net.java.sip.communicator.impl.gui.dnd;
+
+import java.awt.Point;
+
+/**
+ * Implement this interface if you want to have things dropped on you.
+ */
+public interface DnDTarget
+{
+ /**
+ * Convert absolute coordinates into coordinates relative to this target.
+ */
+ public Point relPos(Point absPos);
+
+ /**
+ * Return true if this target will accept the object being dragged.
+ */
+ public boolean dndAccept(Object src, Object obj);
+
+ /**
+ * Something has just been dragged onto this target.
+ * This method will not be called if dndAccept returns false.
+ */
+ public void dndEnter(Point relPos, Object src, Object obj);
+
+ /**
+ * The dragged object has been moved (but it is still over this target).
+ */
+ public void dndMove(Point relPos, Object src, Object obj);
+
+ /**
+ * The mouse has left the building. The dragged object is no longer over
+ * this target.
+ */
+ public void dndExit(Point relPos, Object src, Object obj);
+
+ /**
+ * The dragged bject has been dropped.
+ */
+ public void dndDrop(Point relPos, Object src, Object obj);
+
+ /**
+ * The source has decided to cancel the DnD operation.
+ */
+ public void dndCancel(Point relPos, Object src, Object obj);
+
+ /**
+ * Return true if the specified point is over this target.
+ */
+ public boolean inside(Point absPos);
+}

--------------------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net


#5

Hey Keio,

I've just reviewed your patch. I like the idea of having message filters
and I actually remember seeing a similar request about a week ago, so
your contribution is very well timed :).

Now, here are my comments on your "SIP Communicator patch.txt":

(in no particular order)

1. I noticed that in addition to adding two methods to
OperationSetBasicInstantMessaging, you have also implemented them in
some of the existing protocol providers. This is cool, however, I also
noticed that you chose not to implement them in Zeroconf, Mock, Rss,
SSH, and Gibberish. I guess the reason you did this was because, for
most of these protocols there was no point of supporting such a feature
(e.g., you would never filter an incoming RSS message because it can't
come from anyone else but the server).

That's actually the very reason why operation sets exist in SIP
Communicator. They allow a provider to only implement functionalities
that make sense in the context of its protocol. Therefore, I believe it
would be better if the filtering functionality came in an operation set
of its own, say, OperationSetInstantMessageFiltering.

Once you have created the interface you could keep your existing
implementations where they are, and only add the new interface in the
implements clause of the corresponding IM op set. You would also have to
change protocol providers that would support this feature and declare
the new operation set in their supportedOperationSets hashtables.

This would also allow plugins that use the filters to distinguish
protocol implementations that actually support them.

2. Our coding convention stipulates that opening braces should be on a
line of their own. (http://sip-communicator.org/codeconvention) I've
noticed that you generally respect this but there were several omissions.

3. I noticed several tab characters here in there and in SC we only use
spaces :slight_smile:

4. Most of the time, when you dispatch an incoming message to a filter
you do this

+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+ }

It would probably a good idea to keep iterating through the filters even
after a first filter has returned true in case some other filter would
also like to use the command in the message. Besides, you may also want
to add a try catch clause around the filterEvent() method so that a
filter that throws an exception would not prevent other filters and
message listeners from receiving a message.

... and that's all I guess. Other than the above your patch is quite
neat, so thanks for the effort!

Oh, almost forgot, I haven't reviewed the DND section yet but I just had
a chat with Yana and she promised she'll have a look at it tomorrow.

Cheers
Emil

P.S. Sorry for taking so long to reply and thanks for your patience!
Things should go faster now.

Keio Kraaner написа:

···

Hi,
updated the patch file:
* fixed a bug - DnD did not work after the chat window was closed and
reopened;
* resolved conflicts with some newer changes (those changes did not allow to
applyto the patch correctly any more).

Best,
Keio

----- Original Message -----
From: "Keio Kraaner" <keio@ut.ee>
To: <dev@sip-communicator.dev.java.net>
Sent: Tuesday, February 26, 2008 12:23 PM
Subject: Re: [sip-comm-dev] Drag and drop

Forgot to attach the patch.

Keio Kraaner wrote:

Hi,

The way how contacts are added to a conference chat room is a bit
inconvenient at the moment (push to "Invite others to the chat" button
opens a dialog, where one has to specify an account ID of a contact
and a reason). I think that adding contacts by dragging them to the
chat's contacts panel is a bit more intuitive (and maybe specify a
reason in the dialog).
Attached patch file contains the implementation that enables this drag
and drop functionality (at the moment the reason is not asked, an
invitation with empty reason is sent).
Also the patch contains the implementation of a generic message filter
that I sent first time in December.
Maybe someone could take a look at these two things and give some
feedback (if something should be changed/improved I am eager to do this).

Best,
Keio Kraaner

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net

--------------------------------------------------------------------------------

Index:
src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java

---
src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java
(revision 3320)
+++
src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java
(working copy)
@@ -102,4 +102,20 @@
     * <tt>false</tt> otherwise.
     */
    public boolean isContentTypeSupported(String contentType);
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter);
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any
more
+ * if a event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter);
}
Index:
src/net/java/sip/communicator/service/protocol/event/EventFilter.java

--- src/net/java/sip/communicator/service/protocol/event/EventFilter.java
(revision 0)
+++ src/net/java/sip/communicator/service/protocol/event/EventFilter.java
(revision 0)
@@ -0,0 +1,28 @@
+/*
+/*
+ * 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.service.protocol.event;
+
+import java.util.*;
+
+/**
+ * An event filter that decides if an event should be filtered out or
not.
+ * For instance, maybe some type of received messages should not be
+ * shown in the chat windows.
+ *
+ * @author Keio Kraaner
+ */
+public interface EventFilter
+{
+ /**
+ * Checks if an event should be filtered out or processed.
+ *
+ * @param msg The event that should be checked
+ * @return TRUE if the event was filtered out, otherwise FALSE.
+ */
+ public boolean filterEvent(EventObject msg);
+}
Index:
src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java

---
src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java
(revision 3320)
+++
src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java
(working copy)
@@ -47,6 +47,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceYahooImpl yahooProvider = null;
@@ -303,6 +308,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new
ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -593,4 +613,35 @@

        return linkBuffer.toString();
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any
more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index:
src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java

---
src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java
(revision 3320)
+++
src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java
(working copy)
@@ -69,6 +69,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceJabberImpl jabberProvider = null;
@@ -323,6 +328,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new
ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -588,4 +608,35 @@
            return true;
        }
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any
more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index:
src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java

---
src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java
(revision 3320)
+++
src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java
(working copy)
@@ -289,4 +289,14 @@
        else
           return false;
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
Index:
src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java

---
src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java
(revision 3320)
+++
src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java
(working copy)
@@ -453,4 +453,13 @@
       }
    }

+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
Index:
src/net/java/sip/communicator/impl/protocol/mock/MockBasicInstantMessaging.java

---
src/net/java/sip/communicator/impl/protocol/mock/MockBasicInstantMessaging.java
(revision 3320)
+++
src/net/java/sip/communicator/impl/protocol/mock/MockBasicInstantMessaging.java
(working copy)
@@ -187,4 +187,14 @@
            listener.messageReceived(msgReceivedEvt);
        }
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
\ No newline at end of file
Index:
src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java

---
src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java
(revision 3320)
+++
src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java
(working copy)
@@ -10,6 +10,7 @@
import java.net.*;
import java.text.*;
import java.util.*;
+
import javax.sip.*;
import javax.sip.address.*;
import javax.sip.header.*;
@@ -38,6 +39,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceSipImpl sipProvider = null;
@@ -588,6 +594,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new
ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+ }
+
        Iterator listeners = null;
        synchronized (this.messageListeners)
        {
@@ -1048,5 +1069,36 @@
            }
        }
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any
more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}

Index:
src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java

---
src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java
(revision 3320)
+++
src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java
(working copy)
@@ -46,6 +46,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The icq provider that created us.
     */
    private ProtocolProviderServiceIcqImpl icqProvider = null;
@@ -467,6 +472,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new
ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -769,4 +789,35 @@

        }
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any
more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index:
src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java

---
src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java
(revision 3320)
+++
src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java
(working copy)
@@ -36,6 +36,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceMsnImpl msnProvider = null;
@@ -259,6 +264,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new
ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -382,5 +402,36 @@
                                                      MsnContact contact)
        {
        }
- }
+ }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any
more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index:
src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java

---
src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java
(revision 3469)
+++
src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java
(working copy)
@@ -389,5 +389,15 @@
    {
        return MessageSSHImpl.contentType.equals(contentType);
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }

}
Index:
src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicInstantMessagingGibberishImpl.java

---
src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicInstantMessagingGibberishImpl.java
(revision 3320)
+++
src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicInstantMessagingGibberishImpl.java
(working copy)
@@ -282,4 +282,14 @@
        else
           return false;
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
Index:
src/net/java/sip/communicator/impl/gui/utils/ContactListDraggable.java

--- src/net/java/sip/communicator/impl/gui/utils/ContactListDraggable.java
(revision 3432)
+++ src/net/java/sip/communicator/impl/gui/utils/ContactListDraggable.java
(working copy)
@@ -66,11 +66,15 @@
     * A set of 2 cursors indicating if the dragged element is currently
     * located over the contactlist or not.
     */
- private static Hashtable cursors = new Hashtable(2);
+ private static Hashtable<String, Cursor> cursors = new
Hashtable<String, Cursor>(3);
+ public static Cursor getCursor(String type)
+ {
+ return cursors.get(type);
+ }
    static {
        try
        {
- cursors.put("valid",
Cursor.getSystemCustomCursor("MoveDrop.32x32"));
+ cursors.put("move",
Cursor.getSystemCustomCursor("MoveDrop.32x32"));
        }
        catch (Exception ex)
        {
@@ -88,6 +92,16 @@
                "will use Cursor.WAIT_CURSOR instead", ex);
            cursors.put("invalid",
Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        }
+ try
+ {
+ cursors.put("copy",
Cursor.getSystemCustomCursor("CopyDrop.32x32"));
+ }
+ catch (Exception ex)
+ {
+ logger.debug("Cursor \"CopyDrop.32x32\" isn't available " +
+ "will use Cursor.CROSSHAIR_CURSOR instead", ex);
+ cursors.put("copy",
Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
+ }
    }

    /**
@@ -237,11 +251,11 @@
        Point p = SwingUtilities.convertPoint(this, location,
contactList);
        if (contactList.contains(p))
        {
- setCursor((Cursor) cursors.get("valid"));
+ setCursor(getCursor("move"));
        }
        else
        {
- setCursor((Cursor) cursors.get("invalid"));
+ setCursor(getCursor("invalid"));
        }
    }
}
Index:
src/net/java/sip/communicator/impl/gui/main/chat/ChatContactListPanel.java

---
src/net/java/sip/communicator/impl/gui/main/chat/ChatContactListPanel.java
(revision 3432)
+++
src/net/java/sip/communicator/impl/gui/main/chat/ChatContactListPanel.java
(working copy)
@@ -36,7 +36,7 @@

    private JScrollPane contactsScrollPane = new JScrollPane();

- private JPanel mainPanel = new JPanel(new BorderLayout());
+ private JPanel mainPanel;

    private SIPCommButton addToChatButton = new SIPCommButton(
        ImageLoader.getImage(ImageLoader.ADD_TO_CHAT_BUTTON),
@@ -57,6 +57,12 @@
        super(new BorderLayout(5, 5));

        this.chatPanel = chat;
+
+ if(chatPanel instanceof ConferenceChatPanel)
+ {
+ mainPanel = new
ConferenceChatContactsPanel((ConferenceChatPanel)chatPanel, new
BorderLayout());
+ }
+ else mainPanel = new JPanel(new BorderLayout());

        this.contactsPanel.setLayout(
            new BoxLayout(contactsPanel, BoxLayout.Y_AXIS));
Index: src/net/java/sip/communicator/impl/gui/main/chat/ChatWindow.java

--- src/net/java/sip/communicator/impl/gui/main/chat/ChatWindow.java
(revision 3462)
+++ src/net/java/sip/communicator/impl/gui/main/chat/ChatWindow.java
(working copy)
@@ -16,6 +16,9 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.customcontrols.*;
import net.java.sip.communicator.impl.gui.customcontrols.events.*;
+import net.java.sip.communicator.impl.gui.dnd.DnDCoordinator;
+import net.java.sip.communicator.impl.gui.dnd.DnDFrame;
+import net.java.sip.communicator.impl.gui.dnd.DnDTarget;
import net.java.sip.communicator.impl.gui.main.*;
import net.java.sip.communicator.impl.gui.main.MainFrame.*;
import net.java.sip.communicator.impl.gui.main.chat.menus.*;
@@ -41,7 +44,8 @@
public class ChatWindow
    extends SIPCommFrame
    implements ExportedWindow,
- PluginComponentListener
+ PluginComponentListener,
+ DnDFrame, WindowFocusListener
{
    private Logger logger = Logger.getLogger(ChatWindow.class.getName());

@@ -60,6 +64,11 @@
    public ChatWindow(MainFrame mainFrame)
    {
        this.mainFrame = mainFrame;
+
+ dndCoordinator = mainFrame.getDnDCoordinator();
+ dndCoordinator.registerFrame(this);
+ enableEvents(AWTEvent.WINDOW_FOCUS_EVENT_MASK);
+ addWindowFocusListener(this);

        this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

@@ -721,4 +730,87 @@
            g.drawImage(backgroundImage, 0, 0, null);
        }
    }
+
+ private DnDCoordinator dndCoordinator;
+
+ /**
+ * Vector of objects of type <tt>DnDTarget</tt> that are interested in
dnd events.
+ */
+ private Vector<DnDTarget> dndTargets = new Vector<DnDTarget>();
+ /**
+ * Registers a DnDTarget.
+ * If you don't register, you won't get dnd events.
+ */
+ public void registerDnDTarget(DnDTarget target)
+ {
+ //System.out.println("Registered a DnDTaget: "+
target.getClass().toString());
+ dndTargets.addElement(target);
+ }
+ /**
+ * Unregisters the DnDTarget.
+ * Dnd events will no longer be sent.
+ */
+ public void unregisterDnDTarget(DnDTarget target)
+ {
+ //System.out.println("Unregistered a DnDTaget: "+
target.getClass().toString());
+ dndTargets.removeElement(target);
+ }
+
+ /**
+ * If the specified point is over a target which can accept the dragged
+ * object, then return that target, otherwise return null. The source
and
+ * obj parameters are to help the target decide whether it wants the
+ * offered object.
+ *
+ * We go through the list of registered DnDTargets and see if any of them
+ * are both under the specified point and want to accept the dragged
object.
+ */
+ public DnDTarget findTarget(Point absPos, Object source, Object obj)
+ {
+ for (DnDTarget target: dndTargets)
+ if (target.inside(absPos) && target.dndAccept(source, obj))
+ return target;
+ return null;
+ }
+
+ /**
+ * Return true if the specified point is over this chat frame.
+ * "Over this frame" means title bars, scroll bars, etc.
+ */
+ public boolean inside(Point absPos)
+ {
+ return getBounds().contains(absPos);
+ }
+
+ /**
+ * Overwrites the setVisible method in order to notify the
+ * DnD coorinator that this chat window is on top of other all frames.
+ */
+ public void setVisible(boolean isVisible)
+ {
+ super.setVisible(isVisible);
+ if (isVisible) dndCoordinator.setTopFrame(this);
+ }
+
+ /**
+ * Overwrites the dispose method in order to unregister
+ * this chat window from DnD coordinator.
+ * DnD events are no longer sent to this chat window.
+ */
+ public void dispose()
+ {
+ dndCoordinator.unregisterFrame(this);
+ super.dispose();
+ }
+
+ public void windowGainedFocus(WindowEvent arg0)
+ {
+ //System.out.println("focusGained: "+ this.getClass().toString());
+ dndCoordinator.setTopFrame(this);
+ }
+
+ public void windowLostFocus(WindowEvent arg0)
+ {
+ //System.out.println("focusLost: "+ this.getClass().toString());
+ }
}
Index:
src/net/java/sip/communicator/impl/gui/main/chat/conference/MultiUserChatManager.java

---
src/net/java/sip/communicator/impl/gui/main/chat/conference/MultiUserChatManager.java
(revision 3432)
+++
src/net/java/sip/communicator/impl/gui/main/chat/conference/MultiUserChatManager.java
(working copy)
@@ -67,7 +67,10 @@
    }

    public void invitationRejected(ChatRoomInvitationRejectedEvent evt)
- {
+ {
+ chatWindowManager.getMultiChat(evt.getChatRoom()).
+ processMessage(evt.getInvitee(), evt.getTimestamp(),
+ Constants.ACTION_MESSAGE, "Rejected invitation: "+evt.getReason(),
ChatConversationPanel.TEXT_CONTENT_TYPE);
    }

    /**
Index:
src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatContactsPanel.java

---
src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatContactsPanel.java
(revision 0)
+++
src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatContactsPanel.java
(revision 0)
@@ -0,0 +1,174 @@
+package net.java.sip.communicator.impl.gui.main.chat.conference;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.LayoutManager;
+import java.awt.Point;
+import java.util.Date;
+
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import net.java.sip.communicator.impl.gui.dnd.DnDTarget;
+import
net.java.sip.communicator.impl.gui.main.chat.ChatConversationPanel;
+import net.java.sip.communicator.impl.gui.main.contactlist.ContactList;
+import net.java.sip.communicator.impl.gui.utils.Constants;
+import net.java.sip.communicator.impl.gui.utils.ContactListDraggable;
+import net.java.sip.communicator.service.protocol.ChatRoom;
+import net.java.sip.communicator.service.protocol.Contact;
+
+/**
+ * The <tt>ConferenceChatContactsPanel</tt> is the panel in the
+ * <tt>ChatContactListPanel</tt> that contains a list of
+ * <tt>ChatContactPanel</tt>s if a chat is a conference.
+ * The <tt>ConferenceChatContactsPanel</tt> enables to add participants
+ * to the conference with drag-and-drop.
+ *
+ * @author Keio Kraaner
+ */
+@SuppressWarnings("serial")
+public class ConferenceChatContactsPanel extends JPanel
+ implements DnDTarget
+{
+ private ConferenceChatPanel conferenceChatPanel;
+
+ public ConferenceChatContactsPanel(ConferenceChatPanel
conferenceChatPanel, LayoutManager layout)
+ {
+ super(layout);
+ this.conferenceChatPanel = conferenceChatPanel;
+ conferenceChatPanel.getChatWindow().registerDnDTarget(this);
+ }
+
+ public boolean inside(Point absPos)
+ {
+ return contains(relPos(absPos));
+ }
+
+ public Point relPos(Point absPos)
+ {
+ Point p = (Point) absPos.clone();
+ SwingUtilities.convertPointFromScreen(p, this);
+ return p;
+ }
+
+ public boolean dndAccept(Object src, Object obj)
+ {
+ if (src instanceof ContactList && obj instanceof ContactListDraggable)
+ return true;
+ return false;
+ }
+
+ public void dndDrop(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ Object o = conferenceChatPanel.getChatIdentifier();
+ if (!(o instanceof ChatRoomWrapper)) return;
+ ChatRoomWrapper chatRoomWrapper = (ChatRoomWrapper) o;
+ ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
+ if (chatRoom == null) return;
+ Contact contact;
+ if (draggable.getContact() != null)
+ contact = draggable.getContact();
+ else contact = draggable.getMetaContact().getDefaultContact();
+ chatRoom.invite(contact.getAddress(), "");
+ conferenceChatPanel.processMessage(contact.getDisplayName(),
+ new Date(System.currentTimeMillis()),
+ Constants.ACTION_MESSAGE,
+ "Sending invitation to join",
+ ChatConversationPanel.TEXT_CONTENT_TYPE);
+
+ draggable = null;
+ setCursor(Cursor.getDefaultCursor());
+ repaint();
+ }
+
+ public void dndCancel(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ draggable = null;
+ setCursor(Cursor.getDefaultCursor());
+ repaint();
+ }
+
+ private ContactListDraggable draggable = null;
+ private Point draggablePos;
+ public void dndEnter(Point relPos, Object src, Object obj)
+ {
+ if (!(obj instanceof ContactListDraggable)) return;
+
+ draggable = (ContactListDraggable) obj;
+ draggablePos = relPos;
+ repaint();
+ }
+
+ public void dndExit(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ draggable = null;
+ setCursor(Cursor.getDefaultCursor());
+ repaint();
+ }
+
+ public void dndMove(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ if (relPos.equals(draggablePos)) return;
+
+ draggablePos = relPos;
+ repaint();
+ }
+
+ public void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+ drawDraggable(g);
+ }
+
+ private void drawDraggable(Graphics g)
+ {
+ if (draggable == null || draggablePos == null) return;
+
+ // make the dragged element "transparent" with a coef. of 0.8
+ // and paint it
+ Graphics2D g2 = (Graphics2D) g;
+ g2.setComposite(AlphaComposite.
+ getInstance(AlphaComposite.SRC_OVER, 0.8f));
+
+ g2.drawImage(
+ draggable.getImage(),
+ (int) (draggablePos.getX() -
draggable.getImage().getWidth(null) / 2),
+ (int) (draggablePos.getY() -
draggable.getImage().getHeight(null) / 2),
+ null);
+
+ g2.setColor(Color.GRAY);
+ g2.drawRect((int) (draggablePos.getX() -
draggable.getImage().getWidth(null) / 2),
+ (int) (draggablePos.getY() -
draggable.getImage().getHeight(null) / 2),
+ draggable.getImage().getWidth(null),
+ draggable.getImage().getHeight(null));
+ if (contains(draggablePos))
+ {
+ setCursor(ContactListDraggable.getCursor("copy"));
+ }
+ else
+ {
+ setCursor(ContactListDraggable.getCursor("invalid"));
+ }
+ }
+
+ /**
+ * Overwrites the dispose method in order to unregister
+ * this DnD target from the chat window.
+ * DnD events are no longer sent to this target.
+ */
+ public void dispose()
+ {
+ conferenceChatPanel.getChatWindow().unregisterDnDTarget(this);
+ }
+}
Index: src/net/java/sip/communicator/impl/gui/main/MainFrame.java

--- src/net/java/sip/communicator/impl/gui/main/MainFrame.java (revision
3432)
+++ src/net/java/sip/communicator/impl/gui/main/MainFrame.java (working
copy)
@@ -17,6 +17,7 @@

import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.customcontrols.*;
+import net.java.sip.communicator.impl.gui.dnd.DnDCoordinator;
import net.java.sip.communicator.impl.gui.i18n.*;
import net.java.sip.communicator.impl.gui.main.call.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
@@ -85,6 +86,9 @@
        providerContactHandlers
            = new Hashtable<ProtocolProviderService,
>();

+ private DnDCoordinator dndCoordinator = new DnDCoordinator();
+ public DnDCoordinator getDnDCoordinator() { return dndCoordinator; }
+
    /**
     * Creates an instance of <tt>MainFrame</tt>.
     */
Index:
src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java

---
src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java
(revision 3432)
+++
src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java
(working copy)
@@ -108,6 +108,8 @@
                {
                    draggedElement.setVisible(false);
                    draggedElement = null;
+
+
ContactList.this.mainFrame.getDnDCoordinator().cancelDnD();
                }
            }
        });
@@ -121,6 +123,8 @@
                    {
                        draggedElement.setVisible(false);
                        draggedElement = null;
+
+
ContactList.this.mainFrame.getDnDCoordinator().cancelDnD();
                    }
                }
            }
@@ -788,6 +792,10 @@

            p = SwingUtilities.convertPoint(e.getComponent(), p,
draggedElement);
            draggedElement.setLocation(p);
+
+ Point eventAbsPos = (Point) e.getPoint().clone();
+ SwingUtilities.convertPointToScreen(eventAbsPos,
e.getComponent());
+ mainFrame.getDnDCoordinator().startDnD(eventAbsPos, this,
draggedElement);
        }
    }

@@ -806,6 +814,10 @@
            p = SwingUtilities.convertPoint(e.getComponent(), p,
draggedElement);
            draggedElement.setLocation(p);
            draggedElement.repaint();
+
+ Point eventAbsPos = (Point) e.getPoint().clone();
+ SwingUtilities.convertPointToScreen(eventAbsPos,
e.getComponent());
+ mainFrame.getDnDCoordinator().updateDnD(eventAbsPos);
        }
    }

@@ -823,6 +835,8 @@
        ContactListModel listModel = (ContactListModel) this.getModel();

        Object dest = listModel.getElementAt(selectedIndex);
+ if (!contains(e.getPoint())) dest = null;
+
        if (draggedElement != null)
        {
            if (dest instanceof MetaContact)
@@ -895,6 +909,8 @@

            draggedElement.setVisible(false);
            draggedElement = null;
+
+ mainFrame.getDnDCoordinator().finishDnD();
        }
    }

Index: src/net/java/sip/communicator/impl/gui/dnd/DnDFrame.java

--- src/net/java/sip/communicator/impl/gui/dnd/DnDFrame.java (revision 0)
+++ src/net/java/sip/communicator/impl/gui/dnd/DnDFrame.java (revision 0)
@@ -0,0 +1,23 @@
+
+package net.java.sip.communicator.impl.gui.dnd;
+
+import java.awt.Point;
+
+/**
+ * Every Frame that wants to get DnD events has to implement this
interface.
+ */
+public interface DnDFrame
+{
+ /**
+ * Return true if the specified point is over this frame.
+ * "Over this frame" means title bars, scroll bars, etc.
+ */
+ public boolean inside(Point absPos);
+
+ /**
+ * If the specified point is over a target which can accept the dragged
+ * object, then return that target, otherwise return null. The source
and
+ * obj parameters are to help the target decide whether it wants the
object.
+ */
+ public DnDTarget findTarget(Point absPos, Object source, Object obj);
+}
Index: src/net/java/sip/communicator/impl/gui/dnd/DnDCoordinator.java

--- src/net/java/sip/communicator/impl/gui/dnd/DnDCoordinator.java
(revision 0)
+++ src/net/java/sip/communicator/impl/gui/dnd/DnDCoordinator.java
(revision 0)
@@ -0,0 +1,146 @@
+
+package net.java.sip.communicator.impl.gui.dnd;
+
+/**
+ * <p>This class coordinates the mouse events involved in a drag and drop
+ * transaction. The source where dragging starts should call startDnD,
updateDnD, and finishDnD
+ * or cancelDnD. This class will then call appropriate target methods.
+ * The reason this class is necessary is because when
+ * the mouse starts dragging in one Component and then moves over a
different
+ * Component, the first Component is the one that gets all the mouse
messages.
+ * That first Component is the source, and the second is the target. The
+ * job of this class is to shuttle the mouse events from the source to
the target,
+ * translating the mouse events on the way.
+ *
+ * <p><b>Note</b>: this class assumes only one DnD operation can happen
at a time
+ * (after all, there's only one mouse)
+ */
+
+import java.awt.Point;
+import java.util.Vector;
+
+public class DnDCoordinator
+{
+ private Vector<DnDFrame> frames = new Vector<DnDFrame>();
+ private Object src;
+ private Object draggable;
+ private DnDTarget currentTarget; // target the mouse is currently over
+ private Point currentPos; // position of mouse relative to target
+
+ /**
+ * Register a Frame as DnD-capable. If you don't register the Frame, it
+ * won't be notified of DnD events.
+ */
+ public void registerFrame(DnDFrame frame)
+ {
+ if (!frames.contains(frame))
+ {
+ //System.out.println("Registered a DnDFrame: "+
frame.getClass().toString());
+ frames.addElement(frame);
+ }
+ }
+ /** Unregister a Frame as DnD-capable. Dnd events will no longer be
sent. */
+ public void unregisterFrame(DnDFrame frame)
+ {
+ if (frames.contains(frame))
+ {
+ //System.out.println("Unregistered a DnDFrame: "+
frame.getClass().toString());
+ frames.removeElement(frame);
+ }
+ }
+ /** Call this when a Frame is moved to the top (eg when it gets the
focus) */
+ public void setTopFrame(DnDFrame frame)
+ {
+ if (!frames.contains(frame)) return;
+ frames.removeElement(frame);
+ frames.insertElementAt(frame, 0);
+ }
+
+ /**
+ * Call this to start a DnD operation.
+ *
+ * @param absPos The position of the mouse when the DnD operation started
+ * @param src Where the object is being dragged from. May be null.
+ * @param obj The thing that's being dragged. May be null.
+ */
+ public void startDnD(Point absPos, Object src, Object draggable)
+ {
+ this.src = src;
+ this.draggable = draggable;
+ findTarget(absPos);
+ if (currentTarget != null)
+ currentTarget.dndEnter(currentPos, src, draggable);
+ }
+ /**
+ * Call this when the mouse moves during a DnD operation.
+ *
+ * @param newAbsPos the new position of the mouse
+ */
+ public void updateDnD(Point newAbsPos)
+ {
+ DnDTarget oldTarget = currentTarget;
+ findTarget(newAbsPos);
+ // if we're still over the same target, it's a move()
+ if (currentTarget == oldTarget)
+ {
+ if (currentTarget != null)
+ currentTarget.dndMove(currentPos, src, draggable);
+ }
+ // otherwise we exit() old target and enter() new one
+ else
+ {
+ if (oldTarget != null)
+ oldTarget.dndExit(currentPos, src, draggable);
+ if (currentTarget != null)
+ currentTarget.dndEnter(currentPos, src, draggable);
+ }
+ }
+ /**
+ * Call this when the DnD operation is finished successfully (eg when the
+ * mouse button is released).
+ */
+ public void finishDnD()
+ {
+ if (currentTarget != null)
+ currentTarget.dndDrop(currentPos, src, draggable);
+ src = null;
+ currentTarget = null;
+ }
+ /**
+ * Call this if the user aborts the DnD operation without dropping the
object
+ * (eg by hitting the ESC key)
+ */
+ public void cancelDnD()
+ {
+ if (currentTarget != null)
+ currentTarget.dndCancel(currentPos, src, draggable);
+ src = null;
+ currentTarget = null;
+ }
+
+ private void findTarget(Point absPos)
+ {
+ // check for a target in every frame we know about
+ for (DnDFrame frame: frames)
+ {
+ if (frame.inside(absPos))
+ {
+ DnDTarget target = frame.findTarget(absPos, src, draggable);
+ if (target != null)
+ {
+ // hooray! we found a target
+ currentTarget = target;
+ currentPos = target.relPos(absPos);
+ return;
+ }
+ // we're over a frame, but not over a target
+ currentTarget = null;
+ currentPos = null;
+ return;
+ }
+ }
+ // we're not over a frame that supports DnD
+ currentTarget = null;
+ currentPos = null;
+ }
+}
Index: src/net/java/sip/communicator/impl/gui/dnd/DnDTarget.java

--- src/net/java/sip/communicator/impl/gui/dnd/DnDTarget.java (revision 0)
+++ src/net/java/sip/communicator/impl/gui/dnd/DnDTarget.java (revision 0)
@@ -0,0 +1,52 @@
+
+package net.java.sip.communicator.impl.gui.dnd;
+
+import java.awt.Point;
+
+/**
+ * Implement this interface if you want to have things dropped on you.
+ */
+public interface DnDTarget
+{
+ /**
+ * Convert absolute coordinates into coordinates relative to this target.
+ */
+ public Point relPos(Point absPos);
+
+ /**
+ * Return true if this target will accept the object being dragged.
+ */
+ public boolean dndAccept(Object src, Object obj);
+
+ /**
+ * Something has just been dragged onto this target.
+ * This method will not be called if dndAccept returns false.
+ */
+ public void dndEnter(Point relPos, Object src, Object obj);
+
+ /**
+ * The dragged object has been moved (but it is still over this target).
+ */
+ public void dndMove(Point relPos, Object src, Object obj);
+
+ /**
+ * The mouse has left the building. The dragged object is no longer over
+ * this target.
+ */
+ public void dndExit(Point relPos, Object src, Object obj);
+
+ /**
+ * The dragged bject has been dropped.
+ */
+ public void dndDrop(Point relPos, Object src, Object obj);
+
+ /**
+ * The source has decided to cancel the DnD operation.
+ */
+ public void dndCancel(Point relPos, Object src, Object obj);
+
+ /**
+ * Return true if the specified point is over this target.
+ */
+ public boolean inside(Point absPos);
+}

--------------------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net

------------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net


#6

Hi,

Updated the patch according to Emil's comments.

Best
Keio

Emil Ivov wrote:

SIP Communicator patch.txt (65.3 KB)

···

Hey Keio,

I've just reviewed your patch. I like the idea of having message filters
and I actually remember seeing a similar request about a week ago, so
your contribution is very well timed :).

Now, here are my comments on your "SIP Communicator patch.txt":

(in no particular order)

1. I noticed that in addition to adding two methods to
OperationSetBasicInstantMessaging, you have also implemented them in
some of the existing protocol providers. This is cool, however, I also
noticed that you chose not to implement them in Zeroconf, Mock, Rss,
SSH, and Gibberish. I guess the reason you did this was because, for
most of these protocols there was no point of supporting such a feature
(e.g., you would never filter an incoming RSS message because it can't
come from anyone else but the server).

That's actually the very reason why operation sets exist in SIP
Communicator. They allow a provider to only implement functionalities
that make sense in the context of its protocol. Therefore, I believe it
would be better if the filtering functionality came in an operation set
of its own, say, OperationSetInstantMessageFiltering.

Once you have created the interface you could keep your existing
implementations where they are, and only add the new interface in the
implements clause of the corresponding IM op set. You would also have to
change protocol providers that would support this feature and declare
the new operation set in their supportedOperationSets hashtables.

This would also allow plugins that use the filters to distinguish
protocol implementations that actually support them.

2. Our coding convention stipulates that opening braces should be on a
line of their own. (http://sip-communicator.org/codeconvention) I've
noticed that you generally respect this but there were several omissions.

3. I noticed several tab characters here in there and in SC we only use
spaces :slight_smile:

4. Most of the time, when you dispatch an incoming message to a filter
you do this

+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+ }

It would probably a good idea to keep iterating through the filters even
after a first filter has returned true in case some other filter would
also like to use the command in the message. Besides, you may also want
to add a try catch clause around the filterEvent() method so that a
filter that throws an exception would not prevent other filters and
message listeners from receiving a message.

... and that's all I guess. Other than the above your patch is quite
neat, so thanks for the effort!

Oh, almost forgot, I haven't reviewed the DND section yet but I just had
a chat with Yana and she promised she'll have a look at it tomorrow.

Cheers
Emil

P.S. Sorry for taking so long to reply and thanks for your patience!
Things should go faster now.

Keio Kraaner написа:
  

Hi,
updated the patch file:
* fixed a bug - DnD did not work after the chat window was closed and reopened;
* resolved conflicts with some newer changes (those changes did not allow to applyto the patch correctly any more).

Best,
Keio

----- Original Message ----- From: "Keio Kraaner" <keio@ut.ee>
To: <dev@sip-communicator.dev.java.net>
Sent: Tuesday, February 26, 2008 12:23 PM
Subject: Re: [sip-comm-dev] Drag and drop

Forgot to attach the patch.

Keio Kraaner wrote:
      

Hi,

The way how contacts are added to a conference chat room is a bit
inconvenient at the moment (push to "Invite others to the chat" button
opens a dialog, where one has to specify an account ID of a contact
and a reason). I think that adding contacts by dragging them to the
chat's contacts panel is a bit more intuitive (and maybe specify a
reason in the dialog).
Attached patch file contains the implementation that enables this drag
and drop functionality (at the moment the reason is not asked, an
invitation with empty reason is sent).
Also the patch contains the implementation of a generic message filter
that I sent first time in December.
Maybe someone could take a look at these two things and give some
feedback (if something should be changed/improved I am eager to do this).

Best,
Keio Kraaner

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net

--------------------------------------------------------------------------------

Index: src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java

--- src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java (revision 3320)
+++ src/net/java/sip/communicator/service/protocol/OperationSetBasicInstantMessaging.java (working copy)
@@ -102,4 +102,20 @@
     * <tt>false</tt> otherwise.
     */
    public boolean isContentTypeSupported(String contentType);
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter);
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if a event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter);
}
Index: src/net/java/sip/communicator/service/protocol/event/EventFilter.java

--- src/net/java/sip/communicator/service/protocol/event/EventFilter.java (revision 0)
+++ src/net/java/sip/communicator/service/protocol/event/EventFilter.java (revision 0)
@@ -0,0 +1,28 @@
+/*
+ * 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.service.protocol.event;
+
+import java.util.*;
+
+/**
+ * An event filter that decides if an event should be filtered out or not.
+ * For instance, maybe some type of received messages should not be
+ * shown in the chat windows.
+ *
+ * @author Keio Kraaner
+ */
+public interface EventFilter
+{
+ /**
+ * Checks if an event should be filtered out or processed.
+ *
+ * @param msg The event that should be checked
+ * @return TRUE if the event was filtered out, otherwise FALSE.
+ */
+ public boolean filterEvent(EventObject msg);
+}
Index: src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java

--- src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/yahoo/OperationSetBasicInstantMessagingYahooImpl.java (working copy)
@@ -47,6 +47,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceYahooImpl yahooProvider = null;
@@ -303,6 +308,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -593,4 +613,35 @@

        return linkBuffer.toString();
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java

--- src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/jabber/OperationSetBasicInstantMessagingJabberImpl.java (working copy)
@@ -69,6 +69,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceJabberImpl jabberProvider = null;
@@ -323,6 +328,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -588,4 +608,35 @@
            return true;
        }
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java

--- src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/zeroconf/OperationSetBasicInstantMessagingZeroconfImpl.java (working copy)
@@ -289,4 +289,14 @@
        else
           return false;
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java

--- src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/rss/OperationSetBasicInstantMessagingRssImpl.java (working copy)
@@ -453,4 +453,13 @@
       }
    }

+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/mock/MockBasicInstantMessaging.java

--- src/net/java/sip/communicator/impl/protocol/mock/MockBasicInstantMessaging.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/mock/MockBasicInstantMessaging.java (working copy)
@@ -187,4 +187,14 @@
            listener.messageReceived(msgReceivedEvt);
        }
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
\ No newline at end of file
Index: src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java

--- src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicInstantMessagingSipImpl.java (working copy)
@@ -10,6 +10,7 @@
import java.net.*;
import java.text.*;
import java.util.*;
+
import javax.sip.*;
import javax.sip.address.*;
import javax.sip.header.*;
@@ -38,6 +39,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceSipImpl sipProvider = null;
@@ -588,6 +594,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+
        Iterator listeners = null;
        synchronized (this.messageListeners)
        {
@@ -1048,5 +1069,36 @@
            }
        }
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}

Index: src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java

--- src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/icq/OperationSetBasicInstantMessagingIcqImpl.java (working copy)
@@ -46,6 +46,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The icq provider that created us.
     */
    private ProtocolProviderServiceIcqImpl icqProvider = null;
@@ -467,6 +472,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -769,4 +789,35 @@

        }
    }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java

--- src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/msn/OperationSetBasicInstantMessagingMsnImpl.java (working copy)
@@ -36,6 +36,11 @@
    private Vector messageListeners = new Vector();

    /**
+ * A list of filters registered for message events.
+ */
+ private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
+
+ /**
     * The provider that created us.
     */
    private ProtocolProviderServiceMsnImpl msnProvider = null;
@@ -259,6 +264,21 @@
     */
    private void fireMessageEvent(EventObject evt)
    {
+ // check if this event should be filtered out
+ Iterator<EventFilter> filters = null;
+ synchronized (eventFilters)
+ {
+ filters = new ArrayList<EventFilter>(eventFilters).iterator();
+ }
+ while (filters.hasNext())
+ {
+ // return if a filter has filtered this event out
+ if (filters.next().filterEvent(evt))
+ {
+ return;
+ }
+
        Iterator listeners = null;
        synchronized (messageListeners)
        {
@@ -382,5 +402,36 @@
                                                      MsnContact contact)
        {
        }
- }
+ }
+
+ /**
+ * Registeres an <tt>EventFilter</tt> with this operation set so that
+ * events, that do not need processing, are filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to register.
+ */
+ public void addEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ if(!eventFilters.contains(filter))
+ {
+ eventFilters.add(filter);
+ }
+ }
+ }
+
+ /**
+ * Unregisteres an <tt>EventFilter</tt> so that it won't check any more
+ * if an event should be filtered out.
+ *
+ * @param filter the <tt>EventFilter</tt> to unregister.
+ */
+ public void removeEventFilter(EventFilter filter)
+ {
+ synchronized(eventFilters)
+ {
+ eventFilters.remove(filter);
+ }
+ }
}
Index: src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java

--- src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java (revision 3469)
+++ src/net/java/sip/communicator/impl/protocol/ssh/OperationSetBasicInstantMessagingSSHImpl.java (working copy)
@@ -389,5 +389,15 @@
    {
        return MessageSSHImpl.contentType.equals(contentType);
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }

}
Index: src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicInstantMessagingGibberishImpl.java

--- src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicInstantMessagingGibberishImpl.java (revision 3320)
+++ src/net/java/sip/communicator/impl/protocol/gibberish/OperationSetBasicInstantMessagingGibberishImpl.java (working copy)
@@ -282,4 +282,14 @@
        else
           return false;
    }
+
+ public void addEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeEventFilter(EventFilter filter) {
+ // TODO Auto-generated method stub
+
+ }
}
Index: src/net/java/sip/communicator/impl/gui/utils/ContactListDraggable.java

--- src/net/java/sip/communicator/impl/gui/utils/ContactListDraggable.java (revision 3432)
+++ src/net/java/sip/communicator/impl/gui/utils/ContactListDraggable.java (working copy)
@@ -66,11 +66,15 @@
     * A set of 2 cursors indicating if the dragged element is currently
     * located over the contactlist or not.
     */
- private static Hashtable cursors = new Hashtable(2);
+ private static Hashtable<String, Cursor> cursors = new Hashtable<String, Cursor>(3);
+ public static Cursor getCursor(String type)
+ {
+ return cursors.get(type);
+ }
    static {
        try
        {
- cursors.put("valid", Cursor.getSystemCustomCursor("MoveDrop.32x32"));
+ cursors.put("move", Cursor.getSystemCustomCursor("MoveDrop.32x32"));
        }
        catch (Exception ex)
        {
@@ -88,6 +92,16 @@
                "will use Cursor.WAIT_CURSOR instead", ex);
            cursors.put("invalid", Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        }
+ try
+ {
+ cursors.put("copy", Cursor.getSystemCustomCursor("CopyDrop.32x32"));
+ }
+ catch (Exception ex)
+ {
+ logger.debug("Cursor \"CopyDrop.32x32\" isn't available " +
+ "will use Cursor.CROSSHAIR_CURSOR instead", ex);
+ cursors.put("copy", Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
+ }
    }

    /**
@@ -237,11 +251,11 @@
        Point p = SwingUtilities.convertPoint(this, location, contactList);
        if (contactList.contains(p))
        {
- setCursor((Cursor) cursors.get("valid"));
+ setCursor(getCursor("move"));
        }
        else
        {
- setCursor((Cursor) cursors.get("invalid"));
+ setCursor(getCursor("invalid"));
        }
    }
}
Index: src/net/java/sip/communicator/impl/gui/main/chat/ChatContactListPanel.java

--- src/net/java/sip/communicator/impl/gui/main/chat/ChatContactListPanel.java (revision 3432)
+++ src/net/java/sip/communicator/impl/gui/main/chat/ChatContactListPanel.java (working copy)
@@ -36,7 +36,7 @@

    private JScrollPane contactsScrollPane = new JScrollPane();

- private JPanel mainPanel = new JPanel(new BorderLayout());
+ private JPanel mainPanel;

    private SIPCommButton addToChatButton = new SIPCommButton(
        ImageLoader.getImage(ImageLoader.ADD_TO_CHAT_BUTTON),
@@ -57,6 +57,12 @@
        super(new BorderLayout(5, 5));

        this.chatPanel = chat;
+
+ if(chatPanel instanceof ConferenceChatPanel)
+ {
+ mainPanel = new ConferenceChatContactsPanel((ConferenceChatPanel)chatPanel, new BorderLayout());
+ }
+ else mainPanel = new JPanel(new BorderLayout());

        this.contactsPanel.setLayout(
            new BoxLayout(contactsPanel, BoxLayout.Y_AXIS));
Index: src/net/java/sip/communicator/impl/gui/main/chat/ChatWindow.java

--- src/net/java/sip/communicator/impl/gui/main/chat/ChatWindow.java (revision 3462)
+++ src/net/java/sip/communicator/impl/gui/main/chat/ChatWindow.java (working copy)
@@ -16,6 +16,9 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.customcontrols.*;
import net.java.sip.communicator.impl.gui.customcontrols.events.*;
+import net.java.sip.communicator.impl.gui.dnd.DnDCoordinator;
+import net.java.sip.communicator.impl.gui.dnd.DnDFrame;
+import net.java.sip.communicator.impl.gui.dnd.DnDTarget;
import net.java.sip.communicator.impl.gui.main.*;
import net.java.sip.communicator.impl.gui.main.MainFrame.*;
import net.java.sip.communicator.impl.gui.main.chat.menus.*;
@@ -41,7 +44,8 @@
public class ChatWindow
    extends SIPCommFrame
    implements ExportedWindow,
- PluginComponentListener
+ PluginComponentListener,
+ DnDFrame, WindowFocusListener
{
    private Logger logger = Logger.getLogger(ChatWindow.class.getName());

@@ -60,6 +64,11 @@
    public ChatWindow(MainFrame mainFrame)
    {
        this.mainFrame = mainFrame;
+
+ dndCoordinator = mainFrame.getDnDCoordinator();
+ dndCoordinator.registerFrame(this);
+ enableEvents(AWTEvent.WINDOW_FOCUS_EVENT_MASK);
+ addWindowFocusListener(this);

        this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

@@ -721,4 +730,87 @@
            g.drawImage(backgroundImage, 0, 0, null);
        }
    }
+
+ private DnDCoordinator dndCoordinator;
+
+ /**
+ * Vector of objects of type <tt>DnDTarget</tt> that are interested in dnd events.
+ */
+ private Vector<DnDTarget> dndTargets = new Vector<DnDTarget>();
+ /**
+ * Registers a DnDTarget.
+ * If you don't register, you won't get dnd events.
+ */
+ public void registerDnDTarget(DnDTarget target)
+ {
+ //System.out.println("Registered a DnDTaget: "+ target.getClass().toString());
+ dndTargets.addElement(target);
+ }
+ /**
+ * Unregisters the DnDTarget.
+ * Dnd events will no longer be sent.
+ */
+ public void unregisterDnDTarget(DnDTarget target)
+ {
+ //System.out.println("Unregistered a DnDTaget: "+ target.getClass().toString());
+ dndTargets.removeElement(target);
+ }
+
+ /**
+ * If the specified point is over a target which can accept the dragged
+ * object, then return that target, otherwise return null. The source and
+ * obj parameters are to help the target decide whether it wants the
+ * offered object.
+ *
+ * We go through the list of registered DnDTargets and see if any of them
+ * are both under the specified point and want to accept the dragged object.
+ */
+ public DnDTarget findTarget(Point absPos, Object source, Object obj)
+ {
+ for (DnDTarget target: dndTargets)
+ if (target.inside(absPos) && target.dndAccept(source, obj))
+ return target;
+ return null;
+ }
+
+ /**
+ * Return true if the specified point is over this chat frame.
+ * "Over this frame" means title bars, scroll bars, etc.
+ */
+ public boolean inside(Point absPos)
+ {
+ return getBounds().contains(absPos);
+ }
+
+ /**
+ * Overwrites the setVisible method in order to notify the
+ * DnD coorinator that this chat window is on top of other all frames.
+ */
+ public void setVisible(boolean isVisible)
+ {
+ super.setVisible(isVisible);
+ if (isVisible) dndCoordinator.setTopFrame(this);
+ }
+
+ /**
+ * Overwrites the dispose method in order to unregister
+ * this chat window from DnD coordinator.
+ * DnD events are no longer sent to this chat window.
+ */
+ public void dispose()
+ {
+ dndCoordinator.unregisterFrame(this);
+ super.dispose();
+ }
+
+ public void windowGainedFocus(WindowEvent arg0)
+ {
+ //System.out.println("focusGained: "+ this.getClass().toString());
+ dndCoordinator.setTopFrame(this);
+ }
+
+ public void windowLostFocus(WindowEvent arg0)
+ {
+ //System.out.println("focusLost: "+ this.getClass().toString());
+ }
}
Index: src/net/java/sip/communicator/impl/gui/main/chat/conference/MultiUserChatManager.java

--- src/net/java/sip/communicator/impl/gui/main/chat/conference/MultiUserChatManager.java (revision 3432)
+++ src/net/java/sip/communicator/impl/gui/main/chat/conference/MultiUserChatManager.java (working copy)
@@ -67,7 +67,10 @@
    }

    public void invitationRejected(ChatRoomInvitationRejectedEvent evt)
- {
+ {
+ chatWindowManager.getMultiChat(evt.getChatRoom()).
+ processMessage(evt.getInvitee(), evt.getTimestamp(),
+ Constants.ACTION_MESSAGE, "Rejected invitation: "+evt.getReason(), ChatConversationPanel.TEXT_CONTENT_TYPE);
    }

    /**
Index: src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatContactsPanel.java

--- src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatContactsPanel.java (revision 0)
+++ src/net/java/sip/communicator/impl/gui/main/chat/conference/ConferenceChatContactsPanel.java (revision 0)
@@ -0,0 +1,174 @@
+package net.java.sip.communicator.impl.gui.main.chat.conference;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.LayoutManager;
+import java.awt.Point;
+import java.util.Date;
+
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import net.java.sip.communicator.impl.gui.dnd.DnDTarget;
+import net.java.sip.communicator.impl.gui.main.chat.ChatConversationPanel;
+import net.java.sip.communicator.impl.gui.main.contactlist.ContactList;
+import net.java.sip.communicator.impl.gui.utils.Constants;
+import net.java.sip.communicator.impl.gui.utils.ContactListDraggable;
+import net.java.sip.communicator.service.protocol.ChatRoom;
+import net.java.sip.communicator.service.protocol.Contact;
+
+/**
+ * The <tt>ConferenceChatContactsPanel</tt> is the panel in the
+ * <tt>ChatContactListPanel</tt> that contains a list of
+ * <tt>ChatContactPanel</tt>s if a chat is a conference.
+ * The <tt>ConferenceChatContactsPanel</tt> enables to add participants
+ * to the conference with drag-and-drop.
+ *
+ * @author Keio Kraaner
+ */
+@SuppressWarnings("serial")
+public class ConferenceChatContactsPanel extends JPanel
+ implements DnDTarget
+{
+ private ConferenceChatPanel conferenceChatPanel;
+
+ public ConferenceChatContactsPanel(ConferenceChatPanel conferenceChatPanel, LayoutManager layout)
+ {
+ super(layout);
+ this.conferenceChatPanel = conferenceChatPanel;
+ conferenceChatPanel.getChatWindow().registerDnDTarget(this);
+ }
+
+ public boolean inside(Point absPos)
+ {
+ return contains(relPos(absPos));
+ }
+
+ public Point relPos(Point absPos)
+ {
+ Point p = (Point) absPos.clone();
+ SwingUtilities.convertPointFromScreen(p, this);
+ return p;
+ }
+
+ public boolean dndAccept(Object src, Object obj)
+ {
+ if (src instanceof ContactList && obj instanceof ContactListDraggable)
+ return true;
+ return false;
+ }
+
+ public void dndDrop(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ Object o = conferenceChatPanel.getChatIdentifier();
+ if (!(o instanceof ChatRoomWrapper)) return;
+ ChatRoomWrapper chatRoomWrapper = (ChatRoomWrapper) o;
+ ChatRoom chatRoom = chatRoomWrapper.getChatRoom();
+ if (chatRoom == null) return;
+ Contact contact;
+ if (draggable.getContact() != null)
+ contact = draggable.getContact();
+ else contact = draggable.getMetaContact().getDefaultContact();
+ chatRoom.invite(contact.getAddress(), "");
+ conferenceChatPanel.processMessage(contact.getDisplayName(),
+ new Date(System.currentTimeMillis()),
+ Constants.ACTION_MESSAGE,
+ "Sending invitation to join",
+ ChatConversationPanel.TEXT_CONTENT_TYPE);
+
+ draggable = null;
+ setCursor(Cursor.getDefaultCursor());
+ repaint();
+ }
+
+ public void dndCancel(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ draggable = null;
+ setCursor(Cursor.getDefaultCursor());
+ repaint();
+ }
+
+ private ContactListDraggable draggable = null;
+ private Point draggablePos;
+ public void dndEnter(Point relPos, Object src, Object obj)
+ {
+ if (!(obj instanceof ContactListDraggable)) return;
+
+ draggable = (ContactListDraggable) obj;
+ draggablePos = relPos;
+ repaint();
+ }
+
+ public void dndExit(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ draggable = null;
+ setCursor(Cursor.getDefaultCursor());
+ repaint();
+ }
+
+ public void dndMove(Point relPos, Object src, Object obj)
+ {
+ if (draggable == null) return;
+
+ if (relPos.equals(draggablePos)) return;
+
+ draggablePos = relPos;
+ repaint();
+ }
+
+ public void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+ drawDraggable(g);
+ }
+
+ private void drawDraggable(Graphics g)
+ {
+ if (draggable == null || draggablePos == null) return;
+
+ // make the dragged element "transparent" with a coef. of 0.8
+ // and paint it
+ Graphics2D g2 = (Graphics2D) g;
+ g2.setComposite(AlphaComposite.
+ getInstance(AlphaComposite.SRC_OVER, 0.8f));
+
+ g2.drawImage(
+ draggable.getImage(),
+ (int) (draggablePos.getX() - draggable.getImage().getWidth(null) / 2),
+ (int) (draggablePos.getY() - draggable.getImage().getHeight(null) / 2),
+ null);
+
+ g2.setColor(Color.GRAY);
+ g2.drawRect((int) (draggablePos.getX() - draggable.getImage().getWidth(null) / 2),
+ (int) (draggablePos.getY() - draggable.getImage().getHeight(null) / 2),
+ draggable.getImage().getWidth(null),
+ draggable.getImage().getHeight(null));
+ if (contains(draggablePos))
+ {
+ setCursor(ContactListDraggable.getCursor("copy"));
+ }
+ else
+ {
+ setCursor(ContactListDraggable.getCursor("invalid"));
+ }
+ }
+
+ /**
+ * Overwrites the dispose method in order to unregister
+ * this DnD target from the chat window.
+ * DnD events are no longer sent to this target.
+ */
+ public void dispose()
+ {
+ conferenceChatPanel.getChatWindow().unregisterDnDTarget(this);
+ }
+}
Index: src/net/java/sip/communicator/impl/gui/main/MainFrame.java

--- src/net/java/sip/communicator/impl/gui/main/MainFrame.java (revision 3432)
+++ src/net/java/sip/communicator/impl/gui/main/MainFrame.java (working copy)
@@ -17,6 +17,7 @@

import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.customcontrols.*;
+import net.java.sip.communicator.impl.gui.dnd.DnDCoordinator;
import net.java.sip.communicator.impl.gui.i18n.*;
import net.java.sip.communicator.impl.gui.main.call.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
@@ -85,6 +86,9 @@
        providerContactHandlers
            = new Hashtable<ProtocolProviderService, >();

+ private DnDCoordinator dndCoordinator = new DnDCoordinator();
+ public DnDCoordinator getDnDCoordinator() { return dndCoordinator; }
+
    /**
     * Creates an instance of <tt>MainFrame</tt>.
     */
Index: src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java

--- src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java (revision 3432)
+++ src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java (working copy)
@@ -108,6 +108,8 @@
                {
                    draggedElement.setVisible(false);
                    draggedElement = null;
+
+ ContactList.this.mainFrame.getDnDCoordinator().cancelDnD();
                }
            }
        });
@@ -121,6 +123,8 @@
                    {
                        draggedElement.setVisible(false);
                        draggedElement = null;
+
+ ContactList.this.mainFrame.getDnDCoordinator().cancelDnD();
                    }
                }
            }
@@ -788,6 +792,10 @@

            p = SwingUtilities.convertPoint(e.getComponent(), p, draggedElement);
            draggedElement.setLocation(p);
+
+ Point eventAbsPos = (Point) e.getPoint().clone();
+ SwingUtilities.convertPointToScreen(eventAbsPos, e.getComponent());
+ mainFrame.getDnDCoordinator().startDnD(eventAbsPos, this, draggedElement);
        }
    }

@@ -806,6 +814,10 @@
            p = SwingUtilities.convertPoint(e.getComponent(), p, draggedElement);
            draggedElement.setLocation(p);
            draggedElement.repaint();
+
+ Point eventAbsPos = (Point) e.getPoint().clone();
+ SwingUtilities.convertPointToScreen(eventAbsPos, e.getComponent());
+ mainFrame.getDnDCoordinator().updateDnD(eventAbsPos);
        }
    }

@@ -823,6 +835,8 @@
        ContactListModel listModel = (ContactListModel) this.getModel();

        Object dest = listModel.getElementAt(selectedIndex);
+ if (!contains(e.getPoint())) dest = null;
+
        if (draggedElement != null)
        {
            if (dest instanceof MetaContact)
@@ -895,6 +909,8 @@

            draggedElement.setVisible(false);
            draggedElement = null;
+
+ mainFrame.getDnDCoordinator().finishDnD();
        }
    }

Index: src/net/java/sip/communicator/impl/gui/dnd/DnDFrame.java

--- src/net/java/sip/communicator/impl/gui/dnd/DnDFrame.java (revision 0)
+++ src/net/java/sip/communicator/impl/gui/dnd/DnDFrame.java (revision 0)
@@ -0,0 +1,23 @@
+
+package net.java.sip.communicator.impl.gui.dnd;
+
+import java.awt.Point;
+
+/**
+ * Every Frame that wants to get DnD events has to implement this interface.
+ */
+public interface DnDFrame
+{
+ /**
+ * Return true if the specified point is over this frame.
+ * "Over this frame" means title bars, scroll bars, etc.
+ */
+ public boolean inside(Point absPos);
+
+ /**
+ * If the specified point is over a target which can accept the dragged
+ * object, then return that target, otherwise return null. The source and
+ * obj parameters are to help the target decide whether it wants the object.
+ */
+ public DnDTarget findTarget(Point absPos, Object source, Object obj);
+}
Index: src/net/java/sip/communicator/impl/gui/dnd/DnDCoordinator.java

--- src/net/java/sip/communicator/impl/gui/dnd/DnDCoordinator.java (revision 0)
+++ src/net/java/sip/communicator/impl/gui/dnd/DnDCoordinator.java (revision 0)
@@ -0,0 +1,146 @@
+
+package net.java.sip.communicator.impl.gui.dnd;
+
+/**
+ * <p>This class coordinates the mouse events involved in a drag and drop
+ * transaction. The source where dragging starts should call startDnD, updateDnD, and finishDnD
+ * or cancelDnD. This class will then call appropriate target methods.
+ * The reason this class is necessary is because when
+ * the mouse starts dragging in one Component and then moves over a different
+ * Component, the first Component is the one that gets all the mouse messages.
+ * That first Component is the source, and the second is the target. The
+ * job of this class is to shuttle the mouse events from the source to the target,
+ * translating the mouse events on the way.
+ *
+ * <p><b>Note</b>: this class assumes only one DnD operation can happen at a time
+ * (after all, there's only one mouse)
+ */
+
+import java.awt.Point;
+import java.util.Vector;
+
+public class DnDCoordinator
+{
+ private Vector<DnDFrame> frames = new Vector<DnDFrame>();
+ private Object src;
+ private Object draggable;
+ private DnDTarget currentTarget; // target the mouse is currently over
+ private Point currentPos; // position of mouse relative to target
+
+ /**
+ * Register a Frame as DnD-capable. If you don't register the Frame, it
+ * won't be notified of DnD events.
+ */
+ public void registerFrame(DnDFrame frame)
+ {
+ if (!frames.contains(frame))
+ {
+ //System.out.println("Registered a DnDFrame: "+ frame.getClass().toString());
+ frames.addElement(frame);
+ }
+ /** Unregister a Frame as DnD-capable. Dnd events will no longer be sent. */
+ public void unregisterFrame(DnDFrame frame)
+ {
+ if (frames.contains(frame))
+ {
+ //System.out.println("Unregistered a DnDFrame: "+ frame.getClass().toString());
+ frames.removeElement(frame);
+ }
+ /** Call this when a Frame is moved to the top (eg when it gets the focus) */
+ public void setTopFrame(DnDFrame frame)
+ {
+ if (!frames.contains(frame)) return;
+ frames.removeElement(frame);
+ frames.insertElementAt(frame, 0);
+ }
+
+ /**
+ * Call this to start a DnD operation.
+ *
+ * @param absPos The position of the mouse when the DnD operation started
+ * @param src Where the object is being dragged from. May be null.
+ * @param obj The thing that's being dragged. May be null.
+ */
+ public void startDnD(Point absPos, Object src, Object draggable)
+ {
+ this.src = src;
+ this.draggable = draggable;
+ findTarget(absPos);
+ if (currentTarget != null)
+ currentTarget.dndEnter(currentPos, src, draggable);
+ }
+ /**
+ * Call this when the mouse moves during a DnD operation.
+ *
+ * @param newAbsPos the new position of the mouse
+ */
+ public void updateDnD(Point newAbsPos)
+ {
+ DnDTarget oldTarget = currentTarget;
+ findTarget(newAbsPos);
+ // if we're still over the same target, it's a move()
+ if (currentTarget == oldTarget)
+ {
+ if (currentTarget != null)
+ currentTarget.dndMove(currentPos, src, draggable);
+ }
+ // otherwise we exit() old target and enter() new one
+ else
+ {
+ if (oldTarget != null)
+ oldTarget.dndExit(currentPos, src, draggable);
+ if (currentTarget != null)
+ currentTarget.dndEnter(currentPos, src, draggable);
+ }
+ /**
+ * Call this when the DnD operation is finished successfully (eg when the
+ * mouse button is released).
+ */
+ public void finishDnD()
+ {
+ if (currentTarget != null)
+ currentTarget.dndDrop(currentPos, src, draggable);
+ src = null;
+ currentTarget = null;
+ }
+ /**
+ * Call this if the user aborts the DnD operation without dropping the object
+ * (eg by hitting the ESC key)
+ */
+ public void cancelDnD()
+ {
+ if (currentTarget != null)
+ currentTarget.dndCancel(currentPos, src, draggable);
+ src = null;
+ currentTarget = null;
+ }
+
+ private void findTarget(Point absPos)
+ {
+ // check for a target in every frame we know about
+ for (DnDFrame frame: frames)
+ {
+ if (frame.inside(absPos))
+ {
+ DnDTarget target = frame.findTarget(absPos, src, draggable);
+ if (target != null)
+ {
+ // hooray! we found a target
+ currentTarget = target;
+ currentPos = target.relPos(absPos);
+ return;
+ }
+ // we're over a frame, but not over a target
+ currentTarget = null;
+ currentPos = null;
+ return;
+ }
+ // we're not over a frame that supports DnD
+ currentTarget = null;
+ currentPos = null;
+ }
+}
Index: src/net/java/sip/communicator/impl/gui/dnd/DnDTarget.java

--- src/net/java/sip/communicator/impl/gui/dnd/DnDTarget.java (revision 0)
+++ src/net/java/sip/communicator/impl/gui/dnd/DnDTarget.java (revision 0)
@@ -0,0 +1,52 @@
+
+package net.java.sip.communicator.impl.gui.dnd;
+
+import java.awt.Point;
+
+/**
+ * Implement this interface if you want to have things dropped on you.
+ */
+public interface DnDTarget
+{
+ /**
+ * Convert absolute coordinates into coordinates relative to this target.
+ */
+ public Point relPos(Point absPos);
+
+ /**
+ * Return true if this target will accept the object being dragged.
+ */
+ public boolean dndAccept(Object src, Object obj);
+
+ /**
+ * Something has just been dragged onto this target.
+ * This method will not be called if dndAccept returns false.
+ */
+ public void dndEnter(Point relPos, Object src, Object obj);
+
+ /**
+ * The dragged object has been moved (but it is still over this target).
+ */
+ public void dndMove(Point relPos, Object src, Object obj);
+
+ /**
+ * The mouse has left the building. The dragged object is no longer over
+ * this target.
+ */
+ public void dndExit(Point relPos, Object src, Object obj);
+
+ /**
+ * The dragged bject has been dropped.
+ */
+ public void dndDrop(Point relPos, Object src, Object obj);
+
+ /**
+ * The source has decided to cancel the DnD operation.
+ */
+ public void dndCancel(Point relPos, Object src, Object obj);
+
+ /**
+ * Return true if the specified point is over this target.
+ */
+ public boolean inside(Point absPos);
+}

--------------------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net

------------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net
      
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net