aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl/gui/main/contactlist/DefaultContactList.java
blob: 7113a6a0a8845be2be6443a5abae6f4ebc6ab06d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package net.java.sip.communicator.impl.gui.main.contactlist;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import javax.swing.*;
import javax.swing.text.*;

import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
import net.java.sip.communicator.impl.gui.main.chat.conference.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.skin.*;
import net.java.sip.communicator.util.swing.*;

/**
 * DeafultContactlist used to display <code>JList</code>s with contacts.
 *
 * @author Damian Minkov
 * @author Yana Stamcheva
 * @author Adam Netocny
 */
public class DefaultContactList
    extends JList
    implements Skinnable
{
    private static final long serialVersionUID = 0L;

    /**
     * The cached mouse event.
     */
    private MouseEvent cachedMouseEvent;

    /**
     * List cell renderer.
     */
    ContactListCellRenderer renderer = new ContactListCellRenderer();

    /**
     * Creates an instance of <tt>DefaultContactList</tt>.
     */
    public DefaultContactList()
    {
        this.setOpaque(false);

        this.getSelectionModel().setSelectionMode(
                ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

        this.setDragEnabled(true);
//        this.setTransferHandler(new ContactListTransferHandler(this));
        this.setCellRenderer(renderer);
    }

    /**
     * Checks if the given contact is currently active.
     * Dummy method used and overridden from classes extending this
     * functionality such as ContactList.
     *
     * @param metaContact the <tt>MetaContact</tt> to verify
     * @return TRUE if the given <tt>MetaContact</tt> is active, FALSE -
     * otherwise
     */
    public boolean isMetaContactActive(MetaContact metaContact)
    {
        return false;
    }

    /**
     * Checks whether the group is closed.
     * Dummy method used and overridden from classes extending this
     * functionality such as ContactList.
     *
     * @param group The group to check.
     * @return True if the group is closed, false - otherwise.
     */
    public boolean isGroupClosed(MetaContactGroup group)
    {
        return false;
    }

    /**
     * Returns the general status of the given MetaContact. Detects the status
     * using the priority status table. The priority is defined on the
     * "availability" factor and here the most "available" status is returned.
     *
     * @param metaContact The metaContact for which the status is asked.
     * @return PresenceStatus The most "available" status from all subcontact
     *         statuses.
     */
    public PresenceStatus getMetaContactStatus(MetaContact metaContact)
    {
        PresenceStatus status = null;
        Iterator<Contact> i = metaContact.getContacts();
        while (i.hasNext()) {
            Contact protoContact = i.next();
            PresenceStatus contactStatus = protoContact.getPresenceStatus();

            if (status == null) {
                status = contactStatus;
            } else {
                status = (contactStatus.compareTo(status) > 0) ? contactStatus
                        : status;
            }
        }
        return status;
    }

    /**
     * Creates a customized tooltip for this contact list.
     *
     * @return The customized tooltip.
     */
    public JToolTip createToolTip()
    {
        Point currentMouseLocation = MouseInfo.getPointerInfo().getLocation();

        SwingUtilities.convertPointFromScreen(currentMouseLocation, this);

        int index = this.locationToIndex(currentMouseLocation);

        Object element = getModel().getElementAt(index);

        ExtendedTooltip tip = new ExtendedTooltip(
            GuiActivator.getUIService().getMainFrame(), true);

        if (element instanceof MetaContact)
        {
            MetaContact metaContact = (MetaContact) element;

            byte[] avatarImage = metaContact.getAvatar();

            if (avatarImage != null && avatarImage.length > 0)
                tip.setImage(new ImageIcon(avatarImage));

            tip.setTitle(metaContact.getDisplayName());

            Iterator<Contact> i = metaContact.getContacts();

            String statusMessage = null;
            Contact protocolContact;
            while (i.hasNext())
            {
                protocolContact = i.next();

                ImageIcon protocolStatusIcon
                    = new ImageIcon(
                        protocolContact.getPresenceStatus().getStatusIcon());

                String contactAddress = protocolContact.getAddress();
                //String statusMessage = protocolContact.getStatusMessage();

                tip.addLine(protocolStatusIcon, contactAddress);

                // Set the first found status message.
                if (statusMessage == null
                    && protocolContact.getStatusMessage() != null
                    && protocolContact.getStatusMessage().length() > 0)
                    statusMessage = protocolContact.getStatusMessage();
            }

            if (statusMessage != null)
                tip.setBottomText(statusMessage);
        }
        else if (element instanceof MetaContactGroup)
        {
            MetaContactGroup metaGroup = (MetaContactGroup) element;

            tip.setTitle(metaGroup.getGroupName());
        }
        else if (element instanceof ChatContact<?>)
        {
            ChatContact<?> chatContact = (ChatContact<?>) element;

            ImageIcon avatarImage = chatContact.getAvatar();

            if (avatarImage != null)
                tip.setImage(avatarImage);

            tip.setTitle(chatContact.getName());

            Object descriptor = chatContact.getDescriptor();

            if(descriptor instanceof ChatRoomMember)
            {
                ChatRoomMember member = (ChatRoomMember)descriptor;

                tip.addLine(
                    ChatContactRoleIcon.getRoleIcon(member.getRole()),
                    member.getRole().getLocalizedRoleName());
            }
        }

        tip.setComponent(this);

        return tip;
    }

    /**
     * Returns the string to be used as the tooltip for <i>event</i>. We don't
     * really use this string, but we need to return a different string each
     * time in order to make the TooltipManager change the tooltip over the
     * different cells in the JList.
     *
     * @param event the <tt>MouseEvent</tt> that notified us
     * @return the string to be used as the tooltip for <i>event</i>.
     */
    public String getToolTipText(MouseEvent event)
    {
        Point currentMouseLocation = event.getPoint();

        int index = this.locationToIndex(currentMouseLocation);

        // If the index is equals to -1, then we have nothing to do here, we
        // just return null.
        if (index == -1)
            return null;

        Object element = getModel().getElementAt(index);

        /*
         * As stated above, the returned tooltip isn't actually displayed and we
         * just have to be sure to return different string values for the
         * different list elements. But the displayName property value doesn't
         * cut it because it isn't unique across the elements.
         */
        if (element instanceof MetaContact)
        {
            MetaContact metaContact = (MetaContact) element;

            return metaContact.getMetaUID();
        }
        else if (element instanceof MetaContactGroup)
        {
            MetaContactGroup metaGroup = (MetaContactGroup) element;

            return metaGroup.getMetaUID();
        }
        else if (element instanceof ChatContact<?>)
        {
            ChatContact<?> chatContact = (ChatContact<?>) element;

            return chatContact.getUID();
        }
        return null;
    }

    /**
     * Returns the next list element that starts with a prefix.
     *
     * @param prefix the string to test for a match
     * @param startIndex the index for starting the search
     * @param bias the search direction, either Position.Bias.Forward or
     *            Position.Bias.Backward.
     * @return the index of the next list element that starts with the prefix;
     *         otherwise -1
     */
    public int getNextMatch(String prefix, int startIndex, Position.Bias bias)
    {
        int max = getModel().getSize();

        if (prefix == null)
            throw new IllegalArgumentException("prefix");
        if (startIndex < 0 || startIndex >= max)
            throw new IllegalArgumentException("startIndex");

        prefix = prefix.toUpperCase();

        // start search from the next element after the selected element
        int increment = (bias == Position.Bias.Forward) ? 1 : -1;
        int index = startIndex;
        do
        {
            Object o = getModel().getElementAt(index);

            if (o != null)
            {
                String contactName = null;

                if (o instanceof MetaContact)
                {
                    contactName = ((MetaContact) o).getDisplayName()
                        .toUpperCase();
                }
                else if(o instanceof ConferenceChatContact)
                {
                    contactName = ((ConferenceChatContact) o).getName()
                        .toUpperCase();
                }

                if (contactName != null && contactName.startsWith(prefix))
                {
                    return index;
                }
            }
            index = (index + increment + max) % max;
        } while (index != startIndex);
        return -1;
    }

    /**
     * Processes the <tt>MouseEvent</tt> we have previously cached before
     * invoking the parent <tt>fireSelectionValueChanged</tt> which would
     * notify the <tt>JList</tt> <tt>ListSelectionListener</tt>s that the
     * selection model has changed.
     * <p>
     * Workaround provided by simon@tardell.se on 29-DEC-2002 for bug 4521075
     * http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=a13e98ab2364524506eb91505565?bug_id=4521075
     * "Drag gesture in JAVA different from Windows". The bug is also noticed
     * on Mac Leopard.
     *
     * @param firstIndex the first selected index
     * @param lastIndex the last selected index
     * @param isAdjusting true if multiple changes are being made
     */
    protected void fireSelectionValueChanged(int firstIndex, int lastIndex,
                                            boolean isAdjusting)
    {
        if (cachedMouseEvent != null)
        {
            super.processMouseEvent(new MouseEvent(
                (Component) cachedMouseEvent.getSource(),
                cachedMouseEvent.getID(),
                cachedMouseEvent.getWhen(),
                cachedMouseEvent.getModifiers(),
                cachedMouseEvent.getX(),
                cachedMouseEvent.getY(),
                cachedMouseEvent.getClickCount(),
                cachedMouseEvent.isPopupTrigger()));

            cachedMouseEvent = null;
        }
        super.fireSelectionValueChanged(firstIndex, lastIndex, isAdjusting);
    }

    /**
     * Caches the incoming mouse <tt>event</tt> before passing it to the parent
     * implementation of <tt>processMouseEvent</tt>.
     * <p>
     * Workaround provided by simon@tardell.se on 29-DEC-2002 for bug 4521075
     * http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=a13e98ab2364524506eb91505565?bug_id=4521075
     * "Drag gesture in JAVA different from Windows". The bug is also noticed
     * on Mac Leopard.
     * @param event the <tt>MouseEvent</tt> to process
     */
    protected void processMouseEvent(MouseEvent event)
    {
        if ((event.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0)
            cachedMouseEvent= event;
        super.processMouseEvent(event);
    }

    /**
     * Reloads skin information stored in render class.
     */
    public void loadSkin()
    {
        renderer.loadSkin();
    }
}