aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYana Stamcheva <yana@jitsi.org>2010-05-03 12:27:34 +0000
committerYana Stamcheva <yana@jitsi.org>2010-05-03 12:27:34 +0000
commitafcafe3061f9abaa52633b727fe34769d4cc0e04 (patch)
treea36c09734602576e9a8388794142420f06ce649c /src
parentc454284e43c6db33c1d13f4a1c9d9e06d7726d9f (diff)
downloadjitsi-afcafe3061f9abaa52633b727fe34769d4cc0e04.zip
jitsi-afcafe3061f9abaa52633b727fe34769d4cc0e04.tar.gz
jitsi-afcafe3061f9abaa52633b727fe34769d4cc0e04.tar.bz2
- Introduces new contact list data model that allows adding of external contact sources and hence the search in such sources.
- As part of the support for external contact sources, implements a call history external source and its user interface. - Addresses issue #706 Indicate missed calls
Diffstat (limited to 'src')
-rwxr-xr-xsrc/net/java/sip/communicator/impl/callhistory/CallHistoryActivator.java122
-rw-r--r--src/net/java/sip/communicator/impl/callhistory/CallHistoryContactSource.java153
-rw-r--r--src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java259
-rw-r--r--src/net/java/sip/communicator/impl/callhistory/CallHistorySourceContact.java266
-rw-r--r--src/net/java/sip/communicator/impl/callhistory/CallRecordImpl.java9
-rw-r--r--src/net/java/sip/communicator/impl/callhistory/callhistory.manifest.mf5
-rw-r--r--src/net/java/sip/communicator/impl/contactlist/MetaContactGroupImpl.java108
-rw-r--r--src/net/java/sip/communicator/impl/gui/GuiActivator.java41
-rwxr-xr-xsrc/net/java/sip/communicator/impl/gui/main/MainFrame.java18
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java177
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallManager.java57
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java47
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/ChooseCallAccountPopupMenu.java31
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/MissedCallsListener.java23
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/ReceivedCallDialog.java2
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/chat/ChatTransferHandler.java29
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/CallHistoryFilter.java154
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java56
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListEvent.java76
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListFilter.java40
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListListener.java26
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListNode.java8
-rwxr-xr-xsrc/net/java/sip/communicator/impl/gui/main/contactlist/ContactListPane.java58
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTransferHandler.java65
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java220
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeModel.java214
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/ContactNode.java35
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/DefaultTreeContactList.java85
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/GroupNode.java241
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/MetaContactRightButtonMenu.java (renamed from src/net/java/sip/communicator/impl/gui/main/contactlist/ContactRightButtonMenu.java)103
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/PresenceFilter.java184
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/SearchField.java19
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java275
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/SourceContactRightButtonMenu.java136
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java742
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/UIContact.java128
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/UIContactDetail.java90
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/UIGroup.java89
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/UnknownContactPanel.java2
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java201
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactListSource.java244
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIContact.java408
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIGroup.java164
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java282
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java10
-rw-r--r--src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf1
-rw-r--r--src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java12
-rw-r--r--src/net/java/sip/communicator/impl/history/HistoryReaderImpl.java49
-rw-r--r--src/net/java/sip/communicator/service/callhistory/CallHistoryQuery.java53
-rw-r--r--src/net/java/sip/communicator/service/callhistory/CallHistoryService.java38
-rw-r--r--src/net/java/sip/communicator/service/callhistory/CallRecord.java47
-rw-r--r--src/net/java/sip/communicator/service/callhistory/event/CallHistoryQueryListener.java26
-rw-r--r--src/net/java/sip/communicator/service/callhistory/event/CallRecordsEvent.java61
-rw-r--r--src/net/java/sip/communicator/service/contactlist/MetaContactGroup.java36
-rw-r--r--src/net/java/sip/communicator/service/contactsource/ContactDetail.java41
-rw-r--r--src/net/java/sip/communicator/service/contactsource/ContactQuery.java16
-rw-r--r--src/net/java/sip/communicator/service/contactsource/ContactSourceService.java19
-rw-r--r--src/net/java/sip/communicator/service/contactsource/SourceContact.java10
-rw-r--r--src/net/java/sip/communicator/service/contactsource/contactsource.manifest.mf3
-rw-r--r--src/net/java/sip/communicator/service/history/HistoryReader.java59
-rw-r--r--src/net/java/sip/communicator/util/swing/ExtendedTransferHandler.java9
-rw-r--r--src/net/java/sip/communicator/util/swing/SIPCommTextButton.java49
-rw-r--r--src/net/java/sip/communicator/util/swing/SIPCommTextField.java97
-rw-r--r--src/net/java/sip/communicator/util/swing/event/TextFieldChangeListener.java27
64 files changed, 5085 insertions, 1270 deletions
diff --git a/src/net/java/sip/communicator/impl/callhistory/CallHistoryActivator.java b/src/net/java/sip/communicator/impl/callhistory/CallHistoryActivator.java
index 37118c7..049ca9c 100755
--- a/src/net/java/sip/communicator/impl/callhistory/CallHistoryActivator.java
+++ b/src/net/java/sip/communicator/impl/callhistory/CallHistoryActivator.java
@@ -6,15 +6,22 @@
*/
package net.java.sip.communicator.impl.callhistory;
+import java.util.*;
+
import org.osgi.framework.*;
+
import net.java.sip.communicator.service.history.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.service.callhistory.*;
+import net.java.sip.communicator.service.contactsource.*;
/**
- * Activates the CallHistoryService
+ * Activates the <tt>CallHistoryService</tt>.
*
* @author Damian Minkov
+ * @author Yana Stamcheva
*/
public class CallHistoryActivator
implements BundleActivator
@@ -22,18 +29,39 @@ public class CallHistoryActivator
private static Logger logger =
Logger.getLogger(CallHistoryActivator.class);
- private CallHistoryServiceImpl callHistoryService = null;
+ /**
+ * The bundle context.
+ */
+ public static BundleContext bundleContext;
+
+ /**
+ * The <tt>CallHistoryServiceImpl</tt> instantiated in the start method
+ * of this bundle.
+ */
+ private static CallHistoryServiceImpl callHistoryService = null;
+
+ /**
+ * The service responsible for resources.
+ */
+ private static ResourceManagementService resourcesService;
+
+ /**
+ * The map containing all registered
+ */
+ private static final Map<Object, ProtocolProviderFactory>
+ providerFactoriesMap = new Hashtable<Object, ProtocolProviderFactory>();
/**
* Initialize and start call history
*
- * @param bundleContext BundleContext
+ * @param bc the <tt>BundleContext</tt>
* @throws Exception
*/
- public void start(BundleContext bundleContext) throws Exception
+ public void start(BundleContext bc) throws Exception
{
- try{
+ bundleContext = bc;
+ try{
logger.logEntry();
ServiceReference refHistory = bundleContext.getServiceReference(
@@ -53,6 +81,10 @@ public class CallHistoryActivator
bundleContext.registerService(
CallHistoryService.class.getName(), callHistoryService, null);
+ bundleContext.registerService(
+ ContactSourceService.class.getName(),
+ new CallHistoryContactSource(), null);
+
logger.info("Call History Service ...[REGISTERED]");
}
finally
@@ -62,9 +94,89 @@ public class CallHistoryActivator
}
+ /**
+ * Stops this bundle.
+ * @param bundleContext the <tt>BundleContext</tt>
+ * @throws Exception if the stop operation goes wrong
+ */
public void stop(BundleContext bundleContext) throws Exception
{
if(callHistoryService != null)
callHistoryService.stop(bundleContext);
}
+
+ /**
+ * Returns the instance of <tt>CallHistoryService</tt> created in this
+ * activator.
+ * @return the instance of <tt>CallHistoryService</tt> created in this
+ * activator
+ */
+ public static CallHistoryService getCallHistoryService()
+ {
+ return callHistoryService;
+ }
+
+ /**
+ * Returns the <tt>ResourceManagementService</tt>, through which we will
+ * access all resources.
+ *
+ * @return the <tt>ResourceManagementService</tt>, through which we will
+ * access all resources.
+ */
+ public static ResourceManagementService getResources()
+ {
+ if (resourcesService == null)
+ {
+ ServiceReference serviceReference = bundleContext
+ .getServiceReference(ResourceManagementService.class.getName());
+
+ if(serviceReference == null)
+ return null;
+
+ resourcesService = (ResourceManagementService) bundleContext
+ .getService(serviceReference);
+ }
+ return resourcesService;
+ }
+
+ /**
+ * Returns all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
+ * context.
+ *
+ * @return all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
+ * context
+ */
+ public static Map<Object, ProtocolProviderFactory>
+ getProtocolProviderFactories()
+ {
+ ServiceReference[] serRefs = null;
+ try
+ {
+ // get all registered provider factories
+ serRefs =
+ bundleContext.getServiceReferences(
+ ProtocolProviderFactory.class.getName(), null);
+
+ }
+ catch (InvalidSyntaxException e)
+ {
+ logger.error("LoginManager : " + e);
+ }
+
+ if (serRefs != null)
+ {
+ for (int i = 0; i < serRefs.length; i++)
+ {
+
+ ProtocolProviderFactory providerFactory
+ = (ProtocolProviderFactory) bundleContext
+ .getService(serRefs[i]);
+
+ providerFactoriesMap.put(serRefs[i]
+ .getProperty(ProtocolProviderFactory.PROTOCOL),
+ providerFactory);
+ }
+ }
+ return providerFactoriesMap;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/callhistory/CallHistoryContactSource.java b/src/net/java/sip/communicator/impl/callhistory/CallHistoryContactSource.java
new file mode 100644
index 0000000..74d4249
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/callhistory/CallHistoryContactSource.java
@@ -0,0 +1,153 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.callhistory;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.callhistory.*;
+import net.java.sip.communicator.service.contactsource.*;
+
+/**
+ * The <tt>CallHistoryContactSource</tt> is the contact source for the call
+ * history.
+ *
+ * @author Yana Stamcheva
+ */
+public class CallHistoryContactSource implements ContactSourceService
+{
+ /**
+ * The display name of this contact source.
+ */
+ private static final String CALL_HISTORY_NAME = "Call history";
+
+ /**
+ * Returns the display name of this contact source.
+ * @return the display name of this contact source
+ */
+ public String getDisplayName()
+ {
+ return CALL_HISTORY_NAME;
+ }
+
+ /**
+ * Queries this contact source for the given <tt>searchString</tt>.
+ * @param queryString the string to search for
+ * @return the created query
+ */
+ public ContactQuery queryContactSource(String queryString)
+ {
+ if (queryString != null && queryString.length() > 0)
+ return new CallHistoryQuery(
+ CallHistoryActivator.getCallHistoryService()
+ .findByPeer(queryString));
+ else
+ return new CallHistoryQuery(
+ CallHistoryActivator.getCallHistoryService()
+ .findLast(50));
+ }
+
+ /**
+ * The <tt>CallHistoryQuery</tt> contains information about a current query
+ * to the contact source.
+ */
+ private class CallHistoryQuery implements ContactQuery
+ {
+ /**
+ * A list of all registered query listeners.
+ */
+ private final List<ContactQueryListener> queryListeners
+ = new LinkedList<ContactQueryListener>();
+
+ /**
+ * A list of all source contact results.
+ */
+ private final List<SourceContact> sourceContacts
+ = new LinkedList<SourceContact>();
+
+ /**
+ * Creates a <tt>CallHistoryQuery</tt>.
+ * @param callRecords a collection of the result call records
+ */
+ public CallHistoryQuery(Collection<CallRecord> callRecords)
+ {
+ Iterator<CallRecord> recordsIter = callRecords.iterator();
+
+ while (recordsIter.hasNext())
+ {
+ sourceContacts.add(
+ new CallHistorySourceContact(
+ CallHistoryContactSource.this,
+ recordsIter.next()));
+ }
+ }
+
+ /**
+ * Adds the given <tt>ContactQueryListener</tt> to the list of query
+ * listeners.
+ * @param l the <tt>ContactQueryListener</tt> to add
+ */
+ public void addContactQueryListener(ContactQueryListener l)
+ {
+ synchronized (queryListeners)
+ {
+ queryListeners.add(l);
+ }
+ }
+
+ /**
+ * This query could not be canceled.
+ */
+ public void cancel()
+ {
+
+ }
+
+ /**
+ * Removes the given <tt>ContactQueryListener</tt> from the list of
+ * query listeners.
+ * @param l the <tt>ContactQueryListener</tt> to remove
+ */
+ public void removeContactQueryListener(ContactQueryListener l)
+ {
+ synchronized (queryListeners)
+ {
+ queryListeners.remove(l);
+ }
+ }
+
+ /**
+ * Returns a list containing the results of this query.
+ * @return a list containing the results of this query
+ */
+ public List<SourceContact> getQueryResults()
+ {
+ return sourceContacts;
+ }
+
+ /**
+ * Returns the <tt>ContactSourceService</tt>, where this query was first
+ * initiated.
+ * @return the <tt>ContactSourceService</tt>, where this query was first
+ * initiated
+ */
+ public ContactSourceService getContactSource()
+ {
+ return CallHistoryContactSource.this;
+ }
+ }
+
+ /**
+ * Returns the identifier of this contact source. Some of the common
+ * identifiers are defined here (For example the CALL_HISTORY identifier
+ * should be returned by all call history implementations of this interface)
+ * @return the identifier of this contact source
+ */
+ public String getIdentifier()
+ {
+ return CALL_HISTORY;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java b/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java
index 48fbbb6..93d83f4 100644
--- a/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java
@@ -42,8 +42,9 @@ public class CallHistoryServiceImpl
Logger.getLogger(CallHistoryServiceImpl.class);
private static String[] STRUCTURE_NAMES =
- new String[] { "callStart", "callEnd", "dir", "callParticipantIDs",
- "callParticipantStart", "callParticipantEnd","callParticipantStates" };
+ new String[] { "accountUID", "callStart", "callEnd", "dir",
+ "callParticipantIDs", "callParticipantStart",
+ "callParticipantEnd", "callParticipantStates" };
private static HistoryRecordStructure recordStructure =
new HistoryRecordStructure(STRUCTURE_NAMES);
@@ -59,8 +60,10 @@ public class CallHistoryServiceImpl
private Object syncRoot_HistoryService = new Object();
- private final Map<CallHistorySearchProgressListener, SearchProgressWrapper> progressListeners =
- new Hashtable<CallHistorySearchProgressListener, SearchProgressWrapper>();
+ private final Map<CallHistorySearchProgressListener, SearchProgressWrapper>
+ progressListeners
+ = new Hashtable<CallHistorySearchProgressListener,
+ SearchProgressWrapper>();
private final List<CallRecordImpl> currentCallRecords =
new Vector<CallRecordImpl>();
@@ -68,22 +71,29 @@ public class CallHistoryServiceImpl
private final CallChangeListener historyCallChangeListener =
new HistoryCallChangeListener();
+ private HistoryReader historyReader;
+
+ /**
+ * Returns the underlying history service.
+ * @return the underlying history service
+ */
public HistoryService getHistoryService()
{
return historyService;
}
/**
- * Returns all the calls made by all the contacts
- * in the supplied metacontact after the given date
+ * Returns all the calls made by all the contacts in the supplied
+ * <tt>contact</tt> after the given date.
*
* @param contact MetaContact which contacts participate in
* the returned calls
* @param startDate Date the start date of the calls
- * @return Collection of CallRecords with CallPeerRecord
+ * @return the <tt>CallHistoryQuery</tt>, corresponding to this find
* @throws RuntimeException
*/
- public Collection<CallRecord> findByStartDate(MetaContact contact, Date startDate)
+ public Collection<CallRecord> findByStartDate(
+ MetaContact contact, Date startDate)
throws RuntimeException
{
throw new UnsupportedOperationException("Not implemented yet!");
@@ -93,7 +103,7 @@ public class CallHistoryServiceImpl
* Returns all the calls made after the given date
*
* @param startDate Date the start date of the calls
- * @return Collection of CallRecords with CallPeerRecord
+ * @return the <tt>CallHistoryQuery</tt>, corresponding to this find
* @throws RuntimeException
*/
public Collection<CallRecord> findByStartDate(Date startDate)
@@ -104,16 +114,16 @@ public class CallHistoryServiceImpl
{
// the default ones
History history = this.getHistory(null, null);
- HistoryReader reader = history.getReader();
- addHistorySearchProgressListeners(reader, 1);
+ historyReader = history.getReader();
+ addHistorySearchProgressListeners(historyReader, 1);
QueryResultSet<HistoryRecord> rs
- = reader.findByStartDate(startDate);
+ = historyReader.findByStartDate(startDate);
while (rs.hasNext())
{
HistoryRecord hr = rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
- removeHistorySearchProgressListeners(reader);
+ removeHistorySearchProgressListeners(historyReader);
}
catch (IOException ex)
{
@@ -133,7 +143,8 @@ public class CallHistoryServiceImpl
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
- public Collection<CallRecord> findByEndDate(MetaContact contact, Date endDate)
+ public Collection<CallRecord> findByEndDate(MetaContact contact,
+ Date endDate)
throws RuntimeException
{
throw new UnsupportedOperationException("Not implemented yet!");
@@ -146,22 +157,25 @@ public class CallHistoryServiceImpl
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
- public Collection<CallRecord> findByEndDate(Date endDate) throws RuntimeException
+ public Collection<CallRecord> findByEndDate(Date endDate)
+ throws RuntimeException
{
- TreeSet<CallRecord> result = new TreeSet<CallRecord>(new CallRecordComparator());
+ TreeSet<CallRecord> result
+ = new TreeSet<CallRecord>(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
- HistoryReader reader = history.getReader();
- addHistorySearchProgressListeners(reader, 1);
- QueryResultSet<HistoryRecord> rs = reader.findByEndDate(endDate);
+ historyReader = history.getReader();
+ addHistorySearchProgressListeners(historyReader, 1);
+ QueryResultSet<HistoryRecord> rs
+ = historyReader.findByEndDate(endDate);
while (rs.hasNext())
{
HistoryRecord hr = rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
- removeHistorySearchProgressListeners(reader);
+ removeHistorySearchProgressListeners(historyReader);
}
catch (IOException ex)
{
@@ -181,7 +195,8 @@ public class CallHistoryServiceImpl
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
- public Collection<CallRecord> findByPeriod(MetaContact contact, Date startDate, Date endDate)
+ public Collection<CallRecord> findByPeriod(MetaContact contact,
+ Date startDate, Date endDate)
throws RuntimeException
{
throw new UnsupportedOperationException("Not implemented yet!");
@@ -195,24 +210,25 @@ public class CallHistoryServiceImpl
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
- public Collection<CallRecord> findByPeriod(Date startDate, Date endDate) throws
- RuntimeException
+ public Collection<CallRecord> findByPeriod(Date startDate, Date endDate)
+ throws RuntimeException
{
- TreeSet<CallRecord> result = new TreeSet<CallRecord>(new CallRecordComparator());
+ TreeSet<CallRecord> result
+ = new TreeSet<CallRecord>(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
- HistoryReader reader = history.getReader();
- addHistorySearchProgressListeners(reader, 1);
+ historyReader = history.getReader();
+ addHistorySearchProgressListeners(historyReader, 1);
QueryResultSet<HistoryRecord> rs
- = reader.findByPeriod(startDate, endDate);
+ = historyReader.findByPeriod(startDate, endDate);
while (rs.hasNext())
{
HistoryRecord hr = rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
- removeHistorySearchProgressListeners(reader);
+ removeHistorySearchProgressListeners(historyReader);
}
catch (IOException ex)
{
@@ -247,13 +263,14 @@ public class CallHistoryServiceImpl
*/
public Collection<CallRecord> findLast(int count) throws RuntimeException
{
- TreeSet<CallRecord> result = new TreeSet<CallRecord>(new CallRecordComparator());
+ TreeSet<CallRecord> result
+ = new TreeSet<CallRecord>(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
- QueryResultSet<HistoryRecord> rs
- = history.getReader().findLast(count);
+ historyReader = history.getReader();
+ QueryResultSet<HistoryRecord> rs = historyReader.findLast(count);
while (rs.hasNext())
{
HistoryRecord hr = rs.next();
@@ -277,21 +294,22 @@ public class CallHistoryServiceImpl
public Collection<CallRecord> findByPeer(String address)
throws RuntimeException
{
- TreeSet<CallRecord> result = new TreeSet<CallRecord>(new CallRecordComparator());
+ TreeSet<CallRecord> result
+ = new TreeSet<CallRecord>(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
- HistoryReader reader = history.getReader();
- addHistorySearchProgressListeners(reader, 1);
+ historyReader = history.getReader();
+ addHistorySearchProgressListeners(historyReader, 1);
QueryResultSet<HistoryRecord> rs
- = reader.findByKeyword(address, "callParticipantIDs");
+ = historyReader.findByKeyword(address, "callParticipantIDs");
while (rs.hasNext())
{
HistoryRecord hr = rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
- removeHistorySearchProgressListeners(reader);
+ removeHistorySearchProgressListeners(historyReader);
}
catch (IOException ex)
{
@@ -364,19 +382,21 @@ public class CallHistoryServiceImpl
String propName = hr.getPropertyNames()[i];
String value = hr.getPropertyValues()[i];
- if(propName.equals(STRUCTURE_NAMES[0]))
- result.setStartTime(new Date(Long.parseLong(value)));
+ if (propName.equals(STRUCTURE_NAMES[0]))
+ result.setProtocolProvider(getProtocolProvider(value));
else if(propName.equals(STRUCTURE_NAMES[1]))
- result.setEndTime(new Date(Long.parseLong(value)));
+ result.setStartTime(new Date(Long.parseLong(value)));
else if(propName.equals(STRUCTURE_NAMES[2]))
- result.setDirection(value);
+ result.setEndTime(new Date(Long.parseLong(value)));
else if(propName.equals(STRUCTURE_NAMES[3]))
- callPeerIDs = getCSVs(value);
+ result.setDirection(value);
else if(propName.equals(STRUCTURE_NAMES[4]))
- callPeerStart = getCSVs(value);
+ callPeerIDs = getCSVs(value);
else if(propName.equals(STRUCTURE_NAMES[5]))
- callPeerEnd = getCSVs(value);
+ callPeerStart = getCSVs(value);
else if(propName.equals(STRUCTURE_NAMES[6]))
+ callPeerEnd = getCSVs(value);
+ else if(propName.equals(STRUCTURE_NAMES[7]))
callPeerStates = getStates(value);
}
@@ -557,10 +577,12 @@ public class CallHistoryServiceImpl
* @param source Contact
* @param destination Contact
*/
- private void writeCall(CallRecord callRecord, Contact source,
- Contact destination)
+ private void writeCall( CallRecordImpl callRecord,
+ Contact source,
+ Contact destination)
{
- try {
+ try
+ {
History history = this.getHistory(source, destination);
HistoryWriter historyWriter = history.getWriter();
@@ -589,6 +611,8 @@ public class CallHistoryServiceImpl
}
historyWriter.addRecord(new String[] {
+ callRecord.getSourceCall().getProtocolProvider()
+ .getAccountID().getAccountUniqueID(),
String.valueOf(callRecord.getStartTime().getTime()),
String.valueOf(callRecord.getEndTime().getTime()),
callRecord.getDirection(),
@@ -596,8 +620,10 @@ public class CallHistoryServiceImpl
callPeerStartTime.toString(),
callPeerEndTime.toString(),
callPeerStates.toString()},
- new Date()); // this date is when the history record is written
- } catch (IOException e)
+ new Date()); // this date is when the history
+ // record is written
+ }
+ catch (IOException e)
{
logger.error("Could not add call to history", e);
}
@@ -646,9 +672,11 @@ public class CallHistoryServiceImpl
*/
public void serviceChanged(ServiceEvent serviceEvent)
{
- Object sService = bundleContext.getService(serviceEvent.getServiceReference());
+ Object sService
+ = bundleContext.getService(serviceEvent.getServiceReference());
- logger.trace("Received a service event for: " + sService.getClass().getName());
+ logger.trace("Received a service event for: "
+ + sService.getClass().getName());
// we don't care if the source service is not a protocol provider
if (! (sService instanceof ProtocolProviderService))
@@ -667,13 +695,12 @@ public class CallHistoryServiceImpl
{
this.handleProviderRemoved( (ProtocolProviderService) sService);
}
-
}
/**
* Used to attach the Call History Service to existing or
- * just registered protocol provider. Checks if the provider has implementation
- * of OperationSetBasicTelephony
+ * just registered protocol provider. Checks if the provider has
+ * implementation of OperationSetBasicTelephony
*
* @param provider ProtocolProviderService
*/
@@ -779,34 +806,6 @@ public class CallHistoryServiceImpl
}
/**
- * Gets all the history readers for the contacts in the given MetaContact
- *
- * @param contact MetaContact
- * @return Hashtable
- */
- private Map<Contact, HistoryReader> getHistoryReaders(MetaContact contact)
- {
- Map<Contact, HistoryReader> readers =
- new Hashtable<Contact, HistoryReader>();
- Iterator<Contact> iter = contact.getContacts();
- while (iter.hasNext())
- {
- Contact item = iter.next();
-
- try
- {
- History history = this.getHistory(null, item);
- readers.put(item, history.getReader());
- }
- catch (IOException e)
- {
- logger.error("Could not read history", e);
- }
- }
- return readers;
- }
-
- /**
* CallListener implementation for incoming calls
* @param event CallEvent
*/
@@ -830,17 +829,8 @@ public class CallHistoryServiceImpl
*/
public void callEnded(CallEvent event)
{
- CallRecordImpl callRecord = findCallRecord(event.getSourceCall());
-
- // no such call
- if (callRecord == null)
- return;
-
- callRecord.setEndTime(new Date());
-
- writeCall(callRecord, null, null);
-
- currentCallRecords.remove(callRecord);
+ // We store the call in the callStateChangeEvent where we
+ // have more information on the previous state of the call.
}
/**
@@ -1027,9 +1017,11 @@ public class CallHistoryServiceImpl
*/
private int getProgressMapping(int historyProgress)
{
- currentProgress += (historyProgress - lastHistoryProgress)/contactCount;
+ currentProgress
+ += (historyProgress - lastHistoryProgress)/contactCount;
- if(historyProgress == HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
+ if(historyProgress
+ == HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
{
currentContactCount++;
lastHistoryProgress = 0;
@@ -1074,18 +1066,93 @@ public class CallHistoryServiceImpl
/**
* Receive events for adding or removing peers from a call
*/
- private class HistoryCallChangeListener
- extends CallChangeAdapter
+ private class HistoryCallChangeListener implements CallChangeListener
{
+ /**
+ * Indicates that a new call peer has joined the source call.
+ *
+ * @param evt the <tt>CallPeerEvent</tt> containing the source call
+ * and call peer.
+ */
public void callPeerAdded(CallPeerEvent evt)
{
handlePeerAdded(evt.getSourceCallPeer());
}
+ /**
+ * Indicates that a call peer has left the source call.
+ *
+ * @param evt the <tt>CallPeerEvent</tt> containing the source call
+ * and call peer.
+ */
public void callPeerRemoved(CallPeerEvent evt)
{
handlePeerRemoved(evt.getSourceCallPeer(),
evt.getSourceCall());
}
+
+ /**
+ * A dummy implementation of this listener's callStateChanged() method.
+ *
+ * @param evt the <tt>CallChangeEvent</tt> instance containing the source
+ * calls and its old and new state.
+ */
+ public void callStateChanged(CallChangeEvent evt)
+ {
+ CallRecordImpl callRecord = findCallRecord(evt.getSourceCall());
+
+ // no such call
+ if (callRecord == null)
+ return;
+
+ if (evt.getNewValue().equals(CallState.CALL_ENDED)
+ && evt.getOldValue().equals(CallState.CALL_INITIALIZATION))
+ {
+ callRecord.setEndTime(callRecord.getStartTime());
+ }
+ else
+ callRecord.setEndTime(new Date());
+
+ writeCall(callRecord, null, null);
+
+ currentCallRecords.remove(callRecord);
+ }
+ }
+
+ /**
+ * Returns the <tt>ProtocolProviderService</tt> corresponding to the given
+ * account identifier.
+ * @param accountUID the identifier of the account.
+ * @return the <tt>ProtocolProviderService</tt> corresponding to the given
+ * account identifier
+ */
+ private ProtocolProviderService getProtocolProvider(String accountUID)
+ {
+ for (ProtocolProviderFactory providerFactory
+ : CallHistoryActivator.getProtocolProviderFactories().values())
+ {
+ ServiceReference serRef;
+
+ for (AccountID accountID : providerFactory.getRegisteredAccounts())
+ {
+ if (accountID.getAccountUniqueID().equals(accountUID))
+ {
+ serRef = providerFactory.getProviderForAccount(accountID);
+
+ return (ProtocolProviderService) CallHistoryActivator
+ .bundleContext.getService(serRef);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Cancels the current find. If there's no find going on, then does nothing.
+ */
+ public void cancelCurrentFind()
+ {
+ if (historyReader != null)
+ historyReader.cancelCurrentFind();
}
}
diff --git a/src/net/java/sip/communicator/impl/callhistory/CallHistorySourceContact.java b/src/net/java/sip/communicator/impl/callhistory/CallHistorySourceContact.java
new file mode 100644
index 0000000..b9f7c8c
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/callhistory/CallHistorySourceContact.java
@@ -0,0 +1,266 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.callhistory;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.callhistory.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * The <tt>CallHistorySourceContact</tt> is an implementation of the
+ * <tt>SourceContact</tt> interface based on a <tt>CallRecord</tt>.
+ *
+ * @author Yana Stamcheva
+ */
+public class CallHistorySourceContact implements SourceContact
+{
+ /**
+ * The parent <tt>CallHistoryContactSource</tt>, where this contact is
+ * contained.
+ */
+ private final CallHistoryContactSource contactSource;
+
+ /**
+ * The corresponding call record.
+ */
+ private final CallRecord callRecord;
+
+ /**
+ * The incoming call icon.
+ */
+ private static final byte[] incomingIcon
+ = CallHistoryActivator.getResources()
+ .getImageInBytes("service.gui.icons.INCOMING_CALL");
+
+ /**
+ * The outgoing call icon.
+ */
+ private static byte[] outgoingIcon
+ = CallHistoryActivator.getResources()
+ .getImageInBytes("service.gui.icons.OUTGOING_CALL");
+
+ /**
+ * The missed call icon.
+ */
+ private static byte[] missedCallIcon
+ = CallHistoryActivator.getResources()
+ .getImageInBytes("service.gui.icons.MISSED_CALL");
+
+ /**
+ * A list of all contact details.
+ */
+ private final List<ContactDetail> contactDetails
+ = new LinkedList<ContactDetail>();
+
+ /**
+ * The display name of this contact.
+ */
+ private String displayName = "";
+
+ /**
+ * The display details of this contact.
+ */
+ private final String displayDetails;
+
+ /**
+ * Creates an instance of <tt>CallHistorySourceContact</tt>
+ * @param contactSource
+ * @param callRecord
+ */
+ public CallHistorySourceContact(CallHistoryContactSource contactSource,
+ CallRecord callRecord)
+ {
+ this.contactSource = contactSource;
+ this.callRecord = callRecord;
+
+ this.initPeerDetails();
+
+ this.displayDetails
+ = CallHistoryActivator.getResources()
+ .getI18NString("service.gui.AT") + ": "
+ + getDateString(callRecord.getStartTime().getTime())
+ + " " + CallHistoryActivator.getResources()
+ .getI18NString("service.gui.DURATION") + ": "
+ + GuiUtils.formatTime(
+ GuiUtils.substractDates(
+ callRecord.getEndTime(), callRecord.getStartTime()));
+ }
+
+ /**
+ * Initializes peer details.
+ */
+ private void initPeerDetails()
+ {
+ Iterator<CallPeerRecord> recordsIter
+ = callRecord.getPeerRecords().iterator();
+
+ while (recordsIter.hasNext())
+ {
+ String peerAddress = recordsIter.next().getPeerAddress();
+
+ if (displayName.length() > 0)
+ displayName += "," + peerAddress;
+ else
+ displayName += peerAddress;
+
+ if (peerAddress != null)
+ {
+ ContactDetail contactDetail = new ContactDetail(peerAddress);
+
+ Map<Class<? extends OperationSet>, ProtocolProviderService>
+ preferredProviders = null;
+ if (callRecord.getProtocolProvider() != null)
+ {
+ preferredProviders
+ = new Hashtable<Class<? extends OperationSet>,
+ ProtocolProviderService>();
+
+ preferredProviders.put( OperationSetBasicTelephony.class,
+ callRecord.getProtocolProvider());
+
+ contactDetail
+ .setPreferredProviders(preferredProviders);
+ }
+
+ // Set supported operation sets.
+ LinkedList<Class<? extends OperationSet>> supportedOpSets
+ = new LinkedList<Class<? extends OperationSet>>();
+
+ supportedOpSets.add(OperationSetBasicTelephony.class);
+ contactDetail.setSupportedOpSets(supportedOpSets);
+
+ contactDetails.add(contactDetail);
+ }
+ }
+ }
+
+ /**
+ * Returns a list of available contact details.
+ * @return a list of available contact details
+ */
+ public List<ContactDetail> getContactDetails()
+ {
+ return new LinkedList<ContactDetail>(contactDetails);
+ }
+
+ /**
+ * Returns the parent <tt>ContactSourceService</tt> from which this contact
+ * came from.
+ * @return the parent <tt>ContactSourceService</tt> from which this contact
+ * came from
+ */
+ public ContactSourceService getContactSource()
+ {
+ return contactSource;
+ }
+
+ /**
+ * Returns the display details of this search contact. This could be any
+ * important information that should be shown to the user.
+ *
+ * @return the display details of the search contact
+ */
+ public String getDisplayDetails()
+ {
+ return displayDetails;
+ }
+
+ /**
+ * Returns the display name of this search contact. This is a user-friendly
+ * name that could be shown in the user interface.
+ *
+ * @return the display name of this search contact
+ */
+ public String getDisplayName()
+ {
+ return displayName;
+ }
+
+ /**
+ * An image (or avatar) corresponding to this search contact. If such is
+ * not available this method will return null.
+ *
+ * @return the byte array of the image or null if no image is available
+ */
+ public byte[] getImage()
+ {
+ if (callRecord.getDirection().equals(CallRecord.IN))
+ {
+ if (callRecord.getStartTime().equals(callRecord.getEndTime()))
+ return missedCallIcon;
+ else
+ return incomingIcon;
+ }
+ else if (callRecord.getDirection().equals(CallRecord.OUT))
+ return outgoingIcon;
+
+ return null;
+ }
+
+ /**
+ * Returns a list of all <tt>ContactDetail</tt>s supporting the given
+ * <tt>OperationSet</tt> class.
+ * @param operationSet the <tt>OperationSet</tt> class we're looking for
+ * @return a list of all <tt>ContactDetail</tt>s supporting the given
+ * <tt>OperationSet</tt> class
+ */
+ public List<ContactDetail> getContactDetails(
+ Class<? extends OperationSet> operationSet)
+ {
+ // We support only call details.
+ if (!operationSet.equals(OperationSetBasicTelephony.class))
+ return null;
+
+ return new LinkedList<ContactDetail>(contactDetails);
+ }
+
+ /**
+ * Returns the preferred <tt>ContactDetail</tt> for a given
+ * <tt>OperationSet</tt> class.
+ * @param operationSet the <tt>OperationSet</tt> class, for which we would
+ * like to obtain a <tt>ContactDetail</tt>
+ * @return the preferred <tt>ContactDetail</tt> for a given
+ * <tt>OperationSet</tt> class
+ */
+ public ContactDetail getPreferredContactDetail(
+ Class<? extends OperationSet> operationSet)
+ {
+ // We support only call details.
+ if (!operationSet.equals(OperationSetBasicTelephony.class))
+ return null;
+
+ return contactDetails.get(0);
+ }
+
+ /**
+ * Returns the date string to show for the given date.
+ *
+ * @param date the date to format
+ * @return the date string to show for the given date
+ */
+ public static String getDateString(long date)
+ {
+ String time = GuiUtils.formatTime(date);
+
+ // If the current date we don't go in there and we'll return just the
+ // time.
+ if (GuiUtils.compareDatesOnly(date, System.currentTimeMillis()) < 0)
+ {
+ StringBuffer dateStrBuf = new StringBuffer();
+
+ GuiUtils.formatDate(date, dateStrBuf);
+ dateStrBuf.append(" ");
+ dateStrBuf.append(time);
+ return dateStrBuf.toString();
+ }
+
+ return time;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/callhistory/CallRecordImpl.java b/src/net/java/sip/communicator/impl/callhistory/CallRecordImpl.java
index c52728a..bda0907 100644
--- a/src/net/java/sip/communicator/impl/callhistory/CallRecordImpl.java
+++ b/src/net/java/sip/communicator/impl/callhistory/CallRecordImpl.java
@@ -96,4 +96,13 @@ public class CallRecordImpl
{
this.direction = direction;
}
+
+ /**
+ * Sets the given <tt>ProtocolProviderService</tt> used for the call.
+ * @param pps the <tt>ProtocolProviderService</tt> to set
+ */
+ public void setProtocolProvider(ProtocolProviderService pps)
+ {
+ this.protocolProvider = pps;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/callhistory/callhistory.manifest.mf b/src/net/java/sip/communicator/impl/callhistory/callhistory.manifest.mf
index b8db32f..44a6baf 100644
--- a/src/net/java/sip/communicator/impl/callhistory/callhistory.manifest.mf
+++ b/src/net/java/sip/communicator/impl/callhistory/callhistory.manifest.mf
@@ -14,6 +14,9 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.icqconstants,
net.java.sip.communicator.service.protocol.event,
+ net.java.sip.communicator.service.contactsource,
+ net.java.sip.communicator.service.resources
Export-Package: net.java.sip.communicator.service.callhistory,
net.java.sip.communicator.service.callhistory.event
-Metadata-Location: /net/java/sip/communicator/impl/msghistory/callhistory.metadata.xml
+Metadata-Location: net.java.sip.communicator.impl.msghistory,
+ callhistory.metadata.xml
diff --git a/src/net/java/sip/communicator/impl/contactlist/MetaContactGroupImpl.java b/src/net/java/sip/communicator/impl/contactlist/MetaContactGroupImpl.java
index 4d6a449..924aac9 100644
--- a/src/net/java/sip/communicator/impl/contactlist/MetaContactGroupImpl.java
+++ b/src/net/java/sip/communicator/impl/contactlist/MetaContactGroupImpl.java
@@ -68,6 +68,22 @@ public class MetaContactGroupImpl
private final MetaContactListServiceImpl mclServiceImpl;
/**
+ * The user-specific key-value associations stored in this instance.
+ * <p>
+ * Like the Widget implementation of Eclipse SWT, the storage type takes
+ * into account that there are likely to be many
+ * <code>MetaContactGroupImpl</code> instances and <code>Map</code>s are
+ * thus likely to impose increased memory use. While an array may very well
+ * perform worse than a <code>Map</code> with respect to search, the
+ * mechanism of user-defined key-value associations explicitly states that
+ * it is not guaranteed to be optimized for any particular use and only
+ * covers the most basic cases and performance-savvy code will likely
+ * implement a more optimized solution anyway.
+ * </p>
+ */
+ private Object[] data;
+
+ /**
* Creates an instance of the root meta contact group.
*
* @param mclServiceImpl
@@ -85,6 +101,8 @@ public class MetaContactGroupImpl
* specified meta contact uid. This constructor MUST NOT be used for nothing
* purposes else but restoring contacts extracted from the contactlist.xml
*
+ * @param mclServiceImpl the implementation of the
+ * <tt>MetaContactListService</tt>, to which this group belongs
* @param groupName the name of the group to create
* @param metaUID a UID that has been stored earlier or null when a new
* UID needs to be created.
@@ -932,8 +950,98 @@ public class MetaContactGroupImpl
return subgroups.remove(group);
}
+ /**
+ * Returns the implementation of the <tt>MetaContactListService</tt>, to
+ * which this group belongs.
+ * @return the implementation of the <tt>MetaContactListService</tt>
+ */
final MetaContactListServiceImpl getMclServiceImpl()
{
return mclServiceImpl;
}
+
+ /**
+ * Implements {@link MetaContactGroup#getData(Object)}.
+ * @return the data value corresponding to the given key
+ */
+ public Object getData(Object key)
+ {
+ if (key == null)
+ throw new NullPointerException("key");
+
+ int index = dataIndexOf(key);
+
+ return (index == -1) ? null : data[index + 1];
+ }
+
+ /**
+ * Implements {@link MetaContactGroup#setData(Object, Object)}.
+ * @param key the of the data
+ * @param value the value of the data
+ */
+ public void setData(Object key, Object value)
+ {
+ if (key == null)
+ throw new NullPointerException("key");
+
+ int index = dataIndexOf(key);
+
+ if (index == -1)
+ {
+
+ /*
+ * If value is null, remove the association with key (or just don't
+ * add it).
+ */
+ if (data == null)
+ if (value != null)
+ data = new Object[] { key, value };
+ else if (value == null)
+ {
+ int length = data.length - 2;
+
+ if (length > 0)
+ {
+ Object[] newData = new Object[length];
+
+ System.arraycopy(data, 0, newData, 0, index);
+ System.arraycopy(
+ data, index + 2, newData, index, length - index);
+ data = newData;
+ }
+ else
+ data = null;
+ }
+ else
+ {
+ int length = data.length;
+ Object[] newData = new Object[length + 2];
+
+ System.arraycopy(data, 0, newData, 0, length);
+ data[length++] = key;
+ data[length++] = value;
+ data = newData;
+ }
+ }
+ else
+ data[index + 1] = value;
+ }
+
+ /**
+ * Determines the index in <code>#data</code> of a specific key.
+ *
+ * @param key
+ * the key to retrieve the index in <code>#data</code> of
+ * @return the index in <code>#data</code> of the specified <code>key</code>
+ * if it is contained; <tt>-1</tt> if <code>key</code> is not
+ * contained in <code>#data</code>
+ */
+ private int dataIndexOf(Object key)
+ {
+ if (data != null)
+ for (int index = 0; index < data.length; index += 2)
+ if (key.equals(data[index]))
+ return index;
+ return -1;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/gui/GuiActivator.java b/src/net/java/sip/communicator/impl/gui/GuiActivator.java
index 782465d..0e65b4e 100644
--- a/src/net/java/sip/communicator/impl/gui/GuiActivator.java
+++ b/src/net/java/sip/communicator/impl/gui/GuiActivator.java
@@ -16,6 +16,7 @@ import net.java.sip.communicator.service.browserlauncher.*;
import net.java.sip.communicator.service.callhistory.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.contactlist.*;
+import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.desktop.*;
import net.java.sip.communicator.service.fileaccess.*;
import net.java.sip.communicator.service.gui.*;
@@ -71,6 +72,8 @@ public class GuiActivator implements BundleActivator
private static MediaService mediaService;
+ private static List<ContactSourceService> contactSources;
+
private static final Map<Object, ProtocolProviderFactory>
providerFactoriesMap = new Hashtable<Object, ProtocolProviderFactory>();
@@ -549,6 +552,44 @@ public class GuiActivator implements BundleActivator
}
/**
+ * Returns a list of all registered contact sources.
+ * @return a list of all registered contact sources
+ */
+ public static List<ContactSourceService> getContactSources()
+ {
+ if (contactSources != null)
+ return contactSources;
+
+ contactSources = new Vector<ContactSourceService>();
+
+ ServiceReference[] serRefs = null;
+ try
+ {
+ // get all registered provider factories
+ serRefs =
+ bundleContext.getServiceReferences(
+ ContactSourceService.class.getName(), null);
+ }
+ catch (InvalidSyntaxException e)
+ {
+ logger.error("GuiActivator : " + e);
+ }
+
+ if (serRefs != null)
+ {
+ for (int i = 0; i < serRefs.length; i++)
+ {
+ ContactSourceService contactSource
+ = (ContactSourceService) bundleContext
+ .getService(serRefs[i]);
+
+ contactSources.add(contactSource);
+ }
+ }
+ return contactSources;
+ }
+
+ /**
* Sets the <tt>contactList</tt> component currently used to show the
* contact list.
* @param list the contact list object to set
diff --git a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java
index 6cca385..6c5815b 100755
--- a/src/net/java/sip/communicator/impl/gui/main/MainFrame.java
+++ b/src/net/java/sip/communicator/impl/gui/main/MainFrame.java
@@ -39,7 +39,7 @@ import net.java.sip.communicator.util.swing.*;
import org.osgi.framework.*;
/**
- * The main application window. This class is the core of this ui
+ * The main application window. This class is the core of this UI
* implementation. It stores all available protocol providers and their
* operation sets, as well as all registered accounts, the
* <tt>MetaContactListService</tt> and all sent messages that aren't
@@ -166,10 +166,20 @@ public class MainFrame
*/
this.addWindowListener(new WindowAdapter()
{
+ /**
+ * Invoked when a window has been closed.
+ */
public void windowClosed(WindowEvent event)
{
MainFrame.this.windowClosed(event);
}
+ /**
+ * Invoked when a window has been opened.
+ */
+ public void windowOpened(WindowEvent e)
+ {
+ GuiActivator.getContactList().requestFocusInWindow();
+ }
});
this.initTitleFont();
@@ -223,9 +233,11 @@ public class MainFrame
northPanel.add(accountStatusPanel, BorderLayout.CENTER);
- TransparentPanel searchPanel = new TransparentPanel(new BorderLayout());
+ TransparentPanel searchPanel
+ = new TransparentPanel(new BorderLayout(2, 0));
searchPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
searchPanel.add(searchField);
+ searchPanel.add(new CallHistoryButton(), BorderLayout.EAST);
centerPanel.add(searchPanel, BorderLayout.NORTH);
centerPanel.add(contactListPanel, BorderLayout.CENTER);
@@ -359,7 +371,7 @@ public class MainFrame
presence.addProviderPresenceStatusListener(
new GUIProviderPresenceStatusListener());
presence.addContactPresenceStatusListener(
- contactListPanel.getContactList());
+ TreeContactList.presenceFilter);
}
// Obtain the basic instant messaging operation set.
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java b/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java
new file mode 100644
index 0000000..909dec8
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java
@@ -0,0 +1,177 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.call;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.util.swing.*;
+
+/**
+ * The <tt>CallHistoryButton</tt> is the button shown on the top of the contact
+ * list.
+ * @author Yana Stamcheva
+ */
+public class CallHistoryButton
+ extends SIPCommTextButton
+ implements MissedCallsListener
+{
+ /**
+ * The history icon.
+ */
+ private final Image historyImage
+ = ImageLoader.getImage(ImageLoader.CALL_HISTORY_BUTTON);
+
+ /**
+ * The history pressed icon.
+ */
+ private final Image pressedImage
+ = ImageLoader.getImage(ImageLoader.CALL_HISTORY_BUTTON_PRESSED);
+
+ /**
+ * Indicates if the history is visible.
+ */
+ private boolean isHistoryVisible = false;
+
+ /**
+ * Indicates if this button currently shows the number of missed calls or
+ * the just the history icon.
+ */
+ private boolean isMissedCallView = false;
+
+ /**
+ * The tool tip shown when there are missed calls.
+ */
+ private final static String missedCallsToolTip
+ = GuiActivator.getResources().getI18NString(
+ "service.gui.MISSED_CALLS_TOOL_TIP");
+
+ /**
+ * The tool tip shown by default over the history button.
+ */
+ private final static String callHistoryToolTip
+ = GuiActivator.getResources().getI18NString(
+ "service.gui.CALL_HISTORY_TOOL_TIP");
+
+ /**
+ * The tool tip shown when we're in history view.
+ */
+ private final static String showContactListToolTip
+ = GuiActivator.getResources().getI18NString(
+ "service.gui.SHOW_CONTACT_LIST_TOOL_TIP");
+
+ /**
+ * Creates a <tt>CallHistoryButton</tt>.
+ */
+ public CallHistoryButton()
+ {
+ super("");
+
+ this.setBgImage(historyImage);
+
+ CallManager.setMissedCallsListener(this);
+
+ this.setPreferredSize(new Dimension(29, 22));
+ this.setForeground(Color.WHITE);
+ this.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+ this.setFont(getFont().deriveFont(Font.BOLD, 10f));
+ this.setToolTipText(callHistoryToolTip);
+ this.setBackground(new Color(255, 255, 255, 160));
+
+ this.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ if (isHistoryVisible && !isMissedCallView)
+ {
+ TreeContactList.searchFilter
+ .setSearchSourceType(SearchFilter.DEFAULT_SOURCE);
+ GuiActivator.getContactList()
+ .setDefaultFilter(TreeContactList.presenceFilter);
+ GuiActivator.getContactList().applyFilter(
+ TreeContactList.presenceFilter);
+
+ isHistoryVisible = false;
+ }
+ else
+ {
+ TreeContactList.searchFilter
+ .setSearchSourceType(SearchFilter.HISTORY_SOURCE);
+ GuiActivator.getContactList()
+ .setDefaultFilter(TreeContactList.historyFilter);
+ GuiActivator.getContactList()
+ .applyFilter(TreeContactList.historyFilter);
+
+ CallManager.clearMissedCalls();
+
+ isHistoryVisible = true;
+ }
+ setHistoryView();
+
+ GuiActivator.getContactList().requestFocusInWindow();
+ repaint();
+ }
+ });
+ }
+
+ /**
+ * Indicates that missed calls count has changed.
+ * @param newCallCount the new call count
+ */
+ public void missedCallCountChanged(int newCallCount)
+ {
+ if (newCallCount > 0)
+ {
+ setMissedCallsView(newCallCount);
+ }
+ else if (newCallCount <= 0)
+ {
+ setHistoryView();
+ }
+
+ this.revalidate();
+ this.repaint();
+ }
+
+ /**
+ * Sets the history view.
+ */
+ private void setHistoryView()
+ {
+ isMissedCallView = false;
+
+ if (isHistoryVisible)
+ {
+ setBgImage(pressedImage);
+ setToolTipText(showContactListToolTip);
+ }
+ else
+ {
+ setBgImage(historyImage);
+ setToolTipText(callHistoryToolTip);
+ }
+ setText("");
+ }
+
+ /**
+ * Sets the missed calls view of this button.
+ * @param callCount the missed calls count
+ */
+ private void setMissedCallsView(int callCount)
+ {
+ isMissedCallView = true;
+ this.setBgImage(null);
+ this.setToolTipText(missedCallsToolTip);
+ this.setBackground(new Color(200, 0, 0));
+ this.setText(new Integer(callCount).toString());
+ }
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java
index 7d242d6..8b5b117 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java
@@ -40,6 +40,17 @@ public class CallManager
= new Hashtable<Call, CallDialog>();
/**
+ * Indicates the number of missed calls that the user should be notified
+ * about.
+ */
+ private static int missedCalls = 0;
+
+ /**
+ * Listener notified for changes in missed calls count.
+ */
+ private static MissedCallsListener missedCallsListener;
+
+ /**
* A call listener.
*/
public static class GuiCallListener implements CallListener
@@ -214,7 +225,7 @@ public class CallManager
ProtocolProviderService telProvider = null;
int status = 0;
- Vector<ProtocolProviderService> telProviders = getTelephonyProviders();
+ List<ProtocolProviderService> telProviders = getTelephonyProviders();
for (ProtocolProviderService provider : telProviders)
{
@@ -398,10 +409,10 @@ public class CallManager
* Returns a list of all currently registered telephony providers.
* @return a list of all currently registered telephony providers
*/
- public static Vector<ProtocolProviderService> getTelephonyProviders()
+ public static List<ProtocolProviderService> getTelephonyProviders()
{
- Vector<ProtocolProviderService> telephonyProviders
- = new Vector<ProtocolProviderService>();
+ List<ProtocolProviderService> telephonyProviders
+ = new LinkedList<ProtocolProviderService>();
for (ProtocolProviderFactory providerFactory : GuiActivator
.getProtocolProviderFactories().values())
@@ -429,6 +440,44 @@ public class CallManager
}
/**
+ * Sets the given <tt>MissedCallsListener</tt> that would be notified on
+ * any changes in missed calls count.
+ * @param l the listener to set
+ */
+ public static void setMissedCallsListener(MissedCallsListener l)
+ {
+ missedCallsListener = l;
+ }
+
+ /**
+ * Adds a missed call.
+ */
+ public static void addMissedCall()
+ {
+ missedCalls ++;
+ fireMissedCallCountChangeEvent(missedCalls);
+ }
+
+ /**
+ * Clears the count of missed calls. Sets it to 0.
+ */
+ public static void clearMissedCalls()
+ {
+ missedCalls = 0;
+ }
+
+ /**
+ * Notifies interested <tt>MissedCallListener</tt> that the count has
+ * changed.
+ * @param missedCallsCount the new missed calls count
+ */
+ private static void fireMissedCallCountChangeEvent(int missedCallsCount)
+ {
+ if (missedCallsListener != null)
+ missedCallsListener.missedCallCountChanged(missedCallsCount);
+ }
+
+ /**
* Creates a call from a given Contact or a given String.
*/
private static class CreateCallThread
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java b/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java
index f5a3f5a..34b08b7 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java
@@ -16,13 +16,13 @@ import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.customcontrols.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
-import net.java.sip.communicator.service.contactlist.*;
+import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
/**
- * A TransferHandler that we use to handle dropping of <tt>MetaContact</tt>s or
+ * A TransferHandler that we use to handle dropping of <tt>UIContact</tt>s or
* simple string addresses to an existing <tt>Call</tt>. Dropping of a such data
* in the <tt>CallDialog</tt> would result in the creation of a call conference.
*
@@ -31,9 +31,21 @@ import net.java.sip.communicator.util.swing.*;
public class CallTransferHandler
extends ExtendedTransferHandler
{
+ /**
+ * The data flavor used when transferring <tt>UIContact</tt>s.
+ */
+ protected static final DataFlavor uiContactDataFlavor
+ = new DataFlavor(UIContact.class, "UIContact");
+
+ /**
+ * The logger.
+ */
private static final Logger logger
= Logger.getLogger(CallTransferHandler.class);
+ /**
+ * The call corresponding to the transfer.
+ */
private final Call call;
/**
@@ -63,7 +75,7 @@ public class CallTransferHandler
for (int i = 0, n = flavor.length; i < n; i++)
{
if (flavor[i].equals(DataFlavor.stringFlavor)
- || flavor[i].equals(metaContactDataFlavor))
+ || flavor[i].equals(uiContactDataFlavor))
{
if (comp instanceof JPanel)
{
@@ -89,13 +101,13 @@ public class CallTransferHandler
*/
public boolean importData(JComponent comp, Transferable t)
{
- if (t.isDataFlavorSupported(metaContactDataFlavor))
+ if (t.isDataFlavorSupported(uiContactDataFlavor))
{
Object o = null;
try
{
- o = t.getTransferData(metaContactDataFlavor);
+ o = t.getTransferData(uiContactDataFlavor);
}
catch (UnsupportedFlavorException e)
{
@@ -110,16 +122,29 @@ public class CallTransferHandler
if (o instanceof ContactNode)
{
- MetaContact metaContact = ((ContactNode) o).getMetaContact();
+ UIContact uiContact
+ = ((ContactNode) o).getContactDescriptor();
+
ProtocolProviderService callProvider
= call.getProtocolProvider();
- Iterator<Contact> contacts
- = metaContact.getContactsForProvider(callProvider);
+ Iterator<UIContactDetail> contactDetails
+ = uiContact.getContactDetailsForOperationSet(
+ OperationSetBasicTelephony.class).iterator();
String callee = null;
- if (contacts.hasNext())
- callee = contacts.next().getAddress();
+ while (contactDetails.hasNext())
+ {
+ UIContactDetail detail = contactDetails.next();
+
+ ProtocolProviderService detailProvider
+ = detail.getPreferredProtocolProvider(
+ OperationSetBasicTelephony.class);
+
+ if (detailProvider != null
+ && detailProvider.equals(callProvider))
+ callee = detail.getAddress();
+ }
if (callee != null)
{
@@ -137,7 +162,7 @@ public class CallTransferHandler
new String[]{
callProvider.getAccountID().getService(),
callProvider.getAccountID().getUserID(),
- metaContact.getDisplayName()}))
+ uiContact.getDisplayName()}))
.showDialog();
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/ChooseCallAccountPopupMenu.java b/src/net/java/sip/communicator/impl/gui/main/call/ChooseCallAccountPopupMenu.java
index 7ae58b4..992b174 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/ChooseCallAccountPopupMenu.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/ChooseCallAccountPopupMenu.java
@@ -15,6 +15,7 @@ import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.protocol.*;
@@ -28,6 +29,9 @@ import net.java.sip.communicator.service.protocol.*;
public class ChooseCallAccountPopupMenu
extends JPopupMenu
{
+ /**
+ * The invoker component.
+ */
private final JComponent invoker;
/**
@@ -64,8 +68,8 @@ public class ChooseCallAccountPopupMenu
for (Object o : telephonyObjects)
{
- if (o instanceof Contact)
- this.addTelephonyContactItem((Contact) o);
+ if (o instanceof UIContactDetail)
+ this.addTelephonyContactItem((UIContactDetail) o);
else if (o instanceof ChatTransport)
this.addTelephonyChatTransportItem((ChatTransport) o);
}
@@ -116,7 +120,7 @@ public class ChooseCallAccountPopupMenu
* telephony contact.
* @param telephonyContact the telephony contact to add
*/
- private void addTelephonyContactItem(final Contact telephonyContact)
+ private void addTelephonyContactItem(final UIContactDetail telephonyContact)
{
final ContactMenuItem contactItem
= new ContactMenuItem(telephonyContact);
@@ -125,8 +129,10 @@ public class ChooseCallAccountPopupMenu
{
public void actionPerformed(ActionEvent e)
{
- CallManager.createCall( telephonyContact.getProtocolProvider(),
- telephonyContact);
+ CallManager.createCall(
+ telephonyContact.getPreferredProtocolProvider(
+ OperationSetBasicTelephony.class),
+ telephonyContact.getAddress());
ChooseCallAccountPopupMenu.this.setVisible(false);
}
});
@@ -229,21 +235,26 @@ public class ChooseCallAccountPopupMenu
*/
private class ContactMenuItem extends JMenuItem
{
- private final Contact contact;
+ private final UIContactDetail contact;
- public ContactMenuItem(Contact contact)
+ public ContactMenuItem(UIContactDetail contact)
{
this.contact = contact;
this.setText(contact.getDisplayName());
- BufferedImage contactIcon
- = Constants.getStatusIcon(contact.getPresenceStatus());
+ BufferedImage contactIcon = null;
+ PresenceStatus status = contact.getPresenceStatus();
+
+ if (status != null)
+ contactIcon = Constants.getStatusIcon(status);
+ else
+ contactIcon = Constants.getStatusIcon(Constants.OFFLINE_STATUS);
if (contactIcon != null)
this.setIcon(new ImageIcon(contactIcon));
}
- public Contact getContact()
+ public UIContactDetail getContact()
{
return contact;
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/MissedCallsListener.java b/src/net/java/sip/communicator/impl/gui/main/call/MissedCallsListener.java
new file mode 100644
index 0000000..1c42239
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/call/MissedCallsListener.java
@@ -0,0 +1,23 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.call;
+
+/**
+ * The <tt>MissedCallsListener</tt> listens for changes in the missed calls
+ * count. It is notified each time when a missed calls is registered by the
+ * <tt>CallManager</tt>.
+ *
+ * @author Yana Stamcheva
+ */
+public interface MissedCallsListener
+{
+ /**
+ * Indicates the missed calls count has changed.
+ * @param newCallCount the new missed calls count
+ */
+ public void missedCallCountChanged(int newCallCount);
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/ReceivedCallDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/ReceivedCallDialog.java
index d293ff8..333fe74 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/ReceivedCallDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/ReceivedCallDialog.java
@@ -230,6 +230,8 @@ public class ReceivedCallDialog
if (sourceCall.equals(incomingCall))
{
+ CallManager.addMissedCall();
+
this.dispose();
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/ChatTransferHandler.java b/src/net/java/sip/communicator/impl/gui/main/chat/ChatTransferHandler.java
index c2fe947..c086590 100644
--- a/src/net/java/sip/communicator/impl/gui/main/chat/ChatTransferHandler.java
+++ b/src/net/java/sip/communicator/impl/gui/main/chat/ChatTransferHandler.java
@@ -17,6 +17,7 @@ import javax.swing.text.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.customcontrols.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
@@ -26,7 +27,7 @@ import net.java.sip.communicator.util.swing.*;
* A TransferHandler that we use to handle copying, pasting and DnD operations
* in our <tt>ChatPanel</tt>. The string handler is heavily inspired
* by Sun's <tt>DefaultTransferHandler</tt> with the main difference being that
- * we only accept pasting of plain text. We do this in order to avoid html
+ * we only accept pasting of plain text. We do this in order to avoid HTML
* support problems that appear when pasting formatted text into our editable
* area.
*
@@ -37,6 +38,12 @@ public class ChatTransferHandler
extends ExtendedTransferHandler
{
/**
+ * The data flavor used when transferring <tt>UIContact</tt>s.
+ */
+ protected static final DataFlavor uiContactDataFlavor
+ = new DataFlavor(UIContact.class, "UIContact");
+
+ /**
* This class logger.
*/
private final static Logger logger
@@ -74,7 +81,7 @@ public class ChatTransferHandler
{
for (int i = 0, n = flavor.length; i < n; i++)
{
- if (flavor[i].equals(metaContactDataFlavor))
+ if (flavor[i].equals(uiContactDataFlavor))
{
return true;
}
@@ -124,13 +131,13 @@ public class ChatTransferHandler
logger.debug("Failed to drop files.", e);
}
}
- else if (t.isDataFlavorSupported(metaContactDataFlavor))
+ else if (t.isDataFlavorSupported(uiContactDataFlavor))
{
Object o = null;
try
{
- o = t.getTransferData(metaContactDataFlavor);
+ o = t.getTransferData(uiContactDataFlavor);
}
catch (UnsupportedFlavorException e)
{
@@ -143,14 +150,18 @@ public class ChatTransferHandler
if (o instanceof ContactNode)
{
- MetaContact metaContact
- = ((ContactNode) o).getMetaContact();
+ UIContact uiContact
+ = ((ContactNode) o).getContactDescriptor();
+
+ // We only support drag&drop for MetaContacts for now.
+ if (!(uiContact instanceof MetaUIContact))
+ return false;
ChatTransport currentChatTransport
= chatPanel.getChatSession().getCurrentChatTransport();
- Iterator<Contact> contacts = metaContact
- .getContactsForProvider(
+ Iterator<Contact> contacts = ((MetaContact) uiContact
+ .getDescriptor()).getContactsForProvider(
currentChatTransport.getProtocolProvider());
String contact = null;
@@ -173,7 +184,7 @@ public class ChatTransferHandler
"service.gui.ERROR"),
GuiActivator.getResources().getI18NString(
"service.gui.CONTACT_NOT_SUPPORTING_CHAT_CONF",
- new String[]{metaContact.getDisplayName()}))
+ new String[]{uiContact.getDisplayName()}))
.showDialog();
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/CallHistoryFilter.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/CallHistoryFilter.java
new file mode 100644
index 0000000..f5e85af
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/CallHistoryFilter.java
@@ -0,0 +1,154 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.contactlist;
+
+import java.util.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
+import net.java.sip.communicator.service.contactsource.*;
+
+/**
+ * The <tt>CallHistoryFilter</tt> is a filter over the history contact sources.
+ *
+ * @author Yana Stamcheva
+ */
+public class CallHistoryFilter
+ implements ContactListFilter,
+ ContactQueryListener
+{
+ /**
+ * The <tt>ContactListTreeModel</tt>, where the results of this filter are
+ * stored
+ */
+ private ContactListTreeModel resultTreeModel;
+
+ /**
+ * The current <tt>ContactQuery</tt>.
+ */
+ private ContactQuery currentQuery;
+
+ /**
+ * Applies this filter and stores the result in the given <tt>treeModel</tt>.
+ *
+ * @param treeModel the <tt>ContactListTreeModel</tt>, where the results
+ * of this filter are stored
+ */
+ public void applyFilter(ContactListTreeModel treeModel)
+ {
+ this.resultTreeModel = treeModel;
+
+ Collection<ExternalContactSource> contactSources
+ = TreeContactList.getContactSources();
+
+ for (ExternalContactSource contactSource : contactSources)
+ {
+ ContactSourceService sourceService
+ = contactSource.getContactSourceService();
+
+ if (!sourceService.getIdentifier()
+ .equals(ContactSourceService.CALL_HISTORY))
+ continue;
+
+ // We're in a case of call history contact source.
+ currentQuery = sourceService.queryContactSource("");
+
+ // Add first available results.
+ this.addMatching( currentQuery.getQueryResults(),
+ contactSource);
+
+ currentQuery.addContactQueryListener(this);
+ }
+ }
+
+ public boolean isMatching(UIContact uiContact)
+ {
+ return false;
+ }
+
+ public boolean isMatching(UIGroup uiGroup)
+ {
+ return false;
+ }
+
+ /**
+ * Adds matching <tt>sourceContacts</tt> to the result tree model.
+ * @param sourceContacts the list of <tt>SourceContact</tt>s to add
+ * @param uiSource the <tt>ExternalContactSource</tt>, which contacts
+ * we're adding
+ */
+ private void addMatching( List<SourceContact> sourceContacts,
+ ExternalContactSource uiSource)
+ {
+ Iterator<SourceContact> contactsIter = sourceContacts.iterator();
+
+ while (contactsIter.hasNext())
+ {
+ addHistoryContact(contactsIter.next(), uiSource);
+ }
+ }
+
+ /**
+ * Indicates that a contact has been received for a query.
+ * @param event the <tt>ContactReceivedEvent</tt> that notified us
+ */
+ public void contactReceived(ContactReceivedEvent event)
+ {
+ synchronized (resultTreeModel)
+ {
+ ExternalContactSource sourceUI
+ = TreeContactList.getContactSource(
+ event.getQuerySource().getContactSource());
+
+ addHistoryContact(event.getContact(), sourceUI);
+ }
+ }
+
+ /**
+ * Indicates that the query status has changed.
+ * @param event the <tt>ContactQueryStatusEvent</tt> that notified us
+ */
+ public void queryStatusChanged(ContactQueryStatusEvent event)
+ {
+ int eventType = event.getEventType();
+
+ // Remove the current query when it's stopped for some reason.
+ // QUERY_COMPLETED, QUERY_COMPLETED, QUERY_ERROR
+ currentQuery = null;
+
+ if (eventType == ContactQueryStatusEvent.QUERY_ERROR)
+ {
+ //TODO: Show the error to the user??
+ }
+
+ event.getQuerySource().removeContactQueryListener(this);
+ }
+
+ /**
+ * Adds the given <tt>sourceContact</tt> to the contact list.
+ * @param sourceContact the <tt>SourceContact</tt> to add
+ * @param uiSource the UI adapter for the original contact source
+ */
+ private void addHistoryContact( SourceContact sourceContact,
+ ExternalContactSource uiSource)
+ {
+ GuiActivator.getContactList()
+ .addContact(resultTreeModel,
+ uiSource.getUIContact(sourceContact),
+ false,
+ false);
+ }
+
+ /**
+ * Stops this filter current queries.
+ */
+ public void stopFilter()
+ {
+ if (currentQuery != null)
+ currentQuery.cancel();
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java
index 73ca46c..c713ad6 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactList.java
@@ -66,7 +66,7 @@ public class ContactList
private GroupRightButtonMenu groupRightButtonMenu;
- private ContactRightButtonMenu contactRightButtonMenu;
+ private MetaContactRightButtonMenu contactRightButtonMenu;
/**
* A list of all contacts that are currently "active". An "active" contact
@@ -391,15 +391,16 @@ public class ContactList
* <tt>ContactListListener</tt>s that a contact is selected.
*
* @param sourceContact the contact that this event is about
- * @param protocolContact the protocol contact the this event is about
* @param eventID the id indicating the exact type of the event to fire.
+ * @param clickCount
*/
- public void fireContactListEvent(MetaContact sourceContact,
- Contact protocolContact, int eventID)
+ public void fireContactListEvent( MetaContact sourceContact,
+ int eventID,
+ int clickCount)
{
fireContactListEvent(
contactListListeners,
- new ContactListEvent(sourceContact, protocolContact, eventID));
+ new ContactListEvent(sourceContact, eventID, clickCount));
}
/**
@@ -420,11 +421,8 @@ public class ContactList
case ContactListEvent.CONTACT_CLICKED:
listener.contactClicked(event);
break;
- case ContactListEvent.PROTOCOL_CONTACT_CLICKED:
- listener.protocolContactClicked(event);
- break;
case ContactListEvent.GROUP_CLICKED:
- listener.groupSelected(event);
+ listener.groupClicked(event);
break;
default:
logger.error("Unknown event type " + event.getEventID());
@@ -542,10 +540,6 @@ public class ContactList
{
MetaContact contact = (MetaContact) selectedValue;
- // get the component under the mouse
- Component component = this.getHorizontalComponent(renderer,
- translatedX);
-
// Right click and Ctrl+LeftClick on the contact label opens
// Popup menu
if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0
@@ -630,40 +624,6 @@ public class ContactList
}
/**
- * Runs the info window for the specified contact at the appropriate
- * position.
- */
- private class RunInfoWindow
- implements Runnable
- {
- private final MetaContact contactItem;
-
- private final Point p;
-
- private RunInfoWindow(Point p, MetaContact contactItem)
- {
-
- this.p = p;
- this.contactItem = contactItem;
- }
-
- public void run()
- {
- ContactInfoDialog contactInfoPanel
- = new ContactInfoDialog(mainFrame, contactItem);
-
- SwingUtilities.convertPointToScreen(p, ContactList.this);
-
- // TODO: to calculate popup window position properly.
- contactInfoPanel.setPopupLocation(p.x - 140, p.y - 15);
-
- contactInfoPanel.setVisible(true);
-
- contactInfoPanel.requestFocusInWindow();
- }
- }
-
- /**
* Takes care of keeping the contact list up to date.
*/
private class ContactListRefresh
@@ -977,7 +937,7 @@ public class ContactList
*
* @return the right button menu for a contact
*/
- public ContactRightButtonMenu getContactRightButtonMenu()
+ public MetaContactRightButtonMenu getContactRightButtonMenu()
{
return contactRightButtonMenu;
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListEvent.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListEvent.java
index ac17ae8..986c520 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListEvent.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListEvent.java
@@ -8,12 +8,14 @@ package net.java.sip.communicator.impl.gui.main.contactlist;
import java.util.*;
-import net.java.sip.communicator.service.contactlist.*;
-import net.java.sip.communicator.service.protocol.*;
-
+/**
+ * The <tt>ContactListEvent</tt> is triggered when a contact or a group is
+ * clicked in the contact list.
+ * @author Yana Stamcheva
+ */
public class ContactListEvent
- extends EventObject{
-
+ extends EventObject
+{
private int eventID = -1;
/**
@@ -23,16 +25,10 @@ public class ContactListEvent
public static final int CONTACT_CLICKED = 1;
/**
- * Indicates that the ContactListEvent instance was triggered by
- * selecting a protocol contact in the contact list.
- */
- public static final int PROTOCOL_CONTACT_CLICKED = 2;
-
- /**
* Indicates that the ContactListEvent instance was triggered by selecting
* a group in the contact list.
*/
- public static final int GROUP_CLICKED = 3;
+ public static final int GROUP_CLICKED = 2;
/**
* Indicated the number of click accompanying the event
@@ -40,12 +36,6 @@ public class ContactListEvent
private int clickCount;
/**
- * Specific <tt>Contact</tt> of this <tt>MetaContact</tt> involved in
- * the event if any, null otherwise.
- */
- private Contact sourceProtoContact;
-
- /**
* Creates a new ContactListEvent according to the specified parameters.
* @param source the MetaContact which was selected
* @param eventID one of the XXX_SELECTED static fields indicating the
@@ -56,27 +46,12 @@ public class ContactListEvent
public ContactListEvent(Object source, int eventID, int clickCount)
{
super(source);
-
+
this.eventID = eventID;
this.clickCount = clickCount;
}
/**
- * Creates a new ContactListEvent according to the specified parameters.
- * @param source the MetaContact which was selected
- * @param protocolContact the protocol specifique contact which was selected
- * @param eventID one of the XXX_SELECTED static fields indicating the
- * nature of the event.
- */
- public ContactListEvent(Object source,
- Contact protocolContact, int eventID)
- {
- super(source);
- this.eventID = eventID;
- this.sourceProtoContact = protocolContact;
- }
-
- /**
* Returns an event id specifying whether the type of this event
* (CONTACT_SELECTED or PROTOCOL_CONTACT_SELECTED)
* @return one of the XXX_SELECTED int fields of this class.
@@ -87,36 +62,27 @@ public class ContactListEvent
}
/**
- * Returns the MetaContact for which this event occured.
- * @return the MetaContact for which this event occured
+ * Returns the <tt>UIContactDescriptor</tt> for which this event occured.
+ * @return the </tt>UIContactDescriptor</tt> for which this event occured
*/
- public MetaContact getSourceContact()
+ public UIContact getSourceContact()
{
- if(getSource() instanceof MetaContact)
- return (MetaContact)getSource();
-
+ if(getSource() instanceof UIContact)
+ return (UIContact) getSource();
+
return null;
}
/**
- * Returns the MetaContactGroup for which this event occured.
- * @return the MetaContactGroup for which this event occured
+ * Returns the <tt>UIGroupDescriptor</tt> for which this event occured.
+ * @return the <tt>UIGroupDescriptor</tt> for which this event occured
*/
- public MetaContactGroup getSourceGroup()
+ public UIGroup getSourceGroup()
{
- if(getSource() instanceof MetaContactGroup)
- return (MetaContactGroup)getSource();
-
- return null;
- }
+ if(getSource() instanceof UIGroup)
+ return (UIGroup) getSource();
- /**
- * Returns the protocol contact for which this event occured.
- * @return the protocol contact for which this event occured
- */
- public Contact getSourceProtoContact()
- {
- return sourceProtoContact;
+ return null;
}
/**
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListFilter.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListFilter.java
index 6dfd34c..3cd9156 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListFilter.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListFilter.java
@@ -6,33 +6,41 @@
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
-import net.java.sip.communicator.service.contactlist.*;
-
/**
* The <tt>ContactListFilter</tt> is an interface meant to be implemented by
* modules interested in filtering the contact list. An implementation of this
- * interface should be able answer if a <tt>MetaContact</tt> or a
- * <tt>MetaContactGroup</tt> is matching the corresponding filter.
+ * interface should be able to answer if an <tt>UIContact</tt> or an
+ * <tt>UIGroup</tt> is matching the corresponding filter.
*
* @author Yana Stamcheva
*/
public interface ContactListFilter
{
/**
- * Returns <tt>true</tt> if the given <tt>metaContact</tt> is matching this
- * filter, otherwise returns <tt>false</tt>
- * @param metaContact the <tt>MetaContact</tt> to check
- * @return <tt>true</tt> if the given <tt>metaContact</tt> is matching this
- * filter, otherwise returns <tt>false</tt>
+ * Indicates if the given <tt>uiGroup</tt> is matching the current filter.
+ * @param uiContact the <tt>UIContact</tt> to check
+ * @return <tt>true</tt> to indicate that the given <tt>uiContact</tt>
+ * matches this filter, <tt>false</tt> - otherwise
+ */
+ public boolean isMatching(UIContact uiContact);
+
+ /**
+ * Indicates if the given <tt>uiGroup</tt> is matching the current filter.
+ * @param uiGroup the <tt>UIGroup</tt> to check
+ * @return <tt>true</tt> to indicate that the given <tt>uiGroup</tt>
+ * matches this filter, <tt>false</tt> - otherwise
+ */
+ public boolean isMatching(UIGroup uiGroup);
+
+ /**
+ * Applies this filter to any interested sources and stores the result in
+ * the given <tt>treeModel</tt>.
+ * @param treeModel the <tt>ContactListTreeModel</tt> to store the result in
*/
- public boolean isMatching(MetaContact metaContact);
+ public void applyFilter(ContactListTreeModel treeModel);
/**
- * Returns <tt>true</tt> if the given <tt>metaGroup</tt> is matching this
- * filter, otherwise returns <tt>false</tt>
- * @param metaGroup the <tt>MetaContactGroup</tt> to check
- * @return <tt>true</tt> if the given <tt>metaGroup</tt> is matching this
- * filter, otherwise returns <tt>false</tt>
+ * Stops this filter current queries.
*/
- public boolean isMatching(MetaContactGroup metaGroup);
+ public void stopFilter();
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListListener.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListListener.java
index 5706fcc..b936727 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListListener.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListListener.java
@@ -9,28 +9,24 @@ package net.java.sip.communicator.impl.gui.main.contactlist;
import java.util.*;
/**
- * Listens for events coming from the contact list.
- *
+ * Listens for events coming from mouse events over the contact list. For
+ * example a contact been clicked or a group been selected.
+ *
* @author Yana Stamcheva
*/
public interface ContactListListener extends EventListener
{
/**
- *
- * @param evt
+ * Indicates that a group has been selected.
+ * @param evt the <tt>ContactListEvent</tt> that has been triggered from
+ * the user selection
*/
- public void groupSelected(ContactListEvent evt);
-
+ public void groupClicked(ContactListEvent evt);
+
/**
- *
- * @param evt
+ * Indicates that a contact has been clicked.
+ * @param evt the <tt>ContactListEvent</tt> that has been triggered from
+ * the user click
*/
public void contactClicked(ContactListEvent evt);
-
- /**
- *
- * @param evt
- */
- public void protocolContactClicked(ContactListEvent evt);
-
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListNode.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListNode.java
index 4798042..9452c15 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListNode.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListNode.java
@@ -7,9 +7,9 @@
package net.java.sip.communicator.impl.gui.main.contactlist;
/**
- * The <tt>ContactListNode</tt> represents a node in the contact list. An
- * implementation of this interface should be able to determine the index of
- * this node in the <tt>MetaContactListService</tt>.
+ * The <tt>ContactListNode</tt> represents a node in the contact list data
+ * model. An implementation of this interface should be able to determine the
+ * index of this node in its contact source.
*
* @author Yana Stamcheva
*/
@@ -19,5 +19,5 @@ public interface ContactListNode
* Returns the index of this node in the <tt>MetaContactListService</tt>.
* @return the index of this node in the <tt>MetaContactListService</tt>
*/
- public int getMetaContactListIndex();
+ public int getSourceIndex();
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListPane.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListPane.java
index f79fb92..5b27c3e 100755
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListPane.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListPane.java
@@ -92,9 +92,21 @@ public class ContactListPane
public void initList(MetaContactListService contactListService)
{
this.contactList = new TreeContactList();
-
+ // We should first set the contact list to the GuiActivator, so that
+ // anybody could get it from there.
GuiActivator.setContactList(contactList);
+ // By default we set the current filter to be the presence filter.
+ new Thread()
+ {
+ public void run()
+ {
+ TreeContactList.presenceFilter
+ .setShowOffline(ConfigurationManager.isShowOffline());
+ contactList.applyFilter(TreeContactList.presenceFilter);
+ }
+ }.start();
+
TransparentPanel transparentPanel
= new TransparentPanel(new BorderLayout());
@@ -146,7 +158,13 @@ public class ContactListPane
if (evt.getClickCount() < 2)
return;
- MetaContact metaContact = evt.getSourceContact();
+ UIContact descriptor = evt.getSourceContact();
+
+ // We're currently only interested in MetaContacts.
+ if (!(descriptor.getDescriptor() instanceof MetaContact))
+ return;
+
+ MetaContact metaContact = (MetaContact) descriptor.getDescriptor();
// Searching for the right proto contact to use as default for the
// chat conversation.
@@ -200,24 +218,10 @@ public class ContactListPane
* Implements the ContactListListener.groupSelected method.
* @param evt the <tt>ContactListEvent</tt> that notified us
*/
- public void groupSelected(ContactListEvent evt)
+ public void groupClicked(ContactListEvent evt)
{}
/**
- * Implements the ContactListListener.protocolContactSelected method.
- * @param evt the <tt>ContactListEvent</tt> that notified us
- */
- public void protocolContactClicked(ContactListEvent evt)
- {
- Contact protoContact = evt.getSourceProtoContact();
-
- ContactEventHandler contactHandler = mainFrame
- .getContactHandler(protoContact.getProtocolProvider());
-
- contactHandler.contactClicked(protoContact, evt.getClickCount());
- }
-
- /**
* When a message is received determines whether to open a new chat window
* or chat window tab, or to indicate that a message is received from a
* contact which already has an open chat. When the chat is found checks if
@@ -660,26 +664,6 @@ public class ContactListPane
}
}
- /**
- * Opens chat window when the selected value is a MetaContact and opens a
- * group when the selected value is a MetaContactGroup.
- */
- private class ContactListPanelEnterAction extends AbstractAction
- {
- public void actionPerformed(ActionEvent e)
- {
- Object selectedValue
- = contactList.getSelectionPath().getLastPathComponent();
-
- if (selectedValue instanceof MetaContact)
- {
- MetaContact contact = (MetaContact) selectedValue;
-
- chatWindowManager.startChat(contact);
- }
- }
- }
-
private void initPluginComponents()
{
// Search for plugin components registered through the OSGI bundle
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTransferHandler.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTransferHandler.java
index 82e56b2..1e6f799 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTransferHandler.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTransferHandler.java
@@ -15,6 +15,7 @@ import javax.swing.tree.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
@@ -28,9 +29,21 @@ import net.java.sip.communicator.util.swing.*;
public class ContactListTransferHandler
extends ExtendedTransferHandler
{
+ /**
+ * The data flavor used when transferring <tt>UIContact</tt>s.
+ */
+ protected static final DataFlavor uiContactDataFlavor
+ = new DataFlavor(UIContact.class, "UIContact");
+
+ /**
+ * The logger.
+ */
private static final Logger logger
= Logger.getLogger(ContactListTransferHandler.class);
+ /**
+ * The contact list, where this transfer happens.
+ */
private final DefaultTreeContactList contactList;
/**
@@ -81,7 +94,7 @@ public class ContactListTransferHandler
{
for (int i = 0, n = flavor.length; i < n; i++)
{
- if (flavor[i].equals(metaContactDataFlavor))
+ if (flavor[i].equals(uiContactDataFlavor))
{
return true;
}
@@ -140,13 +153,13 @@ public class ContactListTransferHandler
}
}
}
- else if (t.isDataFlavorSupported(metaContactDataFlavor))
+ else if (t.isDataFlavorSupported(uiContactDataFlavor))
{
Object o = null;
try
{
- o = t.getTransferData(metaContactDataFlavor);
+ o = t.getTransferData(uiContactDataFlavor);
}
catch (UnsupportedFlavorException e)
{
@@ -162,8 +175,13 @@ public class ContactListTransferHandler
if (o instanceof ContactNode
&& comp instanceof TreeContactList)
{
- MetaContact transferredContact
- = ((ContactNode) o).getMetaContact();
+ UIContact transferredContact
+ = ((ContactNode) o).getContactDescriptor();
+
+ // We support darg&drop for MetaContacts only.
+ if (!(transferredContact instanceof MetaUIContact))
+ return false;
+
TreeContactList list = (TreeContactList) comp;
Object dest
@@ -173,25 +191,36 @@ public class ContactListTransferHandler
{
if (dest instanceof ContactNode)
{
- MetaContact destContact
- = ((ContactNode) dest).getMetaContact();
+ UIContact destContact
+ = ((ContactNode) dest).getContactDescriptor();
+
+ // We support darg&drop for MetaContacts only for now.
+ if (!(destContact instanceof MetaUIContact))
+ return false;
+
if (transferredContact != destContact)
{
MetaContactListManager.moveMetaContactToMetaContact(
- transferredContact, destContact);
+ (MetaContact) transferredContact.getDescriptor(),
+ (MetaContact) destContact.getDescriptor());
}
return true;
}
else if (dest instanceof GroupNode)
{
- MetaContactGroup destGroup
- = ((GroupNode) dest).getMetaContactGroup();
+ UIGroup destGroup
+ = ((GroupNode) dest).getGroupDescriptor();
+
+ // We support darg&drop for MetaContacts only for now.
+ if (!(destGroup instanceof MetaUIGroup))
+ return false;
- if (transferredContact.getParentMetaContactGroup()
- != destGroup)
+ if (!transferredContact
+ .getParentGroup().equals(destGroup))
{
MetaContactListManager.moveMetaContactToGroup(
- transferredContact, destGroup);
+ (MetaContact) transferredContact.getDescriptor(),
+ (MetaContactGroup) destGroup.getDescriptor());
}
return true;
}
@@ -225,7 +254,7 @@ public class ContactListTransferHandler
contactList.getCellRenderer()
.getTreeCellRendererComponent(
contactList,
- transferable.getTransferData(metaContactDataFlavor),
+ transferable.getTransferData(uiContactDataFlavor),
true, // is selected
false, // is expanded
true, // is leaf
@@ -303,7 +332,7 @@ public class ContactListTransferHandler
*/
public DataFlavor[] getTransferDataFlavors()
{
- return new DataFlavor[] { metaContactDataFlavor,
+ return new DataFlavor[] { uiContactDataFlavor,
DataFlavor.stringFlavor};
}
@@ -316,7 +345,7 @@ public class ContactListTransferHandler
*/
public boolean isDataFlavorSupported(DataFlavor flavor)
{
- return metaContactDataFlavor.equals(flavor)
+ return uiContactDataFlavor.equals(flavor)
|| DataFlavor.stringFlavor.equals(flavor);
}
@@ -333,7 +362,7 @@ public class ContactListTransferHandler
throws UnsupportedFlavorException,
IOException
{
- if (metaContactDataFlavor.equals(flavor))
+ if (uiContactDataFlavor.equals(flavor))
{
return transferredObject;
}
@@ -341,7 +370,7 @@ public class ContactListTransferHandler
{
if (transferredObject instanceof ContactNode)
return ((ContactNode) transferredObject)
- .getMetaContact().getDisplayName();
+ .getContactDescriptor().getDisplayName();
}
else
throw new UnsupportedFlavorException(flavor);
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java
index de4271a..978cdef 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeCellRenderer.java
@@ -10,7 +10,6 @@ import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
-import java.util.*;
import java.util.List;
import javax.swing.*;
@@ -72,13 +71,6 @@ public class ContactListTreeCellRenderer
private static final int EXTENDED_AVATAR_WIDTH = 45;
/**
- * The key of the user data in <tt>MetaContact</tt> which specifies
- * the avatar cached from previous invocations.
- */
- private static final String AVATAR_DATA_KEY
- = ContactListCellRenderer.class.getName() + ".avatar";
-
- /**
* The icon used for opened groups.
*/
private final ImageIcon openedGroupIcon =
@@ -108,7 +100,7 @@ public class ContactListTreeCellRenderer
/**
* The status message label.
*/
- private final JLabel statusMessageLabel = new JLabel();
+ private final JLabel displayDetailsLabel = new JLabel();
/**
* The call button.
@@ -148,7 +140,7 @@ public class ContactListTreeCellRenderer
/**
* The icon showing the contact status.
*/
- protected final ImageIcon statusIcon = new ImageIcon();
+ protected ImageIcon statusIcon = new ImageIcon();
/**
* Indicates if the current list cell is selected.
@@ -194,8 +186,8 @@ public class ContactListTreeCellRenderer
this.setOpaque(false);
this.nameLabel.setOpaque(false);
- this.statusMessageLabel.setFont(getFont().deriveFont(9f));
- this.statusMessageLabel.setForeground(Color.GRAY);
+ this.displayDetailsLabel.setFont(getFont().deriveFont(9f));
+ this.displayDetailsLabel.setForeground(Color.GRAY);
this.rightLabel.setFont(rightLabel.getFont().deriveFont(9f));
this.rightLabel.setHorizontalAlignment(JLabel.RIGHT);
@@ -239,27 +231,56 @@ public class ContactListTreeCellRenderer
{
public void actionPerformed(ActionEvent e)
{
+ ChooseCallAccountPopupMenu chooseAccountDialog = null;
+
if (treeNode != null && treeNode instanceof ContactNode)
{
- List<Contact> telephonyContacts
- = ((ContactNode) treeNode).getMetaContact()
- .getContactsForOperationSet(
+ List<UIContactDetail> telephonyContacts
+ = ((ContactNode) treeNode).getContactDescriptor()
+ .getContactDetailsForOperationSet(
OperationSetBasicTelephony.class);
if (telephonyContacts.size() == 1)
{
- Contact contact = telephonyContacts.get(0);
- CallManager.createCall(
- telephonyContacts.get(0).getProtocolProvider(),
- contact);
+ UIContactDetail detail
+ = telephonyContacts.get(0);
+
+ ProtocolProviderService preferredProvider
+ = detail.getPreferredProtocolProvider(
+ OperationSetBasicTelephony.class);
+
+ if (preferredProvider != null)
+ CallManager.createCall(
+ preferredProvider,
+ detail.getAddress());
+ else
+ {
+ List<ProtocolProviderService> providers
+ = CallManager.getTelephonyProviders();
+
+ int providersCount = providers.size();
+
+ if (providersCount == 1)
+ CallManager.createCall(
+ providers.get(0),
+ detail.getAddress());
+ else if (providersCount > 1)
+ chooseAccountDialog
+ = new ChooseCallAccountPopupMenu(
+ tree, providers);
+ }
}
else if (telephonyContacts.size() > 1)
{
- ChooseCallAccountPopupMenu chooseAccountDialog
+ chooseAccountDialog
= new ChooseCallAccountPopupMenu(
tree,
telephonyContacts);
+ }
+ // If the choose dialog is created we're going to show it.
+ if (chooseAccountDialog != null)
+ {
Point location = new Point(callButton.getX(),
callButton.getY() + callButton.getHeight());
@@ -281,8 +302,16 @@ public class ContactListTreeCellRenderer
{
if (treeNode != null && treeNode instanceof ContactNode)
{
- GuiActivator.getUIService().getChatWindowManager()
- .startChat(((ContactNode) treeNode).getMetaContact());
+ UIContact contactDescriptor
+ = ((ContactNode) treeNode).getContactDescriptor();
+
+ if (contactDescriptor.getDescriptor()
+ instanceof MetaContact)
+ {
+ GuiActivator.getUIService().getChatWindowManager()
+ .startChat(
+ (MetaContact) contactDescriptor.getDescriptor());
+ }
}
}
});
@@ -318,9 +347,10 @@ public class ContactListTreeCellRenderer
if (value instanceof ContactNode)
{
- MetaContact metaContact = ((ContactNode) value).getMetaContact();
+ UIContact contact
+ = ((ContactNode) value).getContactDescriptor();
- String displayName = metaContact.getDisplayName();
+ String displayName = contact.getDisplayName();
if (displayName == null || displayName.length() < 1)
{
@@ -330,14 +360,13 @@ public class ContactListTreeCellRenderer
this.nameLabel.setText(displayName);
- if(contactList.isContactActive(metaContact))
+ if(contactList.isContactActive(contact))
{
statusIcon.setImage(msgReceivedImage);
}
else
{
- statusIcon.setImage(Constants.getStatusIcon(
- contactList.getMetaContactStatus(metaContact)));
+ statusIcon = contact.getStatusIcon();
}
this.statusLabel.setIcon(statusIcon);
@@ -348,30 +377,35 @@ public class ContactListTreeCellRenderer
// Initializes status message components if the given meta contact
// contains a status message.
- this.initStatusMessage(metaContact);
+ this.initDisplayDetails(contact);
- this.initButtonsPanel(metaContact);
+ this.initButtonsPanel(contact);
+
+ ImageIcon avatar = isSelected
+ ? contact.getAvatar(
+ isSelected, EXTENDED_AVATAR_WIDTH, EXTENDED_AVATAR_HEIGHT)
+ : contact.getAvatar(
+ isSelected, AVATAR_WIDTH, AVATAR_HEIGHT);
- ImageIcon avatar = getAvatar(metaContact);
if (avatar != null)
this.rightLabel.setIcon(avatar);
this.rightLabel.setText("");
- this.setToolTipText(metaContact.getMetaUID());
+ this.setToolTipText(contact.getDescriptor().toString());
}
else if (value instanceof GroupNode)
{
- MetaContactGroup groupItem
- = ((GroupNode) value).getMetaContactGroup();
+ UIGroup groupItem
+ = ((GroupNode) value).getGroupDescriptor();
- this.nameLabel.setText(groupItem.getGroupName());
+ this.nameLabel.setText(groupItem.getDisplayName());
this.nameLabel.setFont(this.getFont().deriveFont(Font.BOLD));
if (groupForegroundColor != null)
this.nameLabel.setForeground(groupForegroundColor);
- this.remove(statusMessageLabel);
+ this.remove(displayDetailsLabel);
this.remove(callButton);
this.remove(chatButton);
@@ -382,74 +416,18 @@ public class ContactListTreeCellRenderer
// We have no photo icon for groups.
this.rightLabel.setIcon(null);
- this.rightLabel.setText( groupItem.countOnlineChildContacts()
- + "/" + groupItem.countChildContacts());
+
+ if (groupItem.countChildContacts() >= 0)
+ this.rightLabel.setText( groupItem.countOnlineChildContacts()
+ + "/" + groupItem.countChildContacts());
- this.setToolTipText(groupItem.getMetaUID());
+ this.setToolTipText(groupItem.getDescriptor().toString());
}
return this;
}
/**
- * Gets the avatar of a specific <tt>MetaContact</tt> in the form of an
- * <tt>ImageIcon</tt> value.
- *
- * @param metaContact the <tt>MetaContact</tt> to retrieve the avatar of
- * @return an <tt>ImageIcon</tt> which represents the avatar of the
- * specified <tt>MetaContact</tt>
- */
- private ImageIcon getAvatar(MetaContact metaContact)
- {
- byte[] avatarBytes = metaContact.getAvatar(true);
- ImageIcon avatar = null;
-
- // If there'rs no avatar we have nothing more to do here.
- if((avatarBytes == null) || (avatarBytes.length <= 0))
- return null;
-
- // If the cell is selected we return a zoomed version of the avatar
- // image.
- if (isSelected)
- return ImageUtils.getScaledRoundedIcon(
- avatarBytes,
- EXTENDED_AVATAR_WIDTH,
- EXTENDED_AVATAR_HEIGHT);
-
- // In any other case try to get the avatar from the cache.
- Object[] avatarCache = (Object[]) metaContact.getData(AVATAR_DATA_KEY);
- if ((avatarCache != null) && (avatarCache[0] == avatarBytes))
- avatar = (ImageIcon) avatarCache[1];
-
- // Just
- int avatarWidth = AVATAR_WIDTH;
- int avatarHeight = AVATAR_HEIGHT;
-
- // If the avatar isn't available or it's not up-to-date, create it.
- if (avatar == null)
- avatar = ImageUtils.getScaledRoundedIcon(
- avatarBytes,
- avatarWidth,
- avatarHeight);
-
- // Cache the avatar in case it has changed.
- if (avatarCache == null)
- {
- if (avatar != null)
- metaContact.setData(
- AVATAR_DATA_KEY,
- new Object[] { avatarBytes, avatar });
- }
- else
- {
- avatarCache[0] = avatarBytes;
- avatarCache[1] = avatar;
- }
-
- return avatar;
- }
-
- /**
* Paints a customized background.
*
* @param g the <tt>Graphics</tt> object through which we paint
@@ -571,35 +549,25 @@ public class ContactListTreeCellRenderer
}
/**
- * Returns the first found status message for the given
- * <tt>metaContact</tt>.
- * @param metaContact the <tt>MetaContact</tt>, for which we'd like to
- * obtain a status message
+ * Initializes the display details component for the given
+ * <tt>UIContact</tt>.
+ * @param contact the <tt>UIContact</tt>, for which we initialize the
+ * details component
*/
- private void initStatusMessage(MetaContact metaContact)
+ private void initDisplayDetails(UIContact contact)
{
- statusMessageLabel.setText("");
- this.remove(statusMessageLabel);
+ displayDetailsLabel.setText("");
+ this.remove(displayDetailsLabel);
- String statusMessage = null;
- Iterator<Contact> protoContacts = metaContact.getContacts();
-
- while (protoContacts.hasNext())
- {
- Contact protoContact = protoContacts.next();
-
- statusMessage = protoContact.getStatusMessage();
- if (statusMessage != null && statusMessage.length() > 0)
- break;
- }
+ String displayDetails = contact.getDisplayDetails();
- if (statusMessage != null && statusMessage.length() > 0)
+ if (displayDetails != null && displayDetails.length() > 0)
{
// Replace all occurrences of new line with slash.
- statusMessage = Html2Text.extractText(statusMessage);
- statusMessage = statusMessage.replaceAll("\n|<br>|<br/>", " / ");
+ displayDetails = Html2Text.extractText(displayDetails);
+ displayDetails = displayDetails.replaceAll("\n|<br>|<br/>", " / ");
- statusMessageLabel.setText(statusMessage);
+ displayDetailsLabel.setText(displayDetails);
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.NONE;
@@ -610,16 +578,16 @@ public class ContactListTreeCellRenderer
constraints.gridwidth = 2;
constraints.gridheight = 1;
- this.add(statusMessageLabel, constraints);
+ this.add(displayDetailsLabel, constraints);
}
}
/**
* Initializes buttons panel.
- * @param metaContact the <tt>MetaContact</tt> for which we initialize the
+ * @param uiContact the <tt>UIContact</tt> for which we initialize the
* button panel
*/
- private void initButtonsPanel(MetaContact metaContact)
+ private void initButtonsPanel(UIContact uiContact)
{
this.remove(callButton);
this.remove(chatButton);
@@ -628,13 +596,13 @@ public class ContactListTreeCellRenderer
return;
int statusMessageLabelHeight = 0;
- if (statusMessageLabel.getText().length() > 0)
+ if (displayDetailsLabel.getText().length() > 0)
statusMessageLabelHeight = 20;
else
statusMessageLabelHeight = 15;
- Contact imContact = metaContact.getDefaultContact(
- OperationSetBasicInstantMessaging.class);
+ UIContactDetail imContact = uiContact.getDefaultContactDetail(
+ OperationSetBasicInstantMessaging.class);
if (imContact != null)
{
@@ -656,8 +624,8 @@ public class ContactListTreeCellRenderer
28, 28);
}
- Contact telephonyContact
- = metaContact.getDefaultContact(OperationSetBasicTelephony.class);
+ UIContactDetail telephonyContact
+ = uiContact.getDefaultContactDetail(OperationSetBasicTelephony.class);
if (telephonyContact != null)
{
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeModel.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeModel.java
index fbabca2..b1069a2 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeModel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListTreeModel.java
@@ -6,10 +6,9 @@
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
+import javax.swing.*;
import javax.swing.tree.*;
-import net.java.sip.communicator.service.contactlist.*;
-
/**
* The data model of the contact list.
*
@@ -18,91 +17,78 @@ import net.java.sip.communicator.service.contactlist.*;
public class ContactListTreeModel
extends DefaultTreeModel
{
- private GroupNode rootGroupNode;
+ /**
+ * The root node.
+ */
+ private final GroupNode rootGroupNode;
/**
- * Creates the <tt>ContactListTreeModel</tt> by specifying the
- * <tt>rootMetaGroup</tt>, which would correspond to the root of our data
- * model.
- * @param rootMetaGroup the root <tt>MetaContactGroup</tt>, which would
- * correspond to our root node
+ * Creates an instance of <tt>ContactListTreeModel</tt>.
*/
- public ContactListTreeModel(MetaContactGroup rootMetaGroup)
+ public ContactListTreeModel()
{
super(null);
- rootGroupNode = new GroupNode(this, rootMetaGroup);
+ RootUIGroup rootDescriptor = new RootUIGroup();
+ rootGroupNode = new GroupNode(this, rootDescriptor);
+ rootDescriptor.setGroupNode(rootGroupNode);
this.setRoot(rootGroupNode);
}
/**
- * Creates the <tt>ContactListTreeModel</tt> by specifying the
- * root node.
- * @param root the root node
- */
- public ContactListTreeModel(TreeNode root)
- {
- super(root);
- }
-
- /**
* Returns the root group node.
* @return the root group node
*/
- @Override
public GroupNode getRoot()
{
return rootGroupNode;
}
/**
- * Returns the <tt>GroupNode</tt> corresponding to the given
- * <tt>metaGroup</tt>. This method will look in deep.
- * @param metaGroup the <tt>MetaContactGroup</tt>, which corresponding node
- * we're looking for
- * @return the <tt>GroupNode</tt> corresponding to the given
- * <tt>metaGroup</tt>
+ * Returns the first found child <tt>ContactNode</tt>.
+ * @return the first found child <tt>ContactNode</tt> or <tt>null</tt>
+ * if there is no ContactNode.
*/
- public GroupNode findGroupNodeByMetaGroup(MetaContactGroup metaGroup)
+ public ContactNode findFirstContactNode()
{
- if (metaGroup.equals(rootGroupNode.getMetaContactGroup()))
- return rootGroupNode;
- else
- return rootGroupNode.findGroupNode(metaGroup);
+ return findFirstContactNode(rootGroupNode);
}
/**
- * Returns the <tt>ContactNode</tt> corresponding to the given
- * <tt>metaContact</tt>. This method will look in deep.
- * @param metaContact the <tt>MetaContact</tt>, which corresponding node
- * we're looking for
- * @return the <tt>ContactNode</tt> corresponding to the given
- * <tt>metaContact</tt>
+ * Clears all dependencies in the abstraction path (i.e. GroupNode - UIGroup
+ * - MetaContactGroup or ContactNode - UIContact - SourceContact).
*/
- public ContactNode findContactNodeByMetaContact(MetaContact metaContact)
+ public void clearDependencies()
{
- MetaContactGroup parentGroup = metaContact.getParentMetaContactGroup();
-
- if (parentGroup == null)
- return null;
-
- GroupNode parentGroupNode = findGroupNodeByMetaGroup(parentGroup);
-
- if (parentGroupNode != null)
- return parentGroupNode.findContactNode(metaContact);
-
- return null;
+ clearDependencies(rootGroupNode);
}
/**
- * Returns the first found child <tt>ContactNode</tt>.
- * @return the first found child <tt>ContactNode</tt> or <tt>null</tt>
- * if there is no ContactNode.
+ * Clears all dependencies for all children in the given <tt>groupNode</tt>
+ * (i.e. GroupNode - UIGroup - MetaContactGroup or ContactNode - UIContact
+ * - SourceContact).
+ * @param groupNode the <tt>TreeNode</tt> in which we clear dependencies
*/
- public ContactNode findFirstContactNode()
+ private void clearDependencies(TreeNode groupNode)
{
- return findFirstContactNode(rootGroupNode);
+ for (int i = 0; i < groupNode.getChildCount(); i ++)
+ {
+ TreeNode treeNode = groupNode.getChildAt(i);
+
+ if (treeNode instanceof ContactNode)
+ {
+ ((ContactNode) treeNode).getContactDescriptor()
+ .setContactNode(null);
+ }
+ else if (treeNode instanceof GroupNode)
+ {
+ ((GroupNode) treeNode).getGroupDescriptor()
+ .setGroupNode(null);
+
+ clearDependencies(treeNode);
+ }
+ }
}
/**
@@ -113,14 +99,128 @@ public class ContactListTreeModel
private ContactNode findFirstContactNode(GroupNode parentNode)
{
// If the parent node has no children we have nothing to do here.
- if (parentNode.getChildCount() ==0)
+ if (parentNode.getChildCount() == 0)
return null;
TreeNode treeNode = parentNode.getFirstChild();
if (treeNode instanceof GroupNode)
- return findFirstContactNode((GroupNode)treeNode);
+ return findFirstContactNode((GroupNode) treeNode);
else
return (ContactNode)treeNode;
}
+
+ /**
+ * The <tt>RootUIGroup</tt> is the root group in this contact list model.
+ */
+ private class RootUIGroup
+ implements UIGroup
+ {
+ /**
+ * The corresponding group node.
+ */
+ private GroupNode groupNode;
+
+ /**
+ * Returns null to indicate that this group has no parent.
+ * @return null
+ */
+ public UIGroup getParentGroup()
+ {
+ return null;
+ }
+
+ /**
+ * This group is not attached to a contact source, so we return the
+ * first index.
+ * @return 0
+ */
+ public int getSourceIndex()
+ {
+ return 0;
+ }
+
+ /**
+ * This group should never be collapsed.
+ * @return false
+ */
+ public boolean isGroupCollapsed()
+ {
+ return false;
+ }
+
+ /**
+ * Returns null to indicate that this group has no display name.
+ * @return null
+ */
+ public String getDisplayName()
+ {
+ return null;
+ }
+
+ /**
+ * As this group is not attached to a contact source it has no child
+ * contacts.
+ * @return 0
+ */
+ public int countChildContacts()
+ {
+ return 0;
+ }
+
+ /**
+ * As this group is not attached to a contact source it has no child
+ * contacts.
+ * @return 0
+ */
+ public int countOnlineChildContacts()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the descriptor of this group, just a string.
+ * @return the descriptor of this group
+ */
+ public Object getDescriptor()
+ {
+ return "RootGroup";
+ }
+
+ /**
+ * Returns null to indicate that this group has no identifier.
+ * @return null
+ */
+ public String getId()
+ {
+ return null;
+ }
+
+ /**
+ * Returns the corresponding <tt>GroupNode</tt>.
+ * @return the corresponding <tt>GroupNode</tt>
+ */
+ public GroupNode getGroupNode()
+ {
+ return groupNode;
+ }
+
+ /**
+ * Sets the corresponding <tt>GroupNode</tt>.
+ * @param groupNode the <tt>GroupNode</tt> to set
+ */
+ public void setGroupNode(GroupNode groupNode)
+ {
+ this.groupNode = groupNode;
+ }
+
+ /**
+ * This group is not visible to the user.
+ * @return null
+ */
+ public JPopupMenu getRightButtonMenu()
+ {
+ return null;
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactNode.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactNode.java
index 888ca65..2ddffb0 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactNode.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactNode.java
@@ -8,11 +8,9 @@ package net.java.sip.communicator.impl.gui.main.contactlist;
import javax.swing.tree.*;
-import net.java.sip.communicator.service.contactlist.*;
-
/**
* The <tt>ContactNode</tt> is a <tt>ContactListNode</tt> corresponding to a
- * given <tt>MetaContact</tt>.
+ * given <tt>UIContact</tt>.
*
* @author Yana Stamcheva
*/
@@ -21,9 +19,9 @@ public class ContactNode
implements ContactListNode
{
/**
- * The <tt>MetaContact</tt> corresponding to this contact node.
+ * The <tt>UIContact</tt> corresponding to this contact node.
*/
- private MetaContact metaContact;
+ private UIContact contact;
/**
* Indicates if this node is currently active. Has unread messages waiting.
@@ -32,32 +30,31 @@ public class ContactNode
/**
* Creates a <tt>ContactNode</tt> by specifying the corresponding
- * <tt>metaContact</tt>.
- * @param metaContact the <tt>MetaContact</tt> corresponding to this node
+ * <tt>contact</tt>.
+ * @param contact the <tt>UIContact</tt> corresponding to this node
*/
- public ContactNode(MetaContact metaContact)
+ public ContactNode(UIContact contact)
{
- super(metaContact);
- this.metaContact = metaContact;
+ super(contact);
+ this.contact = contact;
}
/**
- * Returns the corresponding <tt>MetaContact</tt>.
- * @return the corresponding <tt>MetaContact</tt>
+ * Returns the corresponding <tt>UIContact</tt>.
+ * @return the corresponding <tt>UIContact</tt>
*/
- public MetaContact getMetaContact()
+ public UIContact getContactDescriptor()
{
- return (MetaContact) getUserObject();
+ return (UIContact) getUserObject();
}
/**
- * Returns the index of this contact node in its parent group in
- * the <tt>MetaContactListService</tt>.
- * @return the index in the <tt>MetaContactListService</tt>
+ * Returns the index of this contact node in its parent group.
+ * @return the index of this contact node in its parent group
*/
- public int getMetaContactListIndex()
+ public int getSourceIndex()
{
- return metaContact.getParentMetaContactGroup().indexOf(metaContact);
+ return contact.getSourceIndex();
}
/**
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/DefaultTreeContactList.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/DefaultTreeContactList.java
index 22ce7c6..c1fe795 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/DefaultTreeContactList.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/DefaultTreeContactList.java
@@ -8,7 +8,6 @@ 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.event.*;
@@ -16,9 +15,9 @@ import javax.swing.tree.*;
import net.java.sip.communicator.impl.gui.lookandfeel.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
-import net.java.sip.communicator.service.protocol.*;
/**
* DeafultContactlist used to display <code>JList</code>s with contacts.
@@ -73,40 +72,28 @@ public class DefaultTreeContactList
* Dummy method used and overridden from classes extending this
* functionality such as ContactList.
*
- * @param metaContact the <tt>MetaContact</tt> to verify
+ * @param contact the <tt>MetaContact</tt> to verify
* @return TRUE if the given <tt>MetaContact</tt> is active, FALSE -
* otherwise
*/
- public boolean isContactActive(MetaContact metaContact)
+ public boolean isContactActive(UIContact contact)
{
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.
+ * Checks if the given contact is currently active.
+ * Dummy method used and overridden from classes extending this
+ * functionality such as ContactList.
*
- * @param metaContact The metaContact for which the status is asked.
- * @return PresenceStatus The most "available" status from all subcontact
- * statuses.
+ * @param metaContact the <tt>MetaContact</tt> to verify
+ * @return TRUE if the given <tt>MetaContact</tt> is active, FALSE -
+ * otherwise
*/
- public PresenceStatus getMetaContactStatus(MetaContact metaContact)
+ public boolean isContactActive(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;
+ return isContactActive(
+ MetaContactListSource.getUIContact(metaContact));
}
/**
@@ -125,51 +112,26 @@ public class DefaultTreeContactList
Object element = path.getLastPathComponent();
- ExtendedTooltip tip = new ExtendedTooltip(true);
+ ExtendedTooltip tip = null;
if (element instanceof ContactNode)
{
- MetaContact metaContact = ((ContactNode) element).getMetaContact();
-
- byte[] avatarImage = metaContact.getAvatar();
-
- if (avatarImage != null && avatarImage.length > 0)
- tip.setImage(new ImageIcon(avatarImage));
-
- tip.setTitle(metaContact.getDisplayName());
-
- Iterator<Contact> i = metaContact.getContacts();
+ UIContact contact
+ = ((ContactNode) element).getContactDescriptor();
- String statusMessage = null;
- Contact protocolContact;
- while (i.hasNext())
+ tip = contact.getToolTip();
+ if (tip == null)
{
- 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();
+ tip = new ExtendedTooltip(true);
+ tip.setTitle(contact.getDisplayName());
}
-
- if (statusMessage != null)
- tip.setBottomText(statusMessage);
}
else if (element instanceof GroupNode)
{
- MetaContactGroup metaGroup
- = ((GroupNode) element).getMetaContactGroup();
+ UIGroup group
+ = ((GroupNode) element).getGroupDescriptor();
- tip.setTitle(metaGroup.getGroupName());
+ tip = new ExtendedTooltip(true);
+ tip.setTitle(group.getDisplayName());
}
else if (element instanceof ChatContact)
{
@@ -177,6 +139,7 @@ public class DefaultTreeContactList
ImageIcon avatarImage = chatContact.getAvatar();
+ tip = new ExtendedTooltip(true);
if (avatarImage != null)
tip.setImage(avatarImage);
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/GroupNode.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/GroupNode.java
index 78f09bf..dd65cc2 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/GroupNode.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/GroupNode.java
@@ -10,12 +10,9 @@ import java.util.*;
import javax.swing.tree.*;
-import net.java.sip.communicator.impl.gui.utils.*;
-import net.java.sip.communicator.service.contactlist.*;
-
/**
* The <tt>GroupNode</tt> is a <tt>ContactListNode</tt> corresponding to a
- * given <tt>MetaContactGroup</tt>.
+ * given <tt>UIGroup</tt>.
*
* @author Yana Stamcheva
*/
@@ -24,9 +21,14 @@ public class GroupNode
implements ContactListNode
{
/**
- * The corresponding <tt>MetaContactGroup</tt>.
+ * The parent contact list model.
*/
- private final MetaContactGroup metaGroup;
+ private final ContactListTreeModel treeModel;
+
+ /**
+ * The corresponding <tt>UIGroup</tt>.
+ */
+ private final UIGroup group;
/**
* A node comparator used to sort the list of children.
@@ -40,50 +42,50 @@ public class GroupNode
/**
* Creates a <tt>GroupNode</tt> by specifying the parent <tt>treeModel</tt>
- * and the corresponding <tt>metaGroup</tt> in the
- * <tt>MetaContactListService</tt>.
+ * and the corresponding <tt>uiGroup</tt>.
*
* @param treeModel the parent tree model containing this group
- * @param metaGroup the corresponding <tt>MetaContactGroup</tt>
+ * @param uiGroup the corresponding <tt>UIGroup</tt>
*/
- public GroupNode(ContactListTreeModel treeModel, MetaContactGroup metaGroup)
+ public GroupNode( ContactListTreeModel treeModel,
+ UIGroup uiGroup)
{
- super(metaGroup, true);
+ super(uiGroup, true);
- this.metaGroup = metaGroup;
+ this.treeModel = treeModel;
+ this.group = uiGroup;
- isCollapsed = ConfigurationManager
- .isContactListGroupCollapsed(metaGroup.getMetaUID());
+ isCollapsed = group.isGroupCollapsed();
}
/**
- * Creates a <tt>ContactNode</tt> for the given <tt>metaContact</tt> and
- * adds it to this group.
- * @param metaContact the <tt>MetaContact</tt> to add
+ * Creates a <tt>ContactNode</tt> for the given <tt>uiContact</tt>
+ * and adds it to this group.
+ * @param uiContact the <tt>UIContact</tt> to add
* @return the created <tt>ContactNode</tt>
*/
- public ContactNode addMetaContact(MetaContact metaContact)
+ public ContactNode addContact(UIContact uiContact)
{
- ContactNode contactNode = new ContactNode(metaContact);
+ ContactNode contactNode = new ContactNode(uiContact);
+ uiContact.setContactNode(contactNode);
+
this.add(contactNode);
return contactNode;
}
/**
- * Creates a <tt>ContactNode</tt> for the given <tt>metaContact</tt>,
+ * Creates a <tt>ContactNode</tt> for the given <tt>uiContact</tt>,
* adds it to this group and performs a sort at the end.
- * @param treeModel the <tt>ContactListTreeModel</tt> to which the given
- * <tt>metaContact</tt> is added
- * @param metaContact the <tt>MetaContact</tt> to add
+ * @param uiContact the <tt>UIContact</tt> to add
* @param isRefreshView indicates if the view should be refreshed
* @return the created <tt>ContactNode</tt>
*/
@SuppressWarnings("unchecked")
- public ContactNode sortedAddMetaContact(ContactListTreeModel treeModel,
- MetaContact metaContact,
- boolean isRefreshView)
+ public ContactNode sortedAddContact(UIContact uiContact,
+ boolean isRefreshView)
{
- ContactNode contactNode = new ContactNode(metaContact);
+ ContactNode contactNode = new ContactNode(uiContact);
+ uiContact.setContactNode(contactNode);
this.add(contactNode);
@@ -91,22 +93,19 @@ public class GroupNode
Collections.sort(children, nodeComparator);
if (isRefreshView)
- this.fireNodeInserted(treeModel, getIndex(contactNode));
+ this.fireNodeInserted(getIndex(contactNode));
return contactNode;
}
/**
- * Removes the node corresponding to the given <tt>MetaContact</tt> from
- * this group.
- * @param treeModel the <tt>ContactListTreeModel</tt> from which the
- * given <tt>metaContact</tt> is removed
- * @param metaContact the <tt>MetaContact</tt> to remove
+ * Removes the node corresponding to the given <tt>uiContact</tt> from this
+ * group.
+ * @param uiContact the <tt>UIContact</tt> to remove
*/
- public void removeMetaContact( ContactListTreeModel treeModel,
- MetaContact metaContact)
+ public void removeContact(UIContact uiContact)
{
- ContactNode contactNode = findContactNode(metaContact);
+ ContactNode contactNode = findContactNode(uiContact);
if (contactNode != null)
{
@@ -115,38 +114,35 @@ public class GroupNode
// checks verifying if the node belongs to this parent.
children.removeElementAt(index);
contactNode.setParent(null);
+ uiContact.setContactNode(null);
- fireNodeRemoved(treeModel, contactNode, index);
+ fireNodeRemoved(contactNode, index);
}
}
/**
- * Creates a <tt>GroupNode</tt> for the given <tt>metaGroup</tt> and adds it
- * to this group.
- * @param treeModel the <tt>ContactListTreeModel</tt> to which the given
- * <tt>metaGroup</tt> is added
- * @param metaGroup the <tt>MetaContactGroup</tt> to add
+ * Creates a <tt>GroupNode</tt> for the given <tt>uiGroup</tt> and
+ * adds it to this group.
+ * @param uiGroup the <tt>UIGroup</tt> to add
* @return the created <tt>GroupNode</tt>
*/
- public GroupNode addMetaContactGroup( ContactListTreeModel treeModel,
- MetaContactGroup metaGroup)
+ public GroupNode addContactGroup(UIGroup uiGroup)
{
- GroupNode groupNode = new GroupNode(treeModel, metaGroup);
+ GroupNode groupNode = new GroupNode(treeModel, uiGroup);
+ uiGroup.setGroupNode(groupNode);
+
this.add(groupNode);
return groupNode;
}
/**
- * Removes the node corresponding to the given <tt>metaGroup</tt> from this
+ * Removes the node corresponding to the given <tt>uiGroup</tt> from this
* group node.
- * @param treeModel the <tt>ContactListTreeModel</tt> from which the given
- * <tt>metaGroup</tt> is removed
- * @param metaGroup the <tt>MetaContactGroup</tt> to remove
+ * @param uiGroup the <tt>UIGroup</tt> to remove
*/
- public void removeMetaContactGroup( ContactListTreeModel treeModel,
- MetaContactGroup metaGroup)
+ public void removeContactGroup(UIGroup uiGroup)
{
- GroupNode groupNode = findGroupNode(metaGroup);
+ GroupNode groupNode = uiGroup.getGroupNode();
if (groupNode != null)
{
@@ -155,26 +151,47 @@ public class GroupNode
// checks verifying if the node belongs to this parent.
children.removeElementAt(index);
groupNode.setParent(null);
+ uiGroup.setGroupNode(null);
+
+ fireNodeRemoved(groupNode, index);
+ }
+ }
+
+ /**
+ * Removes all of this node's children, setting their parents to null.
+ * If this node has no children, this method does nothing.
+ */
+ public void removeAllChildren()
+ {
+ for (int i = getChildCount()-1; i >= 0; i--)
+ {
+ TreeNode treeNode = getChildAt(i);
+
+ if (treeNode instanceof ContactNode)
+ ((ContactNode) treeNode).getContactDescriptor()
+ .setContactNode(null);
+ else if (treeNode instanceof GroupNode)
+ ((GroupNode) treeNode).getGroupDescriptor()
+ .setGroupNode(null);
- fireNodeRemoved(treeModel, groupNode, index);
+ children.removeElementAt(i);
+ ((DefaultMutableTreeNode) treeNode).setParent(null);
}
}
/**
- * Creates a <tt>GroupNode</tt> for the given <tt>metaGroup</tt>, adds it
- * to this group node and performs a sort at the end.
- * @param treeModel the <tt>ContactListTreeModel</tt> to which the given
- * <tt>metaGroup</tt> is added
- * @param metaGroup the <tt>MetaContactGroup</tt> to add
+ * Creates a <tt>GroupNode</tt> for the given <tt>uiGroup</tt>,
+ * adds it to this group node and performs a sort at the end.
+ * @param uiGroup the <tt>UIGroup</tt> to add
* @param isRefreshView indicates if the view should be refreshed
* @return the created <tt>GroupNode</tt>
*/
@SuppressWarnings("unchecked")
- public GroupNode sortedAddMetaContactGroup( ContactListTreeModel treeModel,
- MetaContactGroup metaGroup,
- boolean isRefreshView)
+ public GroupNode sortedAddContactGroup( UIGroup uiGroup,
+ boolean isRefreshView)
{
- GroupNode groupNode = new GroupNode(treeModel, metaGroup);
+ GroupNode groupNode = new GroupNode(treeModel, uiGroup);
+ uiGroup.setGroupNode(groupNode);
this.add(groupNode);
@@ -182,77 +199,51 @@ public class GroupNode
Collections.sort(children, nodeComparator);
if (isRefreshView)
- this.fireNodeInserted(treeModel, getIndex(groupNode));
+ this.fireNodeInserted(getIndex(groupNode));
return groupNode;
}
/**
- * Returns the <tt>MetaContactGroup</tt> corresponding to this
- * <tt>GroupNode</tt>.
- * @return the <tt>MetaContactGroup</tt> corresponding to this
- * <tt>GroupNode</tt>
+ * Returns the <tt>UIGroup</tt> corresponding to this <tt>GroupNode</tt>.
+ * @return the <tt>UIGroup</tt> corresponding to this <tt>GroupNode</tt>
*/
- public MetaContactGroup getMetaContactGroup()
+ public UIGroup getGroupDescriptor()
{
- return (MetaContactGroup) getUserObject();
- }
-
- /**
- * Finds the <tt>GroupNode</tt> corresponding to the given
- * <tt>metaGroup</tt> in the children of this node.
- * @param metaGroup the <tt>MetaContactGroup</tt>, which node we're looking
- * for
- * @return the corresponding <tt>GroupNode</tt> or null if no group node
- * was found
- */
- @SuppressWarnings("unchecked")
- public GroupNode findGroupNode(MetaContactGroup metaGroup)
- {
- Enumeration<TreeNode> children = children();
- while(children.hasMoreElements())
- {
- TreeNode treeNode = children.nextElement();
- if (treeNode instanceof GroupNode
- && ((GroupNode)treeNode).getMetaContactGroup().equals(metaGroup))
- return (GroupNode) treeNode;
- }
- return null;
+ return (UIGroup) getUserObject();
}
/**
* Finds the <tt>ContactNode</tt> corresponding to the given
- * <tt>metaContact</tt> in the children of this node.
- * @param metaContact the <tt>MetaContact</tt>, which node we're looking for
- * @return the corresponding <tt>ContactNode</tt> or null if no contact node
- * was found
+ * <tt>uiContact</tt> in the children of this node.
+ * @param uiContact the <tt>UIContact</tt>, which node we're looking for
+ * @return the corresponding <tt>ContactNode</tt> or null if no contact
+ * node was found
*/
@SuppressWarnings("unchecked")
- public ContactNode findContactNode(MetaContact metaContact)
+ public ContactNode findContactNode(UIContact uiContact)
{
Enumeration<TreeNode> children = children();
while(children.hasMoreElements())
{
TreeNode treeNode = children.nextElement();
if (treeNode instanceof ContactNode
- && ((ContactNode)treeNode).getMetaContact().equals(metaContact))
+ && ((ContactNode) treeNode).getContactDescriptor()
+ .equals(uiContact))
+ {
return (ContactNode) treeNode;
+ }
}
return null;
}
/**
- * Returns the index of this node in its parent group in the
- * <tt>MetaContactListService</tt>.
- * @return the index of this node in its parent group in the
- * <tt>MetaContactListService</tt>
+ * Returns the index of this node in its parent group.
+ * @return the index of this node in its parent group
*/
- public int getMetaContactListIndex()
+ public int getSourceIndex()
{
- MetaContactGroup parentGroup = metaGroup.getParentMetaContactGroup();
- if (parentGroup != null)
- return parentGroup.indexOf(metaGroup);
- else return 0; //this is the root group
+ return group.getSourceIndex();
}
/**
@@ -267,7 +258,7 @@ public class GroupNode
{
Collections.sort(children, nodeComparator);
- fireNodesChanged(treeModel);
+ fireNodesChanged();
}
}
@@ -285,11 +276,9 @@ public class GroupNode
/**
* Notifies all interested listeners that a node has been inserted at the
* given <tt>index</tt>.
- * @param treeModel the <tt>ContactListTreeModel</tt> which should be
- * notified
* @param index the index of the newly inserted node
*/
- private void fireNodeInserted(ContactListTreeModel treeModel, int index)
+ private void fireNodeInserted(int index)
{
int[] newIndexs = new int[1];
newIndexs[0] = index;
@@ -299,13 +288,10 @@ public class GroupNode
/**
* Notifies all interested listeners that <tt>node</tt> has been removed
* from the given <tt>index</tt>.
- * @param treeModel the <tt>ContactListTreeModel</tt> which should
- * be notified
* @param node the node that has been removed
* @param index the index of the removed node
*/
- private void fireNodeRemoved(
- ContactListTreeModel treeModel, ContactListNode node, int index)
+ private void fireNodeRemoved(ContactListNode node, int index)
{
int[] removedIndexs = new int[1];
removedIndexs[0] = index;
@@ -314,10 +300,8 @@ public class GroupNode
/**
* Notifies all interested listeners that all nodes has changed.
- * @param treeModel the <tt>ContactListTreeModel</tt> which should
- * be notified
*/
- private void fireNodesChanged(ContactListTreeModel treeModel)
+ private void fireNodesChanged()
{
int childCount = getChildCount();
int[] changedIndexs = new int[childCount];
@@ -328,16 +312,25 @@ public class GroupNode
}
/**
- *
* Note: this comparator imposes orderings that are inconsistent with
* equals.
*/
private class NodeComparator implements Comparator<ContactListNode>
{
+ /**
+ * Compares its two arguments for order. Returns a negative integer,
+ * zero, or a positive integer as the first argument is less than, equal
+ * to, or greater than the second.
+ * @param node1 the first <tt>ContactListNode</tt> to compare
+ * @param node2 the second <tt>ContactListNode</tt> to compare
+ * @return -1 if the first node should be positioned before the second
+ * one, 1 if the first argument should be positioned after the second
+ * one, 0 if there's no matter
+ */
public int compare(ContactListNode node1, ContactListNode node2)
{
- int index1 = node1.getMetaContactListIndex();
- int index2 = node2.getMetaContactListIndex();
+ int index1 = node1.getSourceIndex();
+ int index2 = node2.getSourceIndex();
// Child groups are shown after child contacts.
if (node1 instanceof GroupNode && node2 instanceof ContactNode)
@@ -346,6 +339,14 @@ public class GroupNode
if (node1 instanceof ContactNode && node2 instanceof GroupNode)
return -1;
+ // If the first index is unknown then we position it to the end.
+ if (index1 < 0)
+ return 1;
+
+ // If the second index is unknown then we position it to the end.
+ if (index2 < 0)
+ return -1;
+
if (index1 > index2) return 1;
else if (index1 < index2) return -1;
else return 0;
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactRightButtonMenu.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/MetaContactRightButtonMenu.java
index 5a75dc0..b055980 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactRightButtonMenu.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/MetaContactRightButtonMenu.java
@@ -19,6 +19,7 @@ import net.java.sip.communicator.impl.gui.event.*;
import net.java.sip.communicator.impl.gui.main.*;
import net.java.sip.communicator.impl.gui.main.call.*;
import net.java.sip.communicator.impl.gui.main.chat.history.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.gui.*;
@@ -35,7 +36,7 @@ import org.osgi.framework.*;
*
* @author Yana Stamcheva
*/
-public class ContactRightButtonMenu
+public class MetaContactRightButtonMenu
extends JPopupMenu
implements ActionListener,
PluginComponentListener,
@@ -46,7 +47,8 @@ public class ContactRightButtonMenu
*/
private static final long serialVersionUID = 3033031652970285857L;
- private final Logger logger = Logger.getLogger(ContactRightButtonMenu.class);
+ private final Logger logger
+ = Logger.getLogger(MetaContactRightButtonMenu.class);
private static final String allContactsString
= GuiActivator.getResources().getI18NString("service.gui.ALL_CONTACTS");
@@ -54,8 +56,9 @@ public class ContactRightButtonMenu
private static final String moveToString = GuiActivator.getResources()
.getI18NString("service.gui.MOVE_TO_GROUP");
- private static final String moveSubcontactString = GuiActivator.getResources()
- .getI18NString("service.gui.MOVE_SUBCONTACT");
+ private static final String moveSubcontactString
+ = GuiActivator.getResources()
+ .getI18NString("service.gui.MOVE_SUBCONTACT");
private static final String removeContactString
= GuiActivator.getResources()
@@ -71,7 +74,8 @@ public class ContactRightButtonMenu
= GuiActivator.getResources().getI18NString("service.gui.SEND_FILE");
private static final String renameContactString
- = GuiActivator.getResources().getI18NString("service.gui.RENAME_CONTACT");
+ = GuiActivator.getResources()
+ .getI18NString("service.gui.RENAME_CONTACT");
private static final String viewHistoryString
= GuiActivator.getResources().getI18NString("service.gui.VIEW_HISTORY");
@@ -97,7 +101,8 @@ public class ContactRightButtonMenu
private final JMenuItem sendMessageItem = new JMenuItem(
sendMessageString,
- new ImageIcon(ImageLoader.getImage(ImageLoader.SEND_MESSAGE_16x16_ICON)));
+ new ImageIcon(ImageLoader
+ .getImage(ImageLoader.SEND_MESSAGE_16x16_ICON)));
private final JMenuItem sendFileItem = new JMenuItem(
sendFileString,
@@ -105,7 +110,8 @@ public class ContactRightButtonMenu
private final JMenuItem sendSmsItem = new JMenuItem(
sendSmsString,
- new ImageIcon(ImageLoader.getImage(ImageLoader.SEND_MESSAGE_16x16_ICON)));
+ new ImageIcon(ImageLoader
+ .getImage(ImageLoader.SEND_MESSAGE_16x16_ICON)));
private final JMenuItem renameContactItem = new JMenuItem(
renameContactString,
@@ -137,16 +143,14 @@ public class ContactRightButtonMenu
/**
* Creates an instance of ContactRightButtonMenu.
- * @param contactItem The MetaContact for which the menu is opened.
- * @param contactList The contact list over which this menu is shown.
+ * @param contactItem The MetaContact for which the menu is opened
*/
- public ContactRightButtonMenu( MetaContact contactItem,
- TreeContactList contactList)
+ public MetaContactRightButtonMenu( MetaContact contactItem)
{
super();
this.mainFrame = GuiActivator.getUIService().getMainFrame();
- this.contactList = contactList;
+ this.contactList = GuiActivator.getContactList();
this.contactItem = contactItem;
@@ -621,11 +625,19 @@ public class ContactRightButtonMenu
* the selected contact to the selected group.
* @param evt the <tt>ContactListEvent</tt> has
*/
- public void groupSelected(ContactListEvent evt)
+ public void groupClicked(ContactListEvent evt)
{
this.moveDialog.dispose();
- MetaContactGroup sourceGroup = evt.getSourceGroup();
+ UIGroup sourceGroup = evt.getSourceGroup();
+
+ // TODO: may be show a warning message to tell the user that she should
+ // select another group.
+ if (!(sourceGroup instanceof MetaUIGroup))
+ return;
+
+ MetaContactGroup metaGroup
+ = (MetaContactGroup) sourceGroup.getDescriptor();
contactList.removeContactListListener(this);
@@ -636,12 +648,12 @@ public class ContactRightButtonMenu
if(moveAllContacts)
{
MetaContactListManager
- .moveMetaContactToGroup(contactItem, sourceGroup);
+ .moveMetaContactToGroup(contactItem, metaGroup);
}
else if(contactToMove != null)
{
MetaContactListManager
- .moveContactToGroup(contactToMove, sourceGroup);
+ .moveContactToGroup(contactToMove, metaGroup);
}
contactList.setGroupClickConsumed(false);
@@ -654,17 +666,12 @@ public class ContactRightButtonMenu
*/
public void contactClicked(ContactListEvent evt)
{
- this.moveContact(evt.getSourceContact());
- }
+ UIContact descriptor = evt.getSourceContact();
+ // We're only interested in MetaContacts here.
+ if (!(descriptor instanceof MetaUIContact))
+ return;
- /**
- * Implements ContactListListener.contactSelected method in order
- * to move the chosen sub-contact when a meta contact is selected.
- * @param evt the <tt>ContactListEvent</tt> that notified us
- */
- public void protocolContactClicked(ContactListEvent evt)
- {
- this.moveContact(evt.getSourceContact());
+ this.moveContact((MetaContact) descriptor.getDescriptor());
}
/**
@@ -766,48 +773,4 @@ public class ContactRightButtonMenu
protoContact.getPresenceStatus().getStatusIcon()),
protoContact.getProtocolProvider());
}
-
- /**
- * A menu item that performs an action related to a specific protocol
- * provider.
- *
- */
- private static class ProviderAwareMenuItem extends JMenuItem
- {
- /**
- * An eclipse generated serialVersionUID.
- */
- private static final long serialVersionUID = 6343418726839985645L;
-
- private ProtocolProviderService provider = null;
-
- /**
- * Initializes the menu item and stores a reference to the specified
- * provider.
- *
- * @param provider the provider that we are related to
- * @param text the text string for this menu
- * @param icon the icon to display when showing this menu
- */
- public ProviderAwareMenuItem(ProtocolProviderService provider,
- String text,
- Icon icon)
- {
- super(text, icon);
-
- this.provider = provider;
- }
-
- /**
- * Returns a reference to the <tt>ProtocolProviderService</tt> that
- * this item is related to.
- *
- * @return a reference to the <tt>ProtocolProviderService</tt> that
- * this item is related to.
- */
- public ProtocolProviderService getProvider()
- {
- return provider;
- }
- }
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/PresenceFilter.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/PresenceFilter.java
index cdc0c54..d552b29 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/PresenceFilter.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/PresenceFilter.java
@@ -6,8 +6,16 @@
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
+import java.util.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
+import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
/**
* The <tt>PresenceFilter</tt> is used to filter offline contacts from the
@@ -16,11 +24,79 @@ import net.java.sip.communicator.service.protocol.*;
* @author Yana Stamcheva
*/
public class PresenceFilter
- implements ContactListFilter
+ implements ContactListFilter,
+ ContactPresenceStatusListener
{
+ /**
+ * Indicates if this presence filter shows or hides the offline contacts.
+ */
private boolean isShowOffline;
/**
+ * Indicates if there's a presence filtering going on.
+ */
+ private boolean isFiltering = false;
+
+ private final Object lockedFiltering = new Object();
+
+ /**
+ * Creates an instance of <tt>PresenceFilter</tt>.
+ */
+ public PresenceFilter()
+ {
+ this.setShowOffline(ConfigurationManager.isShowOffline());
+ }
+
+ /**
+ * Applies this filter. This filter is applied over the
+ * <tt>MetaContactListService</tt>.
+ * @param treeModel the model to which we should add the results from the
+ * filtering operation
+ */
+ public void applyFilter(ContactListTreeModel treeModel)
+ {
+ isFiltering = true;
+
+ synchronized (lockedFiltering)
+ {
+ addMatching(GuiActivator.getContactListService().getRoot(),
+ treeModel);
+ }
+
+ isFiltering = false;
+ }
+
+ /**
+ * Indicates if the given <tt>uiContact</tt> is matching this filter.
+ * @param uiContact the <tt>UIContact</tt> to check
+ * @return <tt>true</tt> if the given <tt>uiContact</tt> is matching
+ * this filter, otherwise returns <tt>false</tt>
+ */
+ public boolean isMatching(UIContact uiContact)
+ {
+ Object descriptor = uiContact.getDescriptor();
+ if (descriptor instanceof MetaContact)
+ return isMatching((MetaContact) descriptor);
+
+ return false;
+ }
+
+ /**
+ * Indicates if the given <tt>uiGroup</tt> is matching this filter.
+ * @param uiGroup the <tt>UIGroup</tt> to check
+ * @return <tt>true</tt> if the given <tt>uiGroup</tt> is matching
+ * this filter, otherwise returns <tt>false</tt>
+ */
+ public boolean isMatching(UIGroup uiGroup)
+ {
+ Object descriptor = uiGroup.getDescriptor();
+ if (descriptor instanceof MetaContactGroup)
+ return isMatching((MetaContactGroup) descriptor);
+
+ return false;
+ }
+
+ /**
* Sets the show offline property.
* @param isShowOffline indicates if offline contacts are shown
*/
@@ -47,7 +123,7 @@ public class PresenceFilter
* @return <tt>true</tt> if the given <tt>MetaContact</tt> is matching this
* filter
*/
- public boolean isMatching(MetaContact metaContact)
+ private boolean isMatching(MetaContact metaContact)
{
return isShowOffline || isContactOnline(metaContact);
}
@@ -59,7 +135,7 @@ public class PresenceFilter
* @return <tt>true</tt> if the given <tt>MetaContactGroup</tt> is matching
* this filter
*/
- public boolean isMatching(MetaContactGroup metaGroup)
+ private boolean isMatching(MetaContactGroup metaGroup)
{
return (isShowOffline || metaGroup.countOnlineChildContacts() > 0)
? true
@@ -85,4 +161,106 @@ public class PresenceFilter
return defaultContact.getPresenceStatus().getStatus()
>= PresenceStatus.ONLINE_THRESHOLD;
}
+
+ /**
+ * Adds all contacts contained in the given <tt>MetaContactGroup</tt>
+ * matching the current filter and not contained in the contact list.
+ * @param metaGroup the <tt>MetaContactGroup</tt>, which matching contacts
+ * to add
+ * @param resultTreeModel the <tt>ContactListTreeModel</tt>, where results
+ * should be added
+ */
+ private void addMatching( MetaContactGroup metaGroup,
+ ContactListTreeModel resultTreeModel)
+ {
+ Iterator<MetaContact> childContacts = metaGroup.getChildContacts();
+
+ while(childContacts.hasNext() && isFiltering)
+ {
+ MetaContact metaContact = childContacts.next();
+
+ if(isMatching(metaContact))
+ GuiActivator.getContactList().addContact(
+ resultTreeModel,
+ MetaContactListSource.createUIContact(metaContact),
+ true,
+ false);
+ }
+
+ Iterator<MetaContactGroup> subgroups = metaGroup.getSubgroups();
+ while(subgroups.hasNext() && isFiltering)
+ {
+ MetaContactGroup subgroup = subgroups.next();
+
+ if (subgroup.countChildContacts() == 0
+ && subgroup.countSubgroups() == 0
+ && isMatching(subgroup))
+ GuiActivator.getContactList().addGroup(
+ resultTreeModel,
+ MetaContactListSource.createUIGroup(subgroup),
+ false);
+ else
+ addMatching(subgroup, resultTreeModel);
+ }
+ }
+
+ /**
+ * Indicates that a contact has changed its status.
+ *
+ * @param evt the presence event containing information about the
+ * contact status change
+ */
+ public void contactPresenceStatusChanged(
+ final ContactPresenceStatusChangeEvent evt)
+ {
+ final Contact sourceContact = evt.getSourceContact();
+
+ final MetaContact metaContact = GuiActivator.getContactListService()
+ .findMetaContactByContact(sourceContact);
+
+ if (metaContact == null
+ || (evt.getOldStatus() == evt.getNewStatus()))
+ return;
+
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ UIContact cDescriptor
+ = MetaContactListSource.getUIContact(metaContact);
+
+ if (cDescriptor == null)
+ cDescriptor = MetaContactListSource
+ .createUIContact(metaContact);
+
+ synchronized(lockedFiltering)
+ {
+ if (GuiActivator.getContactList().getCurrentFilter()
+ .equals(PresenceFilter.this)
+ && isMatching(metaContact))
+ {
+ if (cDescriptor.getContactNode() == null)
+ GuiActivator.getContactList()
+ .addContact(cDescriptor);
+ else
+ GuiActivator.getContactList()
+ .nodeChanged(cDescriptor.getContactNode());
+ }
+ else
+ {
+ GuiActivator.getContactList()
+ .removeContact(cDescriptor);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Stops this filter current queries.
+ */
+ public void stopFilter()
+ {
+ isFiltering = false;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchField.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchField.java
index 77a97cc..eec1066 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchField.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchField.java
@@ -10,6 +10,7 @@ import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
+import net.java.sip.communicator.util.swing.event.*;
import net.java.sip.communicator.util.swing.plaf.*;
/**
@@ -19,7 +20,7 @@ import net.java.sip.communicator.util.swing.plaf.*;
*/
public class SearchField
extends SIPCommTextField
- implements DocumentListener
+ implements TextFieldChangeListener
{
private final Logger logger = Logger.getLogger(SearchField.class);
@@ -52,7 +53,7 @@ public class SearchField
this.setPreferredSize(new Dimension(100, 22));
this.setDragEnabled(true);
- this.getDocument().addDocumentListener(this);
+ this.addTextChangeListener(this);
InputMap imap = getInputMap(JComponent.WHEN_FOCUSED);
imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "escape");
@@ -70,9 +71,8 @@ public class SearchField
/**
* Handles the change when a char has been inserted in the field.
- * @param e the <tt>DocumentEvent</tt> that notified us
*/
- public void insertUpdate(DocumentEvent e)
+ public void textInserted()
{
// Should explicitly check if there's a text, because the default text
// triggers also an insertUpdate event.
@@ -85,9 +85,8 @@ public class SearchField
/**
* Handles the change when a char has been removed from the field.
- * @param e the <tt>DocumentEvent</tt> that notified us
*/
- public void removeUpdate(DocumentEvent e)
+ public void textRemoved()
{
scheduleUpdate();
}
@@ -171,10 +170,10 @@ public class SearchField
*/
public void updateContactListView(String filterString)
{
+ TreeContactList contactList = GuiActivator.getContactList();
+
if (filterString != null && filterString.length() > 0)
{
- TreeContactList contactList = GuiActivator.getContactList();
-
boolean hasMatching
= contactList.applyFilter(TreeContactList.searchFilter);
@@ -198,9 +197,7 @@ public class SearchField
}
else
{
- TreeContactList contactList = GuiActivator.getContactList();
-
- contactList.applyFilter(TreeContactList.presenceFilter);
+ contactList.applyDefaultFilter();
enableUnknownContactView(false);
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java
index 63106de..e56ba58 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/SearchFilter.java
@@ -9,8 +9,9 @@ package net.java.sip.communicator.impl.gui.main.contactlist;
import java.util.*;
import java.util.regex.*;
-import net.java.sip.communicator.service.contactlist.*;
-import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
+import net.java.sip.communicator.service.contactsource.*;
/**
* The <tt>SearchFilter</tt> is a <tt>ContactListFilter</tt> that filters the
@@ -19,19 +20,137 @@ import net.java.sip.communicator.service.protocol.*;
* @author Yana Stamcheva
*/
public class SearchFilter
- implements ContactListFilter
+ implements ContactListFilter,
+ ContactQueryListener
{
+ /**
+ * The default contact source search type.
+ */
+ public static final int DEFAULT_SOURCE = 0;
+
+ /**
+ * The history contact source search type.
+ */
+ public static final int HISTORY_SOURCE = 1;
+
+ /**
+ * The string, which we're searching.
+ */
+ private String filterString;
+
+ /**
+ * The pattern to filter.
+ */
private Pattern filterPattern;
/**
+ * The <tt>ContactListTreeModel</tt>, where results from the search
+ * are added.
+ */
+ private ContactListTreeModel resultTreeModel;
+
+ /**
+ * The <tt>MetaContactListSource</tt> to search in.
+ */
+ private final MetaContactListSource mclSource;
+
+ /**
+ * The list of external contact sources to search in.
+ */
+ private Collection<ExternalContactSource> contactSources;
+
+ /**
+ * The current operating query.
+ */
+ private ContactQuery currentQuery;
+
+ /**
+ * The type of the search source. One of the above defined DEFAUT_SOURCE or
+ * HISTORY_SOURCE.
+ */
+ private int searchSourceType;
+
+ /**
+ * Creates an instance of <tt>SearchFilter</tt>.
+ */
+ public SearchFilter()
+ {
+ this.mclSource = new MetaContactListSource();
+ }
+
+ /**
+ * Applies this filter and stores the result in the given <tt>treeModel</tt>.
+ * @param treeModel the <tt>ContactListTreeModel</tt>, in which we store
+ * results
+ */
+ public void applyFilter(ContactListTreeModel treeModel)
+ {
+ resultTreeModel = treeModel;
+
+ if (contactSources == null)
+ contactSources = TreeContactList.getContactSources();
+
+ if (searchSourceType == DEFAULT_SOURCE)
+ // First add the MetaContactListSource
+ mclSource.filter(filterPattern, treeModel);
+
+ for (ExternalContactSource contactSource : contactSources)
+ {
+ ContactSourceService sourceService
+ = contactSource.getContactSourceService();
+ if (sourceService instanceof ExtendedContactSourceService)
+ currentQuery
+ = ((ExtendedContactSourceService) sourceService)
+ .queryContactSource(filterPattern);
+ else
+ currentQuery = sourceService.queryContactSource(filterString);
+
+ // Add first available results.
+ this.addMatching(currentQuery.getQueryResults());
+
+ currentQuery.addContactQueryListener(this);
+ }
+ }
+
+ /**
+ * Indicates if the given <tt>uiGroup</tt> matches this filter.
+ * @param uiGroup the <tt>UIGroup</tt> to check
+ * @return <tt>true</tt> if the given <tt>uiGroup</tt> matches the current
+ * filter, <tt>false</tt> - otherwise
+ */
+ public boolean isMatching(UIContact uiGroup)
+ {
+ Iterator<String> searchStrings = uiGroup.getSearchStrings();
+
+ while (searchStrings != null && searchStrings.hasNext())
+ {
+ if (isMatching(searchStrings.next()))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * For all groups we return false. If some of the child contacts of this
+ * group matches this filter the group would be automatically added when
+ * the contact is added in the list.
+ * @param uiGroup the <tt>UIGroup</tt> to check
+ * @return false
+ */
+ public boolean isMatching(UIGroup uiGroup)
+ {
+ return false;
+ }
+
+ /**
* Creates the <tt>SearchFilter</tt> by specifying the string used for
* filtering.
- * @param filterString the String used for filtering
+ * @param filter the String used for filtering
*/
- public void setFilterString(String filterString)
+ public void setFilterString(String filter)
{
// First escape all special characters from the given filter string.
- filterString = Pattern.quote(filterString);
+ this.filterString = Pattern.quote(filter);
// Then create the pattern.
// By default, case-insensitive matching assumes that only characters
@@ -45,60 +164,138 @@ public class SearchFilter
}
/**
- * Checks if the given <tt>metaContact</tt> is matching the current filter.
- * A <tt>MetaContact</tt> would be matching the filter if one of the
- * following is true:<br>
- * - its display name contains the filter string
- * - at least one of its child protocol contacts has a display name or an
- * address that contains the filter string.
- * @param metaContact the <tt>MetaContact</tt> to check
- * @return <tt>true</tt> to indicate that the given <tt>metaContact</tt> is
+ * Stops the current query.
+ */
+ public void stopFilter()
+ {
+ if (currentQuery != null)
+ currentQuery.cancel();
+ }
+
+ /**
+ * Checks if the given <tt>contact</tt> is matching the current filter.
+ * A <tt>SourceContact</tt> would be matching the filter if its display
+ * name is matching the search string.
+ * @param contact the <tt>ContactListContactDescriptor</tt> to check
+ * @return <tt>true</tt> to indicate that the given <tt>contact</tt> is
* matching the current filter, otherwise returns <tt>false</tt>
*/
- public boolean isMatching(MetaContact metaContact)
+ private boolean isMatching(SourceContact contact)
{
- Matcher matcher = filterPattern.matcher(metaContact.getDisplayName());
+ return isMatching(contact.getDisplayName());
+ }
+
+ /**
+ * Indicates if the given string matches this filter.
+ * @param text the text to check
+ * @return <tt>true</tt> to indicate that the given <tt>text</tt> matches
+ * this filter, <tt>false</tt> - otherwise
+ */
+ private boolean isMatching(String text)
+ {
+ Matcher matcher = filterPattern.matcher(text);
if(matcher.find())
return true;
- Iterator<Contact> contacts = metaContact.getContacts();
- while (contacts.hasNext())
+ return false;
+ }
+
+ /**
+ * Indicates that a contact has been received for a query.
+ * @param event the <tt>ContactReceivedEvent</tt> that notified us
+ */
+ public void contactReceived(ContactReceivedEvent event)
+ {
+ synchronized (resultTreeModel)
{
- Contact contact = contacts.next();
+ addSourceContact(event.getContact());
+ }
+ }
- matcher = filterPattern.matcher(contact.getDisplayName());
+ /**
+ * Indicates that the status of a query has changed.
+ * @param event the <tt>ContactQueryStatusEvent</tt> that notified us
+ */
+ public void queryStatusChanged(ContactQueryStatusEvent event)
+ {
+ int eventType = event.getEventType();
- if (matcher.find())
- return true;
+ // Remove the current query when it's stopped for some reason.
+ // QUERY_COMPLETED, QUERY_COMPLETED, QUERY_ERROR
+ currentQuery = null;
- matcher = filterPattern.matcher(contact.getAddress());
+ if (eventType == ContactQueryStatusEvent.QUERY_ERROR)
+ {
+ //TODO: Show the error to the user??
+ }
- if (matcher.find())
- return true;
+ event.getQuerySource().removeContactQueryListener(this);
+ }
+
+ /**
+ * Adds the list of <tt>sourceContacts</tt> in the current result tree model.
+ * @param sourceContacts the list of <tt>SourceContact</tt>s to add
+ */
+ private void addMatching(List<SourceContact> sourceContacts)
+ {
+ Iterator<SourceContact> contactsIter = sourceContacts.iterator();
+ while (contactsIter.hasNext())
+ {
+ addSourceContact(contactsIter.next());
}
- return false;
}
/**
- * Checks if the given <tt>metaGroup</tt> is matching the current filter. A
- * group is matching the current filter only if it contains at least one
- * child <tt>MetaContact</tt>, which is matching the current filter.
- * @param metaGroup the <tt>MetaContactGroup</tt> to check
- * @return <tt>true</tt> to indicate that the given <tt>metaGroup</tt> is
- * matching the current filter, otherwise returns <tt>false</tt>
+ * Adds the given <tt>sourceContact</tt> to the result tree model.
+ * @param sourceContact the <tt>SourceContact</tt> to add
+ */
+ private void addSourceContact(SourceContact sourceContact)
+ {
+ ContactSourceService contactSource
+ = sourceContact.getContactSource();
+
+ ExternalContactSource sourceUI
+ = TreeContactList.getContactSource(contactSource);
+
+ if (sourceUI != null
+ // ExtendedContactSourceService has already matched the
+ // SourceContact over the pattern
+ && (contactSource instanceof ExtendedContactSourceService)
+ || isMatching(sourceContact))
+ GuiActivator.getContactList().addContact(
+ resultTreeModel,
+ sourceUI.getUIContact(sourceContact),
+ true,
+ false);
+ }
+
+ /**
+ * Sets the search source type: DEFAULT_SOURCE or HISTORY_SOURCE.
+ * @param searchSourceType the type of the search source to set
*/
- public boolean isMatching(MetaContactGroup metaGroup)
+ public void setSearchSourceType(int searchSourceType)
{
- Iterator<MetaContact> contacts = metaGroup.getChildContacts();
+ this.searchSourceType = searchSourceType;
- while (contacts.hasNext())
+ switch(searchSourceType)
{
- MetaContact metaContact = contacts.next();
+ case DEFAULT_SOURCE:
+ contactSources = TreeContactList.getContactSources();
+ break;
+ case HISTORY_SOURCE:
+ {
+ ExternalContactSource historySource
+ = TreeContactList.getContactSource(
+ ContactSourceService.CALL_HISTORY);
- if (isMatching(metaContact))
- return true;
+ Collection<ExternalContactSource> historySources
+ = new LinkedList<ExternalContactSource>();
+
+ historySources.add(historySource);
+ contactSources = historySources;
+ break;
+ }
}
- return false;
}
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/SourceContactRightButtonMenu.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/SourceContactRightButtonMenu.java
new file mode 100644
index 0000000..59bac1b
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/SourceContactRightButtonMenu.java
@@ -0,0 +1,136 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.contactlist;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.call.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.swing.*;
+
+/**
+ * The right button menu for external contact sources.
+ * @see ExternalContactSource
+ */
+public class SourceContactRightButtonMenu
+ extends JPopupMenu
+{
+ private final SourceContact sourceContact;
+
+ /**
+ * Creates an instance of <tt>SourceContactRightButtonMenu</tt> by
+ * specifying the <tt>SourceContact</tt>, for which this menu is created.
+ * @param sourceContact the <tt>SourceContact</tt>, for which this menu is
+ * created
+ */
+ public SourceContactRightButtonMenu(SourceContact sourceContact)
+ {
+ this.sourceContact = sourceContact;
+
+ this.initItems();
+ }
+
+ /**
+ * Initializes menu items.
+ */
+ private void initItems()
+ {
+ ContactDetail cDetail = sourceContact
+ .getPreferredContactDetail(OperationSetBasicTelephony.class);
+
+ if (cDetail != null)
+ add(initCallMenu());
+ }
+
+ /**
+ * Initializes the call menu.
+ * @return the call menu
+ */
+ private Component initCallMenu()
+ {
+ SIPCommMenu callContactMenu = new SIPCommMenu(
+ GuiActivator.getResources().getI18NString("service.gui.CALL"));
+ callContactMenu.setIcon(new ImageIcon(ImageLoader
+ .getImage(ImageLoader.CALL_16x16_ICON)));
+
+ Iterator<ContactDetail> details
+ = sourceContact.getContactDetails(OperationSetBasicTelephony.class)
+ .iterator();
+
+ while (details.hasNext())
+ {
+ final ContactDetail detail = details.next();
+ // add all the contacts that support telephony to the call menu
+ JMenuItem callContactItem = new JMenuItem();
+ callContactItem.setName(detail.getContactAddress());
+ callContactItem.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ ProtocolProviderService protocolProvider
+ = detail.getPreferredProtocolProvider(
+ OperationSetBasicTelephony.class);
+
+ if (protocolProvider != null)
+ CallManager.createCall( protocolProvider,
+ detail.getContactAddress());
+ else
+ CallManager.createCall(detail.getContactAddress());
+ }
+ });
+ callContactMenu.add(callContactItem);
+ }
+ return callContactMenu;
+ }
+
+// private Component initIMMenu()
+// {
+// SIPCommMenu callContactMenu = new SIPCommMenu(
+// GuiActivator.getResources().getI18NString(
+// "service.gui.SEND_MESSAGE"));
+// callContactMenu.setIcon(new ImageIcon(ImageLoader
+// .getImage(ImageLoader.SEND_MESSAGE_16x16_ICON)));
+//
+// Iterator<ContactDetail> details
+// = sourceContact.getContactDetails(
+// OperationSetBasicInstantMessaging.class).iterator();
+//
+// while (details.hasNext())
+// {
+// final ContactDetail detail = details.next();
+// // add all the contacts that support telephony to the call menu
+// JMenuItem callContactItem = new JMenuItem();
+// callContactItem.setName(detail.getContactAddress());
+// callContactItem.addActionListener(new ActionListener()
+// {
+// public void actionPerformed(ActionEvent e)
+// {
+// ProtocolProviderService protocolProvider
+// = detail.getPreferredProtocolProvider(
+// OperationSetBasicInstantMessaging.class);
+//
+// if (protocolProvider != null)
+// CallManager.createCall( protocolProvider,
+// detail.getContactAddress());
+// else
+// GuiActivator.getUIService().getChatWindowManager()
+// .startChat(contactItem);
+// }
+// });
+// callContactMenu.add(callContactItem);
+// }
+// return callContactMenu;
+// }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java
index b708ce3..50903e7 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/TreeContactList.java
@@ -14,12 +14,14 @@ import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
+import org.osgi.framework.*;
+
import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.contactlist.event.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
@@ -31,7 +33,6 @@ import net.java.sip.communicator.util.swing.*;
public class TreeContactList
extends DefaultTreeContactList
implements MetaContactListListener,
- ContactPresenceStatusListener,
MouseListener,
MouseMotionListener,
TreeExpansionListener
@@ -39,10 +40,11 @@ public class TreeContactList
/**
* The logger.
*/
- private static final Logger logger = Logger.getLogger(TreeContactList.class);
+ private static final Logger logger
+ = Logger.getLogger(TreeContactList.class);
/**
- * The tree model.
+ * The default tree model.
*/
private ContactListTreeModel treeModel;
@@ -83,6 +85,18 @@ public class TreeContactList
public static final SearchFilter searchFilter = new SearchFilter();
/**
+ * The call history filter.
+ */
+ public static final CallHistoryFilter historyFilter
+ = new CallHistoryFilter();
+
+ /**
+ * The default filter is initially set to the PresenceFilter. But anyone
+ * could change it by calling setDefaultFilter().
+ */
+ private ContactListFilter defaultFilter = presenceFilter;
+
+ /**
* The current filter.
*/
private ContactListFilter currentFilter;
@@ -98,6 +112,9 @@ public class TreeContactList
*/
private MouseListener[] originalMouseListeners;
+ private static final Collection<ExternalContactSource> contactSources
+ = new LinkedList<ExternalContactSource>();
+
/**
* Indicates if we're currently filtering the tree model. If there's an
* ongoing filtering this variable would be set to true, otherwise the value
@@ -123,18 +140,15 @@ public class TreeContactList
GuiActivator.getContactListService().addMetaContactListListener(this);
- treeModel = new ContactListTreeModel(
- GuiActivator.getContactListService().getRoot());
-
- // By default we set the current filter to be the presence filter.
- presenceFilter.setShowOffline(ConfigurationManager.isShowOffline());
- applyFilter(presenceFilter);
+ treeModel = new ContactListTreeModel();
// We hide the root node as it doesn't represent a real group.
if (isRootVisible())
setRootVisible(false);
this.initKeyActions();
+
+ this.initContactSources();
}
/**
@@ -146,6 +160,9 @@ public class TreeContactList
{
final MetaContactGroup metaGroup = evt.getSourceMetaContactGroup();
+ final UIGroup uiGroup
+ = MetaContactListSource.getUIGroup(metaGroup);
+
SwingUtilities.invokeLater(new Runnable()
{
public void run()
@@ -156,10 +173,9 @@ public class TreeContactList
// the temporary model.
synchronized (tempTreeModel)
{
- if (currentFilter.isMatching(metaGroup))
+ if (uiGroup != null)
{
- GroupNode groupNode
- = treeModel.findGroupNodeByMetaGroup(metaGroup);
+ GroupNode groupNode = uiGroup.getGroupNode();
if (groupNode != null)
groupNode.sort(treeModel);
@@ -178,6 +194,9 @@ public class TreeContactList
{
final MetaContact metaContact = evt.getSourceMetaContact();
+ final UIContact uiContact
+ = MetaContactListSource.createUIContact(metaContact);
+
SwingUtilities.invokeLater(new Runnable()
{
public void run()
@@ -188,8 +207,10 @@ public class TreeContactList
// the temporary model.
synchronized (tempTreeModel)
{
- if (currentFilter.isMatching(metaContact))
- addContact(metaContact);
+ if (currentFilter.isMatching(uiContact))
+ addContact(uiContact);
+ else
+ MetaContactListSource.removeUIContact(metaContact);
}
}
});
@@ -204,6 +225,9 @@ public class TreeContactList
{
final MetaContactGroup metaGroup = evt.getSourceMetaContactGroup();
+ final UIGroup uiGroup
+ = MetaContactListSource.createUIGroup(metaGroup);
+
SwingUtilities.invokeLater(new Runnable()
{
public void run()
@@ -214,8 +238,10 @@ public class TreeContactList
// the temporary model.
synchronized (tempTreeModel)
{
- if (currentFilter.isMatching(metaGroup))
- addGroup(metaGroup);
+ if (currentFilter.isMatching(uiGroup))
+ addGroup(uiGroup);
+ else
+ MetaContactListSource.removeUIGroup(metaGroup);
}
}
});
@@ -230,6 +256,9 @@ public class TreeContactList
{
final MetaContactGroup metaGroup = evt.getSourceMetaContactGroup();
+ final UIGroup uiGroup
+ = MetaContactListSource.getUIGroup(metaGroup);
+
SwingUtilities.invokeLater(new Runnable()
{
public void run()
@@ -240,10 +269,9 @@ public class TreeContactList
// the temporary model.
synchronized (tempTreeModel)
{
- if (currentFilter.isMatching(metaGroup))
+ if (uiGroup != null)
{
- GroupNode groupNode
- = treeModel.findGroupNodeByMetaGroup(metaGroup);
+ GroupNode groupNode = uiGroup.getGroupNode();
if (groupNode != null)
treeModel.nodeChanged(groupNode);
@@ -261,6 +289,10 @@ public class TreeContactList
*/
public void metaContactGroupRemoved(final MetaContactGroupEvent evt)
{
+ final UIGroup uiGroup
+ = MetaContactListSource.getUIGroup(
+ evt.getSourceMetaContactGroup());
+
SwingUtilities.invokeLater(new Runnable()
{
public void run()
@@ -271,7 +303,7 @@ public class TreeContactList
// the temporary model.
synchronized (tempTreeModel)
{
- removeGroup(evt.getSourceMetaContactGroup());
+ removeGroup(uiGroup);
}
}
});
@@ -284,7 +316,9 @@ public class TreeContactList
*/
public void metaContactModified(MetaContactModifiedEvent evt)
{
- final MetaContact metaContact = evt.getSourceMetaContact();
+ final UIContact uiContact
+ = MetaContactListSource.getUIContact(
+ evt.getSourceMetaContact());
SwingUtilities.invokeLater(new Runnable()
{
@@ -296,11 +330,10 @@ public class TreeContactList
// the temporary model.
synchronized (tempTreeModel)
{
- if (currentFilter.isMatching(metaContact))
+ if (uiContact != null)
{
ContactNode contactNode
- = treeModel.findContactNodeByMetaContact(
- metaContact);
+ = uiContact.getContactNode();
if (contactNode != null)
treeModel.nodeChanged(contactNode);
@@ -317,7 +350,15 @@ public class TreeContactList
*/
public void metaContactMoved(final MetaContactMovedEvent evt)
{
- final MetaContact metaContact = evt.getSourceMetaContact();
+ final UIContact uiContact
+ = MetaContactListSource.getUIContact(
+ evt.getSourceMetaContact());
+
+ final UIGroup oldUIGroup
+ = MetaContactListSource.getUIGroup(evt.getOldParent());
+
+ final UIGroup newUIGroup
+ = MetaContactListSource.getUIGroup(evt.getNewParent());
SwingUtilities.invokeLater(new Runnable()
{
@@ -329,22 +370,15 @@ public class TreeContactList
// the temporary model.
synchronized (tempTreeModel)
{
- if (currentFilter.isMatching(metaContact))
+ if (currentFilter.isMatching(uiContact))
{
- GroupNode oldParent
- = treeModel.findGroupNodeByMetaGroup(
- evt.getOldParent());
- GroupNode newParent
- = treeModel.findGroupNodeByMetaGroup(
- evt.getNewParent());
+ GroupNode oldParent = oldUIGroup.getGroupNode();
+ GroupNode newParent = newUIGroup.getGroupNode();
if (oldParent != null)
- oldParent
- .removeMetaContact(treeModel, metaContact);
-
+ oldParent.removeContact(uiContact);
if (newParent != null)
- newParent.sortedAddMetaContact(treeModel,
- metaContact, true);
+ newParent.sortedAddContact(uiContact, true);
}
}
}
@@ -369,8 +403,11 @@ public class TreeContactList
// the temporary model.
synchronized (tempTreeModel)
{
- removeContact( evt.getSourceMetaContact(),
- evt.getParentGroup());
+ UIContact uiContact = MetaContactListSource.getUIContact(
+ evt.getSourceMetaContact());
+
+ if (uiContact != null)
+ removeContact(uiContact);
}
}
});
@@ -383,7 +420,9 @@ public class TreeContactList
*/
public void metaContactRenamed(MetaContactRenamedEvent evt)
{
- final MetaContact metaContact = evt.getSourceMetaContact();
+ final UIContact uiContact
+ = MetaContactListSource.getUIContact(
+ evt.getSourceMetaContact());
SwingUtilities.invokeLater(new Runnable()
{
@@ -395,11 +434,9 @@ public class TreeContactList
// the temporary model.
synchronized (tempTreeModel)
{
- if (currentFilter.isMatching(metaContact))
+ if (uiContact != null)
{
- ContactNode contactNode
- = treeModel.findContactNodeByMetaContact(
- metaContact);
+ ContactNode contactNode = uiContact.getContactNode();
if (contactNode != null)
treeModel.nodeChanged(contactNode);
@@ -416,7 +453,9 @@ public class TreeContactList
*/
public void metaContactAvatarUpdated(MetaContactAvatarUpdateEvent evt)
{
- final MetaContact metaContact = evt.getSourceMetaContact();
+ final UIContact uiContact
+ = MetaContactListSource.getUIContact(
+ evt.getSourceMetaContact());
SwingUtilities.invokeLater(new Runnable()
{
@@ -428,11 +467,9 @@ public class TreeContactList
// the temporary model.
synchronized (tempTreeModel)
{
- if (currentFilter.isMatching(metaContact))
+ if (uiContact != null)
{
- ContactNode contactNode
- = treeModel.findContactNodeByMetaContact(
- metaContact);
+ ContactNode contactNode = uiContact.getContactNode();
if (contactNode != null)
treeModel.nodeChanged(contactNode);
@@ -450,22 +487,34 @@ public class TreeContactList
*/
public void protoContactAdded(ProtoContactEvent evt)
{
- MetaContact parent = evt.getNewParent();
+ final MetaContact metaContact = evt.getNewParent();
- // We synchronize the matching and all MetaContactListener
- // events on the tempTreeModel in order to prevent modification
- // to be done on the actual treeModel while we're working with
- // the temporary model.
- synchronized (tempTreeModel)
- {
- ContactNode contactNode
- = treeModel.findContactNodeByMetaContact(parent);
+ final UIContact parentUIContact
+ = MetaContactListSource.getUIContact(metaContact);
- if (contactNode == null && currentFilter.isMatching(parent))
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
{
- addContact(parent);
+ // We synchronize the matching and all MetaContactListener
+ // events on the tempTreeModel in order to prevent modification
+ // to be done on the actual treeModel while we're working with
+ // the temporary model.
+ synchronized (tempTreeModel)
+ {
+ if (parentUIContact == null)
+ {
+ UIContact uiContact
+ = MetaContactListSource.createUIContact(metaContact);
+
+ if (currentFilter.isMatching(uiContact))
+ addContact(uiContact);
+ else
+ MetaContactListSource.removeUIContact(metaContact);
+ }
+ }
}
- }
+ });
}
public void protoContactModified(ProtoContactEvent evt) {}
@@ -478,35 +527,46 @@ public class TreeContactList
*/
public void protoContactMoved(ProtoContactEvent evt)
{
- // Remove old parent if not matching.
- MetaContact oldParent = evt.getOldParent();
+ final MetaContact oldParent = evt.getOldParent();
+ final MetaContact newParent = evt.getNewParent();
- // We synchronize the matching and all MetaContactListener
- // events on the tempTreeModel in order to prevent modification
- // to be done on the actual treeModel while we're working with
- // the temporary model.
- synchronized (tempTreeModel)
+ SwingUtilities.invokeLater(new Runnable()
{
- ContactNode oldContactNode
- = treeModel.findContactNodeByMetaContact(oldParent);
-
- if (oldContactNode != null && !currentFilter.isMatching(oldParent))
+ public void run()
{
- removeContact(oldParent,
- oldParent.getParentMetaContactGroup());
- }
+ // We synchronize the matching and all MetaContactListener
+ // events on the tempTreeModel in order to prevent modification
+ // to be done on the actual treeModel while we're working with
+ // the temporary model.
+ synchronized (tempTreeModel)
+ {
+ final UIContact oldUIContact
+ = MetaContactListSource.getUIContact(oldParent);
+
+ // Remove old parent if not matching.
+ if (oldUIContact != null
+ && !currentFilter.isMatching(oldUIContact))
+ {
+ removeContact(oldUIContact);
+ }
- // Add new parent if matching.
- MetaContact newParent = evt.getNewParent();
+ // Add new parent if matching.
+ UIContact newUIContact
+ = MetaContactListSource.getUIContact(newParent);
- ContactNode newContactNode
- = treeModel.findContactNodeByMetaContact(newParent);
+ if (newUIContact == null)
+ {
+ UIContact uiContact
+ = MetaContactListSource.createUIContact(newParent);
- if (newContactNode == null && currentFilter.isMatching(newParent))
- {
- addContact(newParent);
+ if (currentFilter.isMatching(uiContact))
+ addContact(uiContact);
+ else
+ MetaContactListSource.removeUIContact(newParent);
+ }
+ }
}
- }
+ });
}
/**
@@ -517,23 +577,26 @@ public class TreeContactList
*/
public void protoContactRemoved(ProtoContactEvent evt)
{
- MetaContact oldParent = evt.getOldParent();
+ final MetaContact oldParent = evt.getOldParent();
- // We synchronize the matching and all MetaContactListener
- // events on the tempTreeModel in order to prevent modification
- // to be done on the actual treeModel while we're working with
- // the temporary model.
- synchronized (tempTreeModel)
+ SwingUtilities.invokeLater(new Runnable()
{
- ContactNode contactNode
- = treeModel.findContactNodeByMetaContact(oldParent);
-
- if (contactNode != null && !currentFilter.isMatching(oldParent))
+ public void run()
{
- removeContact(oldParent,
- oldParent.getParentMetaContactGroup());
+ // We synchronize the matching and all MetaContactListener
+ // events on the tempTreeModel in order to prevent modification
+ // to be done on the actual treeModel while we're working with
+ // the temporary model.
+ synchronized (tempTreeModel)
+ {
+ UIContact oldUIContact
+ = MetaContactListSource.getUIContact(oldParent);
+
+ if (oldUIContact != null)
+ removeContact(oldUIContact);
+ }
}
- }
+ });
}
/**
@@ -574,7 +637,8 @@ public class TreeContactList
synchronized (tempTreeModel)
{
ContactNode contactNode
- = treeModel.findContactNodeByMetaContact(metaContact);
+ = MetaContactListSource.getUIContact(metaContact)
+ .getContactNode();
if (contactNode != null)
{
@@ -586,7 +650,7 @@ public class TreeContactList
// SystrayService stray = GuiActivator.getSystrayService();
//
// if (stray != null)
-// stray.setSystrayIcon(SystrayService.ENVELOPE_IMG_TYPE);
+// stray.setSystrayIcon(SystrayService.ENVELOPE_IMG_TYPE);
}
else
activeContacts.remove(contactNode);
@@ -599,14 +663,13 @@ public class TreeContactList
/**
* Returns <tt>true</tt> if the given <tt>metaContact</tt> has been
* previously set to active, otherwise returns <tt>false</tt>.
- * @param metaContact the <tt>MetaContact</tt> to check
+ * @param contact the <tt>UIContact</tt> to check
* @return <tt>true</tt> if the given <tt>metaContact</tt> has been
* previously set to active, otherwise returns <tt>false</tt>
*/
- public boolean isContactActive(MetaContact metaContact)
+ public boolean isContactActive(UIContact contact)
{
- ContactNode contactNode
- = treeModel.findContactNodeByMetaContact(metaContact);
+ ContactNode contactNode = contact.getContactNode();
if (contactNode != null)
return contactNode.isActive();
@@ -614,39 +677,49 @@ public class TreeContactList
}
/**
- * Adds the given <tt>MetaContact</tt> to this list default tree model and
- * refreshes the tree view.
- * @param metaContact the <tt>MetaContact</tt> to add
+ * Adds the given <tt>contact</tt> to this list.
+ * @param contact the <tt>UIContact</tt> to add
*/
- private void addContact(MetaContact metaContact)
+ public void addContact(UIContact contact)
{
- addContact(treeModel, metaContact, true);
+ addContact(treeModel, contact, true, true);
}
/**
- * Adds the given <tt>metaContact</tt> to the given <tt>treeModel</tt>, by
- * specifying if the view should be refreshed.
- * @param treeModel the <tt>ContactListTreeModel</tt> to which to add the
- * given <tt>metaContact</tt>
- * @param metaContact the <tt>MetaContact</tt> to add
+ * Adds the given <tt>contact</tt> to this list.
+ * @param treeModel the <tt>ContactListTreeModel</tt>, to which this contact
+ * should be added
+ * @param contact the <tt>UIContact</tt> to add
+ * @param isSorted indicates if the contact should be sorted regarding to
+ * the <tt>GroupNode</tt> policy
* @param isRefreshView indicates if the view should be refreshed after
- * the contact adding
+ * adding the contact
*/
- private void addContact(ContactListTreeModel treeModel,
- MetaContact metaContact,
+ public void addContact( ContactListTreeModel treeModel,
+ UIContact contact,
+ boolean isSorted,
boolean isRefreshView)
{
- MetaContactGroup metaGroup = metaContact.getParentMetaContactGroup();
+ UIGroup group = contact.getParentGroup();
- GroupNode groupNode = treeModel.findGroupNodeByMetaGroup(metaGroup);
+ GroupNode groupNode;
+ if (group == null)
+ groupNode = treeModel.getRoot();
+ else
+ {
+ groupNode = group.getGroupNode();
- if (groupNode == null)
- groupNode = addGroup(treeModel, metaGroup, isRefreshView);
+ if (groupNode == null)
+ groupNode = addGroup(treeModel, group, isRefreshView);
+ }
- groupNode.sortedAddMetaContact(treeModel, metaContact, isRefreshView);
+ if (isSorted)
+ groupNode.sortedAddContact(contact, isRefreshView);
+ else
+ groupNode.addContact(contact);
- if (currentFilter.equals(presenceFilter)
- && !groupNode.isCollapsed()
+ if ((!currentFilter.equals(presenceFilter)
+ || !groupNode.isCollapsed())
&& isRefreshView)
this.expandGroup(treeModel, groupNode);
}
@@ -654,21 +727,22 @@ public class TreeContactList
/**
* Removes the node corresponding to the given <tt>MetaContact</tt> from
* this list.
- * @param metaContact the <tt>MetaContact</tt> to remove
- * @param parentMetaGroup the <tt>MetaContactGroup</tt> that is the parent
- * of this <tt>metaContact</tt>
+ * @param contact the <tt>UIContact</tt> to remove
*/
- private void removeContact( MetaContact metaContact,
- MetaContactGroup parentMetaGroup)
+ public void removeContact(UIContact contact)
{
- GroupNode parentGroupNode
- = treeModel.findGroupNodeByMetaGroup(parentMetaGroup);
+ UIGroup parentGroup = contact.getParentGroup();
+
+ if (parentGroup == null)
+ return;
+
+ GroupNode parentGroupNode = parentGroup.getGroupNode();
// Nothing more to do here if we didn't find the parent.
if (parentGroupNode == null)
return;
- parentGroupNode.removeMetaContact(treeModel, metaContact);
+ parentGroupNode.removeContact(contact);
// If the parent group is empty remove it.
if (parentGroupNode.getChildCount() < 1
@@ -680,38 +754,44 @@ public class TreeContactList
/**
* Adds the given group to this list.
- * @param metaGroup the <tt>MetaContactGroup</tt> to add
+ * @param group the <tt>UIGroup</tt> to add
* @return the created <tt>GroupNode</tt> corresponding to the group
*/
- private GroupNode addGroup(MetaContactGroup metaGroup)
+ public GroupNode addGroup(UIGroup group)
{
- return addGroup(treeModel, metaGroup, true);
+ return addGroup(treeModel, group, true);
}
/**
- * Adds the given <tt>metaGroup</tt> to the given <tt>treeModel</tt>.
- * @param treeModel the <tt>ContactListTreeModel</tt>, to which to add the
- * given <tt>metaGroup</tt>
- * @param metaGroup the <tt>MetaContactGroup</tt> to add
- * @param isRefreshView indicates if the view should be refresh after the
- * group adding
+ * Adds the given group to this list.
+ * @param treeModel the <tt>ContactListTreeModel</tt>, to which the given
+ * <tt>group</tt> should be added
+ * @param group the <tt>UIGroup</tt> to add
+ * @param isRefreshView indicates if the view should be refreshed after
+ * adding the group
* @return the created <tt>GroupNode</tt> corresponding to the group
*/
- private GroupNode addGroup( ContactListTreeModel treeModel,
- MetaContactGroup metaGroup,
+ public GroupNode addGroup( ContactListTreeModel treeModel,
+ UIGroup group,
boolean isRefreshView)
{
- MetaContactGroup parentGroup = metaGroup.getParentMetaContactGroup();
+ UIGroup parentGroup
+ = group.getParentGroup();
- GroupNode parentGroupNode
- = treeModel.findGroupNodeByMetaGroup(parentGroup);
-
- GroupNode groupNode = null;
- if (parentGroupNode == null)
- addGroup(parentGroup);
+ GroupNode parentGroupNode;
+ if (parentGroup == null)
+ parentGroupNode = treeModel.getRoot();
else
- groupNode = parentGroupNode
- .sortedAddMetaContactGroup(treeModel, metaGroup, isRefreshView);
+ {
+ parentGroupNode = parentGroup.getGroupNode();
+
+ if (parentGroupNode == null)
+ parentGroupNode
+ = addGroup(treeModel, parentGroup, isRefreshView);
+ }
+
+ GroupNode groupNode = parentGroupNode
+ .sortedAddContactGroup(group, isRefreshView);
expandPath(new TreePath(treeModel.getRoot().getPath()));
@@ -720,147 +800,26 @@ public class TreeContactList
/**
* Removes the given group and its children from the list.
- * @param metaGroup the <tt>MetaContactGroup</tt> to remove
+ * @param group the <tt>UIGroup</tt> to remove
*/
- private void removeGroup(MetaContactGroup metaGroup)
+ private void removeGroup(UIGroup group)
{
- MetaContactGroup parentGroup = metaGroup.getParentMetaContactGroup();
+ UIGroup parentGroup = group.getParentGroup();
GroupNode parentGroupNode
- = treeModel.findGroupNodeByMetaGroup(parentGroup);
+ = parentGroup.getGroupNode();
// Nothing more to do here if we didn't find the parent.
if (parentGroupNode == null)
return;
- parentGroupNode.removeMetaContactGroup(treeModel, metaGroup);
+ parentGroupNode.removeContactGroup(group);
// If the parent group is empty remove it.
if (parentGroupNode.getChildCount() < 1)
treeModel.removeNodeFromParent(parentGroupNode);
}
- private void addAllMatching(ContactListTreeModel treeModel)
- {
- addMatching(treeModel,
- GuiActivator.getContactListService().getRoot());
- }
-
- /**
- * Removes all contacts contained in the given <tt>MetaContactGroup</tt> not
- * matching the current filter.
- * @param metaGroup the <tt>MetaContactGroup</tt>, which unmatching contacts
- * to remove
- */
- private void removeUnmatching(MetaContactGroup metaGroup)
- {
- Iterator<MetaContact> childContacts = metaGroup.getChildContacts();
-
- while(childContacts.hasNext())
- {
- MetaContact metaContact = childContacts.next();
- if(!currentFilter.isMatching(metaContact))
- removeContact(metaContact,
- metaContact.getParentMetaContactGroup());
- }
-
- Iterator<MetaContactGroup> subgroups = metaGroup.getSubgroups();
- while(subgroups.hasNext())
- {
- removeUnmatching(subgroups.next());
- }
- }
-
- /**
- * Adds all contacts contained in the given <tt>MetaContactGroup</tt>
- * matching the current filter and not contained in the contact list.
- * @param treeModel the tree model to add to
- * @param metaGroup the <tt>MetaContactGroup</tt>, which matching contacts
- * to add
- */
- private void addMatching( ContactListTreeModel treeModel,
- MetaContactGroup metaGroup)
- {
- Iterator<MetaContact> childContacts = metaGroup.getChildContacts();
-
- // Check also the isFiltering variable in order to be sure that we
- // should continue with this filtering or it has been interrupted and
- // a new one is scheduled.
- while(childContacts.hasNext() && isFiltering)
- {
- MetaContact metaContact = childContacts.next();
-
- if(currentFilter.isMatching(metaContact))
- addContact(treeModel, metaContact, false);
- }
-
- Iterator<MetaContactGroup> subgroups = metaGroup.getSubgroups();
-
- // Check also the isFiltering variable in order to be sure that we
- // should continue with this filtering or it has been interrupted and
- // a new one is scheduled.
- while(subgroups.hasNext() && isFiltering)
- {
- MetaContactGroup subgroup = subgroups.next();
-
- if (subgroup.countChildContacts() == 0
- && subgroup.countSubgroups() == 0
- && currentFilter.isMatching(subgroup))
- addGroup(treeModel, subgroup, false);
- else
- addMatching(treeModel, subgroup);
- }
- }
-
- /**
- * Indicates that a contact has changed its status.
- *
- * @param evt the presence event containing information about the
- * contact status change
- */
- public void contactPresenceStatusChanged(
- final ContactPresenceStatusChangeEvent evt)
- {
- final Contact sourceContact = evt.getSourceContact();
-
- final MetaContact metaContact = GuiActivator.getContactListService()
- .findMetaContactByContact(sourceContact);
-
- if (metaContact == null
- || (evt.getOldStatus() == evt.getNewStatus()))
- return;
-
- SwingUtilities.invokeLater(new Runnable()
- {
- public void run()
- {
- // We synchronize the matching and all MetaContactListener
- // events on the tempTreeModel in order to prevent modification
- // to be done on the actual treeModel while we're working with
- // the temporary model.
- synchronized (tempTreeModel)
- {
- ContactNode contactNode = treeModel
- .findContactNodeByMetaContact(metaContact);
-
- if (contactNode == null
- && currentFilter.isMatching(metaContact))
- {
- addContact(metaContact);
- }
- else if (contactNode != null)
- {
- if (!currentFilter.isMatching(metaContact))
- removeContact(metaContact,
- metaContact.getParentMetaContactGroup());
- else
- treeModel.nodeChanged(contactNode);
- }
- }
- }
- });
- }
-
/**
* Adds a listener for <tt>ContactListEvent</tt>s.
*
@@ -904,13 +863,25 @@ public class TreeContactList
*/
public void stopFiltering()
{
+ currentFilter.stopFilter();
this.isFiltering = false;
}
/**
- * Applies the given <tt>filter</tt>. This is a filter over the content of
- * the contact list.
- * @param filter the new filter to apply
+ * Applies the default filter.
+ * @return <tt>true</tt> to indicate that the filter has found a match,
+ * <tt>false</tt> if no matches were found and the contact list is then
+ * empty.
+ */
+ public boolean applyDefaultFilter()
+ {
+ return applyFilter(defaultFilter);
+ }
+
+ /**
+ * Applies the given <tt>filter</tt> and changes the content of the
+ * contact list according to it.
+ * @param filter the new filter to set
* @return <tt>true</tt> to indicate that the filter has found a match,
* <tt>false</tt> if no matches were found and the contact list is then
* empty.
@@ -924,15 +895,16 @@ public class TreeContactList
if (currentFilter == null || !currentFilter.equals(filter))
this.currentFilter = filter;
- tempTreeModel = new ContactListTreeModel(
- GuiActivator.getContactListService().getRoot());
+ tempTreeModel = new ContactListTreeModel();
// We synchronize the matching and all MetaContactListener events on
// the searchTreeModel in order to prevent modification to be done on
// the actual treeModel while we're working with the temporary model.
synchronized (tempTreeModel)
{
- addAllMatching(tempTreeModel);
+ treeModel.clearDependencies();
+
+ currentFilter.applyFilter(tempTreeModel);
treeModel = tempTreeModel;
}
@@ -977,6 +949,15 @@ public class TreeContactList
}
/**
+ * Sets the default filter to the given <tt>filter</tt>.
+ * @param filter the <tt>ContactListFilter</tt> to set as default
+ */
+ public void setDefaultFilter(ContactListFilter filter)
+ {
+ this.defaultFilter = filter;
+ }
+
+ /**
* Returns the currently applied filter.
* @return the currently applied filter
*/
@@ -1035,9 +1016,10 @@ public class TreeContactList
}
/**
- *
- * @param contactListListeners
- * @param event
+ * Notifies all interested listeners that a <tt>ContactListEvent</tt> has
+ * occurred.
+ * @param contactListListeners the list of listeners to notify
+ * @param event the <tt>ContactListEvent</tt> to trigger
*/
protected void fireContactListEvent(
java.util.List<ContactListListener> contactListListeners,
@@ -1052,11 +1034,8 @@ public class TreeContactList
case ContactListEvent.CONTACT_CLICKED:
listener.contactClicked(event);
break;
- case ContactListEvent.PROTOCOL_CONTACT_CLICKED:
- listener.protocolContactClicked(event);
- break;
case ContactListEvent.GROUP_CLICKED:
- listener.groupSelected(event);
+ listener.groupClicked(event);
break;
default:
logger.error("Unknown event type " + event.getEventID());
@@ -1089,11 +1068,13 @@ public class TreeContactList
{
TreePath treePath = getPathForRow(i);
+ Object c = treePath.getLastPathComponent();
+
GroupNode groupNode = null;
- if (!(treePath.getLastPathComponent() instanceof GroupNode))
+ if (!(c instanceof GroupNode))
continue;
else
- groupNode = (GroupNode) treePath.getLastPathComponent();
+ groupNode = (GroupNode) c;
if (groupNode != null
&& (currentFilter.equals(searchFilter)
@@ -1138,13 +1119,13 @@ public class TreeContactList
if (lastComponent instanceof ContactNode)
{
fireContactListEvent(
- ((ContactNode)lastComponent).getMetaContact(),
+ ((ContactNode) lastComponent).getContactDescriptor(),
ContactListEvent.CONTACT_CLICKED, e.getClickCount());
}
else if (lastComponent instanceof GroupNode)
{
fireContactListEvent(
- ((GroupNode)lastComponent).getMetaContactGroup(),
+ ((GroupNode) lastComponent).getGroupDescriptor(),
ContactListEvent.GROUP_CLICKED, e.getClickCount());
}
@@ -1197,37 +1178,35 @@ public class TreeContactList
// Open message window, right button menu when mouse is pressed.
if (lastComponent instanceof ContactNode)
{
- ContactNode contactNode = (ContactNode) lastComponent;
+ UIContact uiContact
+ = ((ContactNode) lastComponent).getContactDescriptor();
fireContactListEvent(
- contactNode.getMetaContact(),
+ uiContact,
ContactListEvent.CONTACT_CLICKED, e.getClickCount());
- // Right click and Ctrl+LeftClick on the contact label opens
- // Popup menu
if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0
- || (e.isControlDown() && !e.isMetaDown()))
+ || (e.isControlDown() && !e.isMetaDown()))
{
- rightButtonMenu = new ContactRightButtonMenu(
- contactNode.getMetaContact(), this);
+ rightButtonMenu = uiContact.getRightButtonMenu();
openRightButtonMenu(e.getPoint());
}
}
else if (lastComponent instanceof GroupNode)
{
- GroupNode groupNode = (GroupNode) lastComponent;
+ UIGroup uiGroup
+ = ((GroupNode) lastComponent).getGroupDescriptor();
fireContactListEvent(
- groupNode.getMetaContactGroup(),
+ uiGroup,
ContactListEvent.GROUP_CLICKED, e.getClickCount());
if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0
- || (e.isControlDown() && !e.isMetaDown()))
+ || (e.isControlDown() && !e.isMetaDown()))
{
- rightButtonMenu = new GroupRightButtonMenu(
- GuiActivator.getUIService().getMainFrame(),
- groupNode.getMetaContactGroup());
+ rightButtonMenu = uiGroup.getRightButtonMenu();
+
openRightButtonMenu(e.getPoint());
}
}
@@ -1313,9 +1292,10 @@ public class TreeContactList
&& currentFilter.equals(presenceFilter))
{
GroupNode groupNode = (GroupNode) collapsedNode;
- ConfigurationManager
- .setContactListGroupCollapsed(
- groupNode.getMetaContactGroup().getMetaUID(), true);
+ String id = groupNode.getGroupDescriptor().getId();
+ if (id != null)
+ ConfigurationManager
+ .setContactListGroupCollapsed(id, true);
}
}
@@ -1334,9 +1314,10 @@ public class TreeContactList
&& currentFilter.equals(presenceFilter))
{
GroupNode groupNode = (GroupNode) collapsedNode;
- ConfigurationManager
- .setContactListGroupCollapsed(
- groupNode.getMetaContactGroup().getMetaUID(), false);
+ String id = groupNode.getGroupDescriptor().getId();
+ if (id != null)
+ ConfigurationManager
+ .setContactListGroupCollapsed(id, false);
}
}
@@ -1442,21 +1423,23 @@ public class TreeContactList
/**
* Starts a chat with the currently selected contact if any, otherwise
- * nothing happens.
+ * nothing happens. A chat is started with only <tt>MetaContact</tt>s for
+ * now.
*/
public void startSelectedContactChat()
{
TreePath selectionPath = getSelectionPath();
if (selectionPath != null
- && selectionPath.getLastPathComponent()
- instanceof ContactNode)
+ && selectionPath.getLastPathComponent() instanceof ContactNode)
{
- ContactNode contactNode
- = (ContactNode) selectionPath.getLastPathComponent();
+ UIContact uiContact
+ = ((ContactNode) selectionPath.getLastPathComponent())
+ .getContactDescriptor();
- GuiActivator.getUIService().getChatWindowManager()
- .startChat(contactNode.getMetaContact());
+ if (uiContact instanceof MetaUIContact)
+ GuiActivator.getUIService().getChatWindowManager()
+ .startChat((MetaContact) uiContact.getDescriptor());
}
}
@@ -1494,4 +1477,121 @@ public class TreeContactList
setRowHeight(0);
setToggleClickCount(1);
}
+
+ /**
+ * Indicates that a node has been changed. Transfers the event to the
+ * default tree model.
+ * @param node the <tt>TreeNode</tt> that has been refreshed
+ */
+ public void nodeChanged(TreeNode node)
+ {
+ treeModel.nodeChanged(node);
+ }
+
+ /**
+ * Initializes the list of available contact sources for this contact list.
+ */
+ private void initContactSources()
+ {
+ for (ContactSourceService contactSource
+ : GuiActivator.getContactSources())
+ {
+ contactSources.add(new ExternalContactSource(contactSource));
+ }
+ GuiActivator.bundleContext.addServiceListener(
+ new ContactSourceServiceListener());
+ }
+
+ /**
+ * Returns the list of registered contact sources to search in.
+ * @return the list of registered contact sources to search in
+ */
+ public static Collection<ExternalContactSource> getContactSources()
+ {
+ return contactSources;
+ }
+
+ /**
+ * Returns the <tt>ExternalContactSource</tt> corresponding to the given
+ * <tt>ContactSourceService</tt>.
+ * @param contactSource the <tt>ContactSourceService</tt>, which
+ * corresponding external source implementation we're looking for
+ * @return the <tt>ExternalContactSource</tt> corresponding to the given
+ * <tt>ContactSourceService</tt>
+ */
+ public static ExternalContactSource getContactSource(
+ ContactSourceService contactSource)
+ {
+ Iterator<ExternalContactSource> extSourcesIter
+ = contactSources.iterator();
+
+ while (extSourcesIter.hasNext())
+ {
+ ExternalContactSource extSource = extSourcesIter.next();
+
+ if (extSource.getContactSourceService().equals(contactSource))
+ return extSource;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the contact source with the given identifier.
+ * @param identifier the identifier we're looking for
+ * @return the contact source with the given identifier
+ */
+ public static ExternalContactSource getContactSource(String identifier)
+ {
+ Iterator<ExternalContactSource> extSourcesIter
+ = contactSources.iterator();
+
+ while (extSourcesIter.hasNext())
+ {
+ ExternalContactSource extSource = extSourcesIter.next();
+
+ if (extSource.getContactSourceService().getIdentifier()
+ .equals(identifier))
+ return extSource;
+ }
+ return null;
+ }
+
+ /**
+ * Listens for adding and removing of <tt>ContactSourceService</tt>
+ * implementations.
+ */
+ private class ContactSourceServiceListener
+ implements ServiceListener
+ {
+ public void serviceChanged(ServiceEvent event)
+ {
+ ServiceReference serviceRef = event.getServiceReference();
+
+ // if the event is caused by a bundle being stopped, we don't want
+ // to know
+ if (serviceRef.getBundle().getState() == Bundle.STOPPING)
+ return;
+
+ Object service = GuiActivator.bundleContext.getService(serviceRef);
+
+ // we don't care if the source service is
+ // not a contact source service
+ if (!(service instanceof ContactSourceService))
+ return;
+
+ switch (event.getType())
+ {
+ case ServiceEvent.REGISTERED:
+ contactSources.add(
+ new ExternalContactSource((ContactSourceService) service));
+ break;
+ case ServiceEvent.UNREGISTERING:
+ ExternalContactSource cSource
+ = getContactSource((ContactSourceService) service);
+ if (cSource != null)
+ contactSources.remove(cSource);
+ break;
+ }
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/UIContact.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/UIContact.java
new file mode 100644
index 0000000..6f8c92a
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/UIContact.java
@@ -0,0 +1,128 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.contactlist;
+
+import java.util.*;
+import java.util.List;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * The <tt>UIContact</tt> represents the user interface contact contained in the
+ * contact list component.
+ *
+ * @author Yana Stamcheva
+ */
+public interface UIContact
+{
+ /**
+ * Returns the descriptor of this contact.
+ * @return the descriptor of this contact
+ */
+ public Object getDescriptor();
+
+ /**
+ * Returns the display name of this contact.
+ * @return the display name of this contact
+ */
+ public String getDisplayName();
+
+ /**
+ * Returns the display details of this contact. These would be shown
+ * whenever the contact is selected.
+ * @return the display details of this contact
+ */
+ public String getDisplayDetails();
+
+ /**
+ * Returns the index of this contact in its source.
+ * @return the source index
+ */
+ public int getSourceIndex();
+
+ /**
+ * Returns the avatar of this contact.
+ * @param isSelected indicates if the contact is selected
+ * @param width the width of the avatar
+ * @param height the height of the avatar
+ * @return the avatar of this contact
+ */
+ public ImageIcon getAvatar(boolean isSelected, int width, int height);
+
+ /**
+ * Returns the status icon of this contact or null if no status is
+ * available.
+ * @return the status icon of this contact or null if no status is
+ * available
+ */
+ public ImageIcon getStatusIcon();
+
+ /**
+ * Creates a tool tip for this contact. If such tooltip is
+ * provided it would be shown on mouse over over this <tt>UIContact</tt>.
+ * @return the tool tip for this contact descriptor
+ */
+ public ExtendedTooltip getToolTip();
+
+ /**
+ * Returns the right button menu component.
+ * @return the right button menu component
+ */
+ public JPopupMenu getRightButtonMenu();
+
+ /**
+ * Returns the parent group.
+ * @return the parent group
+ */
+ public UIGroup getParentGroup();
+
+ /**
+ * Returns an <tt>Iterator</tt> over a list of the search strings of this
+ * contact.
+ * @return an <tt>Iterator</tt> over a list of the search strings of this
+ * contact
+ */
+ public Iterator<String> getSearchStrings();
+
+ /**
+ * Returns the corresponding <tt>ContactNode</tt>. The <tt>ContactNode</tt>
+ * is the real node that is stored in the contact list component data model.
+ * @return the corresponding <tt>ContactNode</tt>
+ */
+ public ContactNode getContactNode();
+
+ /**
+ * Sets the given <tt>contactNode</tt>. The <tt>ContactNode</tt>
+ * is the real node that is stored in the contact list component data model.
+ * @param contactNode the <tt>ContactNode</tt> that corresponds to this
+ * <tt>UIGroup</tt>
+ */
+ public void setContactNode(ContactNode contactNode);
+
+ /**
+ * Returns the default <tt>ContactDetail</tt> to use for any operations
+ * depending to the given <tt>OperationSet</tt> class.
+ * @param opSetClass the <tt>OperationSet</tt> class we're interested in
+ * @return the default <tt>ContactDetail</tt> to use for any operations
+ * depending to the given <tt>OperationSet</tt> class
+ */
+ public UIContactDetail getDefaultContactDetail(
+ Class<? extends OperationSet> opSetClass);
+
+ /**
+ * Returns a list of all <tt>UIContactDetail</tt>s corresponding to the
+ * given <tt>OperationSet</tt> class.
+ * @param opSetClass the <tt>OperationSet</tt> class we're looking for
+ * @return a list of all <tt>UIContactDetail</tt>s corresponding to the
+ * given <tt>OperationSet</tt> class
+ */
+ public List<UIContactDetail> getContactDetailsForOperationSet(
+ Class<? extends OperationSet> opSetClass);
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/UIContactDetail.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/UIContactDetail.java
new file mode 100644
index 0000000..3011735
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/UIContactDetail.java
@@ -0,0 +1,90 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.contactlist;
+
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * The <tt>UIContactDetail</tt> corresponds to a particular contact detail,
+ * phone number, IM identifier, email, etc. which has it's preferred mode of
+ * transport <tt>ProtocolProviderService</tt>.
+ *
+ * @author Yana Stamcheva
+ */
+public abstract class UIContactDetail
+{
+ /**
+ * The address of this detail.
+ */
+ private final String address;
+
+ /**
+ * The display name of this detail.
+ */
+ private final String displayName;
+
+ /**
+ * The <tt>ProtocolProviderService</tt> corresponding to this detail.
+ */
+ private final ProtocolProviderService protocolProvider;
+
+ /**
+ * Creates a <tt>UIContactDetail</tt> by specifying the contact
+ * <tt>address</tt>, the <tt>displayName</tt> and <tt>preferredProvider</tt>.
+ * @param address the contact address
+ * @param displayName the contact display name
+ * @param preferredProvider the preferred protocol provider
+ */
+ public UIContactDetail(
+ String address,
+ String displayName,
+ ProtocolProviderService preferredProvider)
+ {
+ this.address = address;
+ this.displayName = displayName;
+ this.protocolProvider = preferredProvider;
+ }
+
+ /**
+ * Returns the display name of this detail.
+ * @return the display name of this detail
+ */
+ public String getDisplayName()
+ {
+ return displayName;
+ }
+
+ /**
+ * Returns the address of this detail.
+ * @return the address of this detail
+ */
+ public String getAddress()
+ {
+ return address;
+ }
+
+ /**
+ * Returns the protocol provider preferred for contacting this detail for
+ * the given <tt>OperationSet</tt> class.
+ * @param opSetClass the <tt>OperationSet</tt> class for which we're looking
+ * for provider
+ * @return the protocol provider preferred for contacting this detail
+ */
+ public ProtocolProviderService getPreferredProtocolProvider(
+ Class<? extends OperationSet> opSetClass)
+ {
+ return protocolProvider;
+ }
+
+ /**
+ * Returns the <tt>PresenceStatus</tt> of this <tt>ContactDetail</tt> or
+ * null if the detail doesn't support presence.
+ * @return the <tt>PresenceStatus</tt> of this <tt>ContactDetail</tt> or
+ * null if the detail doesn't support presence
+ */
+ public abstract PresenceStatus getPresenceStatus();
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/UIGroup.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/UIGroup.java
new file mode 100644
index 0000000..baf57e2
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/UIGroup.java
@@ -0,0 +1,89 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.contactlist;
+
+import javax.swing.*;
+
+/**
+ * The <tt>UIGroup</tt> represents the user interface contact list group.
+ *
+ * @author Yana Stamcheva
+ */
+public interface UIGroup
+{
+ /**
+ * Returns the descriptor of the group. This would be the underlying object
+ * that should provide all other necessary information for the group.
+ * @return the descriptor of the group
+ */
+ public Object getDescriptor();
+
+ /**
+ * The display name of the group. The display name is the name to be shown
+ * in the contact list group row.
+ * @return the display name of the group
+ */
+ public String getDisplayName();
+
+ /**
+ * Returns the index of this group in its source. In other words this is
+ * the descriptor index.
+ * @return the index of this group in its source
+ */
+ public int getSourceIndex();
+
+ /**
+ * Returns the parent group.
+ * @return the parent group
+ */
+ public UIGroup getParentGroup();
+
+ /**
+ * Indicates if the group is collapsed or expanded.
+ * @return <tt>true</tt> to indicate that the group is collapsed,
+ * <tt>false</tt> to indicate that it's expanded
+ */
+ public boolean isGroupCollapsed();
+
+ /**
+ * Returns the count of online child contacts.
+ * @return the count of online child contacts
+ */
+ public int countOnlineChildContacts();
+
+ /**
+ * Returns the child contacts count.
+ * @return child contacts count
+ */
+ public int countChildContacts();
+
+ /**
+ * Returns the identifier of this group.
+ * @return the identifier of this group
+ */
+ public String getId();
+
+ /**
+ * Returns the <tt>GroupNode</tt> corresponding to this <tt>UIGroup</tt>.
+ * The is the actual node used in the contact list component data model.
+ * @return the <tt>GroupNode</tt> corresponding to this <tt>UIGroup</tt>
+ */
+ public GroupNode getGroupNode();
+
+ /**
+ * Sets the <tt>GroupNode</tt> corresponding to this <tt>UIGroup</tt>.
+ * @param groupNode the <tt>GroupNode</tt> to set. The is the actual
+ * node used in the contact list component data model.
+ */
+ public void setGroupNode(GroupNode groupNode);
+
+ /**
+ * Returns the right button menu for this group.
+ * @return the right button menu component for this group
+ */
+ public JPopupMenu getRightButtonMenu();
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/UnknownContactPanel.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/UnknownContactPanel.java
index 5ff7aa0..9db2fb0 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/UnknownContactPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/UnknownContactPanel.java
@@ -85,7 +85,7 @@ public class UnknownContactPanel
if (searchText == null)
return;
- Vector<ProtocolProviderService> telephonyProviders
+ List<ProtocolProviderService> telephonyProviders
= CallManager.getTelephonyProviders();
if (telephonyProviders.size() == 1)
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java
new file mode 100644
index 0000000..f631b86
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/ExternalContactSource.java
@@ -0,0 +1,201 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.main.contactlist.*;
+import net.java.sip.communicator.service.contactsource.*;
+
+/**
+ * The <tt>ExternalContactSource</tt> is the UI abstraction of the
+ * <tt>ContactSourceService</tt>.
+ *
+ * @author Yana Stamcheva
+ */
+public class ExternalContactSource
+{
+ /**
+ * The <tt>SourceUIGroup</tt> containing all contacts from this source.
+ */
+ private final SourceUIGroup sourceUIGroup;
+
+ private final ContactSourceService contactSource;
+
+ /**
+ * Creates an <tt>ExternalContactSource</tt> based on the given
+ * <tt>ContactSourceService</tt>.
+ * @param contactSource the <tt>ContactSourceService</tt>, on which this
+ * <tt>ExternalContactSource</tt> is based
+ */
+ public ExternalContactSource(ContactSourceService contactSource)
+ {
+ this.contactSource = contactSource;
+ sourceUIGroup
+ = new SourceUIGroup(contactSource.getDisplayName());
+ }
+
+ /**
+ * Returns the corresponding <tt>ContactSourceService</tt>.
+ * @return the corresponding <tt>ContactSourceService</tt>
+ */
+ public ContactSourceService getContactSourceService()
+ {
+ return contactSource;
+ }
+
+ /**
+ * Returns the UI group for this contact source. There's only one group
+ * descriptor per external source.
+ * @return the group descriptor
+ */
+ public UIGroup getUIGroup()
+ {
+ return sourceUIGroup;
+ }
+
+ /**
+ * Returns the <tt>UIContact</tt> corresponding to the given
+ * <tt>sourceContact</tt>.
+ * @param sourceContact the <tt>SourceContact</tt>, for which we search a
+ * corresponding <tt>UIContact</tt>
+ * @return the <tt>UIContact</tt> corresponding to the given
+ * <tt>sourceContact</tt>
+ */
+ public UIContact getUIContact(SourceContact sourceContact)
+ {
+ return new SourceUIContact(sourceContact, sourceUIGroup);
+ }
+
+ /**
+ * The <tt>SourceUIGroup</tt> is the implementation of the UIGroup for the
+ * <tt>ExternalContactSource</tt>. It takes the name of the source and
+ * sets it as a group name.
+ */
+ private class SourceUIGroup
+ implements UIGroup
+ {
+ /**
+ * The display name of the group.
+ */
+ private final String displayName;
+
+ /**
+ * The corresponding group node.
+ */
+ private GroupNode groupNode;
+
+ /**
+ * Creates an instance of <tt>SourceUIGroup</tt>.
+ * @param name the name of the group
+ */
+ public SourceUIGroup(String name)
+ {
+ this.displayName = name;
+ }
+
+ /**
+ * Returns null to indicate that this group doesn't have a parent group
+ * and can be added directly to the root group.
+ * @return null
+ */
+ public UIGroup getParentGroup()
+ {
+ return null;
+ }
+
+ /**
+ * Returns -1 to indicate that this group doesn't have a source index.
+ * @return -1
+ */
+ public int getSourceIndex()
+ {
+ return -1;
+ }
+
+ /**
+ * Returns <tt>false</tt> to indicate that this group is always opened.
+ * @return false
+ */
+ public boolean isGroupCollapsed()
+ {
+ return false;
+ }
+
+ /**
+ * Returns the display name of this group.
+ * @return the display name of this group
+ */
+ public String getDisplayName()
+ {
+ return displayName;
+ }
+
+ /**
+ * Returns -1 to indicate that the child count is unknown.
+ * @return -1
+ */
+ public int countChildContacts()
+ {
+ return -1;
+ }
+
+ /**
+ * Returns -1 to indicate that the child count is unknown.
+ * @return -1
+ */
+ public int countOnlineChildContacts()
+ {
+ return -1;
+ }
+
+ /**
+ * Returns the display name of the group.
+ * @return the display name of the group
+ */
+ public Object getDescriptor()
+ {
+ return displayName;
+ }
+
+ /**
+ * Returns null to indicate that this group doesn't have an identifier.
+ * @return null
+ */
+ public String getId()
+ {
+ return null;
+ }
+
+ /**
+ * Returns the corresponding <tt>GroupNode</tt>.
+ * @return the corresponding <tt>GroupNode</tt>
+ */
+ public GroupNode getGroupNode()
+ {
+ return groupNode;
+ }
+
+ /**
+ * Sets the given <tt>groupNode</tt>.
+ * @param groupNode the <tt>GroupNode</tt> to set
+ */
+ public void setGroupNode(GroupNode groupNode)
+ {
+ this.groupNode = groupNode;
+ }
+
+ /**
+ * Returns the right button menu for this group.
+ * @return null
+ */
+ public JPopupMenu getRightButtonMenu()
+ {
+ return null;
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactListSource.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactListSource.java
new file mode 100644
index 0000000..fa3181e
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactListSource.java
@@ -0,0 +1,244 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
+
+import java.util.*;
+import java.util.regex.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.*;
+import net.java.sip.communicator.service.contactlist.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * The <tt>MetaContactListSource</tt> is an abstraction of the
+ * <tt>MetaContactListService</tt>, which makes the correspondence between a
+ * <tt>MetaContact</tt> and an <tt>UIContact</tt> and between a
+ * <tt>MetaContactGroup</tt> and an <tt>UIGroup</tt>. It is also responsible
+ * for filtering of the <tt>MetaContactListService</tt> through a given pattern.
+ *
+ * @author Yana Stamcheva
+ */
+public class MetaContactListSource
+{
+ /**
+ * The data key of the MetaContactDescriptor object used to store a
+ * reference to this object in its corresponding MetaContact.
+ */
+ public static final String UI_CONTACT_DATA_KEY
+ = MetaUIContact.class.getName() + ".uiContactDescriptor";
+
+ /**
+ * The data key of the MetaGroupDescriptor object used to store a
+ * reference to this object in its corresponding MetaContactGroup.
+ */
+ public static final String UI_GROUP_DATA_KEY
+ = MetaUIGroup.class.getName() + ".uiGroupDescriptor";
+
+ /**
+ * Returns the <tt>UIContact</tt> corresponding to the given
+ * <tt>MetaContact</tt>.
+ * @param metaContact the <tt>MetaContact</tt>, which corresponding UI
+ * contact we're looking for
+ * @return the <tt>UIContact</tt> corresponding to the given
+ * <tt>MetaContact</tt>
+ */
+ public static UIContact getUIContact(MetaContact metaContact)
+ {
+ return (UIContact) metaContact.getData(UI_CONTACT_DATA_KEY);
+ }
+
+ /**
+ * Returns the <tt>UIGroup</tt> corresponding to the given
+ * <tt>MetaContactGroup</tt>.
+ * @param metaGroup the <tt>MetaContactGroup</tt>, which UI group we're
+ * looking for
+ * @return the <tt>UIGroup</tt> corresponding to the given
+ * <tt>MetaContactGroup</tt>
+ */
+ public static UIGroup getUIGroup(MetaContactGroup metaGroup)
+ {
+ return (UIGroup) metaGroup.getData(UI_GROUP_DATA_KEY);
+ }
+
+ /**
+ * Creates a <tt>UIContact</tt> for the given <tt>metaContact</tt>.
+ * @param metaContact the <tt>MetaContact</tt> for which we would like to
+ * create an <tt>UIContact</tt>
+ * @return an <tt>UIContact</tt> for the given <tt>metaContact</tt>
+ */
+ public static UIContact createUIContact(MetaContact metaContact)
+ {
+ UIGroup uiGroup = null;
+ MetaContactGroup parentMetaGroup
+ = metaContact.getParentMetaContactGroup();
+ if (parentMetaGroup != null
+ && !parentMetaGroup.equals(
+ GuiActivator.getContactListService().getRoot()))
+ {
+ uiGroup = MetaContactListSource.getUIGroup(parentMetaGroup);
+
+ if (uiGroup == null)
+ uiGroup = MetaContactListSource.createUIGroup(parentMetaGroup);
+ }
+
+ MetaUIContact descriptor
+ = new MetaUIContact(metaContact, uiGroup);
+ metaContact.setData(UI_CONTACT_DATA_KEY, descriptor);
+
+ return descriptor;
+ }
+
+ /**
+ * Removes the <tt>UIContact</tt> from the given <tt>metaContact</tt>.
+ * @param metaContact the <tt>MetaContact</tt>, which corresponding UI
+ * contact we would like to remove
+ */
+ public static void removeUIContact(MetaContact metaContact)
+ {
+ metaContact.setData(UI_CONTACT_DATA_KEY, null);
+ }
+
+ /**
+ * Creates a <tt>UIGroupDescriptor</tt> for the given <tt>metaGroup</tt>.
+ * @param metaGroup the <tt>MetaContactGroup</tt> for which we would like to
+ * create an <tt>UIContact</tt>
+ * @return a <tt>UIGroup</tt> for the given <tt>metaGroup</tt>
+ */
+ public static UIGroup createUIGroup(MetaContactGroup metaGroup)
+ {
+ MetaUIGroup descriptor
+ = new MetaUIGroup(metaGroup);
+ metaGroup.setData(UI_GROUP_DATA_KEY, descriptor);
+
+ return descriptor;
+ }
+
+ /**
+ * Removes the descriptor from the given <tt>metaGroup</tt>.
+ * @param metaGroup the <tt>MetaContactGroup</tt>, which descriptor we
+ * would like to remove
+ */
+ public static void removeUIGroup(
+ MetaContactGroup metaGroup)
+ {
+ metaGroup.setData(UI_GROUP_DATA_KEY, null);
+ }
+
+ /**
+ * Filters the <tt>MetaContactListService</tt> to match the given
+ * <tt>filterPattern</tt> and stores the result in the given
+ * <tt>treeModel</tt>.
+ * @param filterPattern the pattern to filter through
+ * @param treeModel the <tt>ContactListTreeModel</tt>, in which we store
+ * the results
+ */
+ public void filter(Pattern filterPattern, ContactListTreeModel treeModel)
+ {
+ filter(filterPattern, treeModel,
+ GuiActivator.getContactListService().getRoot());
+ }
+
+ /**
+ * Filters the children in the given <tt>MetaContactGroup</tt> to match the
+ * given <tt>filterPattern</tt> and stores the result in the given
+ * <tt>treeModel</tt>.
+ * @param filterPattern the pattern to filter through
+ * @param treeModel the <tt>ContactListTreeModel</tt>, in which we store
+ * the results
+ * @param parentGroup the <tt>MetaContactGroup</tt> to filter
+ */
+ private void filter(Pattern filterPattern,
+ ContactListTreeModel treeModel,
+ MetaContactGroup parentGroup)
+ {
+ Iterator<MetaContact> childContacts = parentGroup.getChildContacts();
+
+ while (childContacts.hasNext())
+ {
+ MetaContact metaContact = childContacts.next();
+
+ if (isMatching(filterPattern, metaContact))
+ {
+ GuiActivator.getContactList().addContact(
+ treeModel,
+ MetaContactListSource.createUIContact(metaContact),
+ true,
+ false);
+ }
+ }
+
+ Iterator<MetaContactGroup> subgroups = parentGroup.getSubgroups();
+ while (subgroups.hasNext())
+ {
+ MetaContactGroup subgroup = subgroups.next();
+
+ filter(filterPattern, treeModel, subgroup);
+ }
+ }
+
+ /**
+ * Checks if the given <tt>metaContact</tt> is matching the given
+ * <tt>filterPattern</tt>.
+ * A <tt>MetaContact</tt> would be matching the filter if one of the
+ * following is true:<br>
+ * - its display name contains the filter string
+ * - at least one of its child protocol contacts has a display name or an
+ * address that contains the filter string.
+ * @param filterPattern the filter pattern to check for matches
+ * @param metaContact the <tt>MetaContact</tt> to check
+ * @return <tt>true</tt> to indicate that the given <tt>metaContact</tt> is
+ * matching the current filter, otherwise returns <tt>false</tt>
+ */
+ private boolean isMatching(Pattern filterPattern, MetaContact metaContact)
+ {
+ Matcher matcher = filterPattern.matcher(metaContact.getDisplayName());
+
+ if(matcher.find())
+ return true;
+
+ Iterator<Contact> contacts = metaContact.getContacts();
+ while (contacts.hasNext())
+ {
+ Contact contact = contacts.next();
+
+ matcher = filterPattern.matcher(contact.getDisplayName());
+
+ if (matcher.find())
+ return true;
+
+ matcher = filterPattern.matcher(contact.getAddress());
+
+ if (matcher.find())
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if the given <tt>metaGroup</tt> is matching the current filter. A
+ * group is matching the current filter only if it contains at least one
+ * child <tt>MetaContact</tt>, which is matching the current filter.
+ * @param filterPattern the filter pattern to check for matches
+ * @param metaGroup the <tt>MetaContactGroup</tt> to check
+ * @return <tt>true</tt> to indicate that the given <tt>metaGroup</tt> is
+ * matching the current filter, otherwise returns <tt>false</tt>
+ */
+ public boolean isMatching(Pattern filterPattern, MetaContactGroup metaGroup)
+ {
+ Iterator<MetaContact> contacts = metaGroup.getChildContacts();
+
+ while (contacts.hasNext())
+ {
+ MetaContact metaContact = contacts.next();
+
+ if (isMatching(filterPattern, metaContact))
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIContact.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIContact.java
new file mode 100644
index 0000000..7a1188e
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIContact.java
@@ -0,0 +1,408 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
+
+import java.util.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.contactlist.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * The <tt>MetaUIContact</tt> is the implementation of the UIContact interface
+ * for the <tt>MetaContactListService</tt>. This implementation is based on the
+ * <tt>MetaContact</tt>.
+ *
+ * @author Yana Stamcheva
+ */
+public class MetaUIContact
+ implements UIContact
+{
+ /**
+ * The key of the user data in <tt>MetaContact</tt> which specifies
+ * the avatar cached from previous invocations.
+ */
+ private static final String AVATAR_DATA_KEY
+ = MetaUIContact.class.getName() + ".avatar";
+
+ /**
+ * A list of all search strings available for the underlying
+ * <tt>MetaContact</tt>.
+ */
+ private final List<String> searchStrings = new LinkedList<String>();
+
+ /**
+ * The <tt>MetaContact</tt>, on which this implementation is based.
+ */
+ private MetaContact metaContact;
+
+ /**
+ * The corresponding <tt>ContactNode</tt> in the contact list component
+ * data model.
+ */
+ private ContactNode contactNode;
+
+ /**
+ * The parent <tt>UIGroup</tt> of this contact.
+ */
+ private UIGroup parentUIGroup;
+
+ /**
+ * Creates an instance of <tt>MetaUIContact</tt> by specifying the
+ * underlying <tt>MetaContact</tt>, on which it's based.
+ * @param metaContact the <tt>MetaContact</tt>, on which this implementation
+ * is based
+ * @param group the parent <tt>UIGroup</tt>
+ */
+ public MetaUIContact(MetaContact metaContact, UIGroup group)
+ {
+ this.metaContact = metaContact;
+ this.parentUIGroup = group;
+
+ initSearchStrings();
+ }
+
+ /**
+ * Returns the underlying <tt>MetaContact</tt>.
+ * @return the underlying <tt>MetaContact</tt>
+ */
+ public Object getDescriptor()
+ {
+ return metaContact;
+ }
+
+ /**
+ * Returns the display name of this <tt>MetaUIContact</tt>.
+ * @return the display name of this <tt>MetaUIContact</tt>
+ */
+ public String getDisplayName()
+ {
+ return metaContact.getDisplayName();
+ }
+
+ /**
+ * Returns the index of the underlying <tt>MetaContact</tt> in its
+ * <tt>MetaContactListService</tt> parent group.
+ * @return the source index of the underlying <tt>MetaContact</tt>
+ */
+ public int getSourceIndex()
+ {
+ return metaContact.getParentMetaContactGroup().indexOf(metaContact);
+ }
+
+ /**
+ * Returns an <tt>Iterator</tt> over a list of strings, which can be used
+ * to find this contact.
+ * @return an <tt>Iterator</tt> over a list of search strings
+ */
+ public Iterator<String> getSearchStrings()
+ {
+ return searchStrings.iterator();
+ }
+
+ /**
+ * Returns the general status icon 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.
+ *
+ * @return PresenceStatus The most "available" status from all
+ * sub-contact statuses.
+ */
+ public ImageIcon getStatusIcon()
+ {
+ 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;
+ }
+
+ if (status != null)
+ return new ImageIcon(Constants.getStatusIcon(status));
+
+ return null;
+ }
+
+ /**
+ * Returns the parent <tt>UIGroup</tt>.
+ * @return the parent <tt>UIGroup</tt>
+ */
+ public UIGroup getParentGroup()
+ {
+ return parentUIGroup;
+ }
+
+ /**
+ * Returns the default <tt>ContactDetail</tt> to use for any operations
+ * depending to the given <tt>OperationSet</tt> class.
+ * @param opSetClass the <tt>OperationSet</tt> class we're interested in
+ * @return the default <tt>ContactDetail</tt> to use for any operations
+ * depending to the given <tt>OperationSet</tt> class
+ */
+ public UIContactDetail getDefaultContactDetail(
+ Class<? extends OperationSet> opSetClass)
+ {
+ List<UIContactDetail> details
+ = getContactDetailsForOperationSet(opSetClass);
+
+ if (details != null && !details.isEmpty())
+ return details.get(0);
+
+ return null;
+ }
+
+ /**
+ * Returns a list of <tt>UIContactDetail</tt>s supporting the given
+ * <tt>OperationSet</tt> class.
+ * @param opSetClass the <tt>OperationSet</tt> class we're interested in
+ * @return a list of <tt>UIContactDetail</tt>s supporting the given
+ * <tt>OperationSet</tt> class
+ */
+ public List<UIContactDetail> getContactDetailsForOperationSet(
+ Class<? extends OperationSet> opSetClass)
+ {
+ List<UIContactDetail> resultList
+ = new LinkedList<UIContactDetail>();
+
+ Iterator<Contact> contacts
+ = metaContact.getContactsForOperationSet(opSetClass).iterator();
+
+ while (contacts.hasNext())
+ {
+ resultList.add(new MetaContactDetail(contacts.next()));
+ }
+ return resultList;
+ }
+
+ /**
+ * Gets the avatar of a specific <tt>MetaContact</tt> in the form of an
+ * <tt>ImageIcon</tt> value.
+ *
+ * @param isSelected indicates if the contact is selected
+ * @param width the desired icon width
+ * @param height the desired icon height
+ * @return an <tt>ImageIcon</tt> which represents the avatar of the
+ * specified <tt>MetaContact</tt>
+ */
+ public ImageIcon getAvatar(
+ boolean isSelected, int width, int height)
+ {
+ byte[] avatarBytes = metaContact.getAvatar(true);
+ ImageIcon avatar = null;
+
+ // If there'rs no avatar we have nothing more to do here.
+ if((avatarBytes == null) || (avatarBytes.length <= 0))
+ return null;
+
+ // If the cell is selected we return a zoomed version of the avatar
+ // image.
+ if (isSelected)
+ return ImageUtils.getScaledRoundedIcon(
+ avatarBytes,
+ width,
+ height);
+
+ // In any other case try to get the avatar from the cache.
+ Object[] avatarCache
+ = (Object[]) metaContact.getData(AVATAR_DATA_KEY);
+
+ if ((avatarCache != null) && (avatarCache[0] == avatarBytes))
+ avatar = (ImageIcon) avatarCache[1];
+
+ // Just
+ int avatarWidth = width;
+ int avatarHeight = height;
+
+ // If the avatar isn't available or it's not up-to-date, create it.
+ if (avatar == null)
+ avatar = ImageUtils.getScaledRoundedIcon(
+ avatarBytes,
+ avatarWidth,
+ avatarHeight);
+
+ // Cache the avatar in case it has changed.
+ if (avatarCache == null)
+ {
+ if (avatar != null)
+ metaContact.setData(
+ AVATAR_DATA_KEY,
+ new Object[] { avatarBytes, avatar });
+ }
+ else
+ {
+ avatarCache[0] = avatarBytes;
+ avatarCache[1] = avatar;
+ }
+
+ return avatar;
+ }
+
+ /**
+ * Returns the display details for the underlying <tt>MetaContact</tt>.
+ * @return the display details for the underlying <tt>MetaContact</tt>
+ */
+ public String getDisplayDetails()
+ {
+ String statusMessage = null;
+ Iterator<Contact> protoContacts = metaContact.getContacts();
+
+ while (protoContacts.hasNext())
+ {
+ Contact protoContact = protoContacts.next();
+
+ statusMessage = protoContact.getStatusMessage();
+ if (statusMessage != null && statusMessage.length() > 0)
+ break;
+ }
+
+ return statusMessage;
+ }
+
+ /**
+ * Returns the tool tip opened on mouse over.
+ * @return the tool tip opened on mouse over
+ */
+ public ExtendedTooltip getToolTip()
+ {
+ ExtendedTooltip tip = new ExtendedTooltip(true);
+
+ 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);
+
+ return tip;
+ }
+
+ /**
+ * Returns the corresponding <tt>ContactNode</tt> in the contact list
+ * component data model.
+ * @return the corresponding <tt>ContactNode</tt>
+ */
+ public ContactNode getContactNode()
+ {
+ return contactNode;
+ }
+
+ /**
+ * Sets the corresponding <tt>ContactNode</tt>.
+ * @param contactNode the corresponding <tt>ContactNode</tt> in the contact
+ * list component data model
+ */
+ public void setContactNode(ContactNode contactNode)
+ {
+ this.contactNode = contactNode;
+ if (contactNode == null)
+ MetaContactListSource.removeUIContact(metaContact);
+ }
+
+ /**
+ * Initializes all search strings for this <tt>MetaUIGroup</tt>.
+ */
+ private void initSearchStrings()
+ {
+ searchStrings.add(metaContact.getDisplayName());
+
+ Iterator<Contact> contacts = metaContact.getContacts();
+ while (contacts.hasNext())
+ {
+ Contact contact = contacts.next();
+
+ searchStrings.add(contact.getDisplayName());
+ searchStrings.add(contact.getAddress());
+ }
+ }
+
+ /**
+ * The implementation of the <tt>UIContactDetail</tt> interface for the
+ * <tt>MetaContactListService</tt>.
+ */
+ private class MetaContactDetail extends UIContactDetail
+ {
+ /**
+ * The underlying protocol contact.
+ */
+ private Contact contact;
+
+ /**
+ * Creates an instance of <tt>MetaContactDetail</tt> by specifying the
+ * underlying protocol <tt>Contact</tt>.
+ * @param contact the protocol contact, on which this implementation
+ * is based
+ */
+ public MetaContactDetail(Contact contact)
+ {
+ super( contact.getAddress(),
+ contact.getDisplayName(),
+ contact.getProtocolProvider());
+
+ this.contact = contact;
+ }
+
+ /**
+ * Returns the presence status of the underlying protocol
+ * <tt>Contact</tt>.
+ * @return the presence status of the underlying protocol
+ * <tt>Contact</tt>
+ */
+ public PresenceStatus getPresenceStatus()
+ {
+ return contact.getPresenceStatus();
+ }
+ }
+
+ /**
+ * Returns the right button menu component.
+ * @return the right button menu component
+ */
+ public JPopupMenu getRightButtonMenu()
+ {
+ return new MetaContactRightButtonMenu(metaContact);
+ }
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIGroup.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIGroup.java
new file mode 100644
index 0000000..1a3ae48
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaUIGroup.java
@@ -0,0 +1,164 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.contactlist.*;
+
+/**
+ * The <tt>MetaUIGroup</tt> is the implementation of the UIGroup for the
+ * <tt>MetaContactListService</tt>. This implementation is based on the
+ * <tt>MetaContactGroup</tt>.
+ *
+ * @author Yana Stamcheva
+ */
+public class MetaUIGroup
+ implements UIGroup
+{
+ /**
+ * The <tt>MetaContactGroup</tt>, on which this UI group is based.
+ */
+ private final MetaContactGroup metaGroup;
+
+ /**
+ * The corresponding <tt>GroupNode</tt> in the contact list component data
+ * model.
+ */
+ private GroupNode groupNode;
+
+ /**
+ * Creates an instance of <tt>MetaUIGroup</tt> by specifying the underlying
+ * <tt>MetaContactGroup</tt>.
+ * @param metaGroup the <tt>MetaContactGroup</tt>, on which this UI group
+ * is based
+ */
+ public MetaUIGroup(MetaContactGroup metaGroup)
+ {
+ this.metaGroup = metaGroup;
+ }
+
+ /**
+ * Returns the underlying <tt>MetaContactGroup</tt>.
+ * @return the underlying <tt>MetaContactGroup</tt>
+ */
+ public Object getDescriptor()
+ {
+ return metaGroup;
+ }
+
+ /**
+ * Returns the index of the underlying <tt>MetaContactGroup</tt> in its
+ * <tt>MetaContactListService</tt> parent group.
+ * @return the source index of the underlying <tt>MetaContactGroup</tt>
+ */
+ public int getSourceIndex()
+ {
+ return metaGroup.getParentMetaContactGroup().indexOf(metaGroup);
+ }
+
+ /**
+ * Returns the parent <tt>UIGroup</tt>.
+ * @return the parent <tt>UIGroup</tt>
+ */
+ public UIGroup getParentGroup()
+ {
+ MetaContactGroup parentGroup = metaGroup.getParentMetaContactGroup();
+
+ if (parentGroup != null
+ && !parentGroup.equals(
+ GuiActivator.getContactListService().getRoot()))
+ return new MetaUIGroup(parentGroup);
+
+ return null;
+ }
+
+ /**
+ * Indicates if this group was collapsed.
+ * @return <tt>true</tt> to indicate that this group has been collapsed,
+ * <tt>false</tt> - otherwise
+ */
+ public boolean isGroupCollapsed()
+ {
+ return ConfigurationManager
+ .isContactListGroupCollapsed(metaGroup.getMetaUID());
+ }
+
+ /**
+ * Returns the display name of the underlying <tt>MetaContactGroup</tt>.
+ * @return the display name of the underlying <tt>MetaContactGroup</tt>
+ */
+ public String getDisplayName()
+ {
+ return metaGroup.getGroupName();
+ }
+
+ /**
+ * Returns the count of child contacts of the underlying
+ * <tt>MetaContactGroup</tt>.
+ * @return the count of child contacts
+ */
+ public int countChildContacts()
+ {
+ return metaGroup.countChildContacts();
+ }
+
+ /**
+ * Returns the count of online child contacts of the underlying
+ * <tt>MetaContactGroup</tt>.
+ * @return the count of online child contacts
+ */
+ public int countOnlineChildContacts()
+ {
+ return metaGroup.countOnlineChildContacts();
+ }
+
+ /**
+ * Returns the identifier of the underlying <tt>MetaContactGroup</tt>.
+ * @return the identifier of the underlying <tt>MetaContactGroup</tt>
+ */
+ public String getId()
+ {
+ return metaGroup.getMetaUID();
+ }
+
+ /**
+ * Returns the corresponding <tt>GroupNode</tt>.
+ * @return the corresponding <tt>GroupNode</tt>
+ */
+ public GroupNode getGroupNode()
+ {
+ return groupNode;
+ }
+
+ /**
+ * Sets the corresponding <tt>GroupNode</tt>.
+ * @param groupNode the corresponding <tt>GroupNoe</tt> in the contact list
+ * component data model
+ */
+ public void setGroupNode(GroupNode groupNode)
+ {
+ this.groupNode = groupNode;
+ if (groupNode == null)
+ MetaContactListSource.removeUIGroup(metaGroup);
+ }
+
+ /**
+ * Returns the <tt>JPopupMenu</tt> opened on a right button click over this
+ * group in the contact list.
+ * @return the <tt>JPopupMenu</tt> opened on a right button click over this
+ * group in the contact list
+ */
+ public JPopupMenu getRightButtonMenu()
+ {
+ return new GroupRightButtonMenu(
+ GuiActivator.getUIService().getMainFrame(), metaGroup);
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java
new file mode 100644
index 0000000..16ef84e
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/SourceUIContact.java
@@ -0,0 +1,282 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
+
+import java.util.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.main.contactlist.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * The <tt>SourceUIContact</tt> is the implementation of the UIContact for the
+ * <tt>ExternalContactSource</tt>.
+ *
+ * @author Yana Stamcheva
+ */
+public class SourceUIContact
+ implements UIContact
+{
+ /**
+ * The corresponding <tt>SourceContact</tt>, on which this abstraction is
+ * based.
+ */
+ private final SourceContact sourceContact;
+
+ /**
+ * The corresponding <tt>ContactNode</tt> in the contact list component.
+ */
+ private ContactNode contactNode;
+
+ /**
+ * The parent <tt>UIGroup</tt>.
+ */
+ private UIGroup uiGroup;
+
+ /**
+ * Creates an instance of <tt>SourceUIContact</tt> by specifying the
+ * <tt>SourceContact</tt>, on which this abstraction is based and the
+ * parent <tt>UIGroup</tt>.
+ *
+ * @param contact the <tt>SourceContact</tt>, on which this abstraction
+ * is based
+ * @param parentGroup the parent <tt>UIGroup</tt>
+ */
+ public SourceUIContact( SourceContact contact,
+ UIGroup parentGroup)
+ {
+ this.sourceContact = contact;
+ this.uiGroup = parentGroup;
+ }
+
+ /**
+ * Returns the display name of the underlying <tt>SourceContact</tt>.
+ * @return the display name
+ */
+ public String getDisplayName()
+ {
+ return sourceContact.getDisplayName();
+ }
+
+ /**
+ * Returns the parent <tt>UIGroup</tt>.
+ * @return the parent <tt>UIGroup</tt>
+ */
+ public UIGroup getParentGroup()
+ {
+ return uiGroup;
+ }
+
+ /**
+ * Returns -1 to indicate that the source index of the underlying
+ * <tt>SourceContact</tt> is unknown.
+ * @return -1
+ */
+ public int getSourceIndex()
+ {
+ return -1;
+ }
+
+ /**
+ * Returns null to indicate unknown status of the underlying
+ * <tt>SourceContact</tt>.
+ * @return null
+ */
+ public ImageIcon getStatusIcon()
+ {
+ return null;
+ }
+
+ /**
+ * Returns the image corresponding to the underlying <tt>SourceContact</tt>.
+ * @param isSelected indicates if the contact is currently selected in the
+ * contact list component
+ * @param width the desired image width
+ * @param height the desired image height
+ * @return the image
+ */
+ public ImageIcon getAvatar(boolean isSelected, int width, int height)
+ {
+ ImageIcon icon = new ImageIcon(sourceContact.getImage());
+
+ if (icon.getIconWidth() > width
+ || icon.getIconHeight() > height)
+ {
+ icon = ImageUtils
+ .getScaledRoundedIcon(icon.getImage(), width, height);
+ }
+
+ return icon;
+ }
+
+ /**
+ * Returns the default <tt>ContactDetail</tt> to use for any operations
+ * depending to the given <tt>OperationSet</tt> class.
+ * @param opSetClass the <tt>OperationSet</tt> class we're interested in
+ * @return the default <tt>ContactDetail</tt> to use for any operations
+ * depending to the given <tt>OperationSet</tt> class
+ */
+ public UIContactDetail getDefaultContactDetail(
+ Class<? extends OperationSet> opSetClass)
+ {
+ List<UIContactDetail> details
+ = getContactDetailsForOperationSet(opSetClass);
+
+ if (details != null && !details.isEmpty())
+ return details.get(0);
+ return null;
+ }
+
+ /**
+ * Returns the underlying <tt>SourceContact</tt> this abstraction is about.
+ * @return the underlying <tt>SourceContact</tt>
+ */
+ public Object getDescriptor()
+ {
+ return sourceContact;
+ }
+
+ /**
+ * Returns the display details for the underlying <tt>SourceContact</tt>.
+ * @return the display details for the underlying <tt>SourceContact</tt>
+ */
+ public String getDisplayDetails()
+ {
+ return sourceContact.getDisplayDetails();
+ }
+
+ /**
+ * Returns a list of <tt>UIContactDetail</tt>s supporting the given
+ * <tt>OperationSet</tt> class.
+ * @param opSetClass the <tt>OperationSet</tt> class we're interested in
+ * @return a list of <tt>UIContactDetail</tt>s supporting the given
+ * <tt>OperationSet</tt> class
+ */
+ public List<UIContactDetail> getContactDetailsForOperationSet(
+ Class<? extends OperationSet> opSetClass)
+ {
+ List<UIContactDetail> resultList
+ = new LinkedList<UIContactDetail>();
+
+ Iterator<ContactDetail> details
+ = sourceContact.getContactDetails().iterator();
+
+ while (details.hasNext())
+ {
+ ContactDetail detail = details.next();
+
+ if (detail.getSupportedOperationSets().contains(opSetClass))
+ resultList.add(new SourceContactDetail(detail, opSetClass));
+ }
+ return resultList;
+ }
+
+ public Iterator<String> getSearchStrings()
+ {
+ return null;
+ }
+
+ /**
+ * Returns the corresponding <tt>ContactNode</tt> from the contact list
+ * component.
+ * @return the corresponding <tt>ContactNode</tt>
+ */
+ public ContactNode getContactNode()
+ {
+ return contactNode;
+ }
+
+ /**
+ * Sets the corresponding <tt>ContactNode</tt>.
+ * @param contactNode the corresponding <tt>ContactNode</tt>
+ */
+ public void setContactNode(ContactNode contactNode)
+ {
+ this.contactNode = contactNode;
+ }
+
+ /**
+ * The implementation of the <tt>UIContactDetail</tt> interface for the
+ * external source <tt>ContactDetail</tt>s.
+ */
+ private class SourceContactDetail extends UIContactDetail
+ {
+ /**
+ * Creates an instance of <tt>SourceContactDetail</tt> by specifying
+ * the underlying <tt>detail</tt> and the <tt>OperationSet</tt> class
+ * for it.
+ * @param detail the underlying <tt>ContactDetail</tt>
+ * @param opSetClass the <tt>OperationSet</tt> class for the
+ * preferred protocol provider
+ */
+ public SourceContactDetail( ContactDetail detail,
+ Class<? extends OperationSet> opSetClass)
+ {
+ super( detail.getContactAddress(),
+ detail.getContactAddress(),
+ detail.getPreferredProtocolProvider(opSetClass));
+ }
+
+ /**
+ * Returns null to indicate that this detail doesn't support presence.
+ * @return null
+ */
+ public PresenceStatus getPresenceStatus()
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the <tt>JPopupMenu</tt> opened on a right button click over this
+ * <tt>SourceUIContact</tt>.
+ * @return the <tt>JPopupMenu</tt> opened on a right button click over this
+ * <tt>SourceUIContact</tt>
+ */
+ public JPopupMenu getRightButtonMenu()
+ {
+ return new SourceContactRightButtonMenu(sourceContact);
+ }
+
+ /**
+ * Returns the tool tip opened on mouse over.
+ * @return the tool tip opened on mouse over
+ */
+ public ExtendedTooltip getToolTip()
+ {
+ ExtendedTooltip tip = new ExtendedTooltip(true);
+
+ byte[] avatarImage = sourceContact.getImage();
+
+ if (avatarImage != null && avatarImage.length > 0)
+ tip.setImage(new ImageIcon(avatarImage));
+
+ tip.setTitle(sourceContact.getDisplayName());
+
+ Iterator<ContactDetail> details
+ = sourceContact.getContactDetails().iterator();
+
+ ContactDetail contactDetail;
+ while (details.hasNext())
+ {
+ contactDetail = details.next();
+
+ String contactAddress = contactDetail.getContactAddress();
+ //String statusMessage = protocolContact.getStatusMessage();
+
+ tip.addLine(null, contactAddress);
+ }
+
+ tip.setBottomText(getDisplayDetails());
+
+ return tip;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java b/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java
index fd16561..6cb7a4e 100644
--- a/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java
+++ b/src/net/java/sip/communicator/impl/gui/main/menus/ToolsMenu.java
@@ -119,8 +119,14 @@ public class ToolsMenu
TreeContactList.presenceFilter.setShowOffline(!isShowOffline);
- GuiActivator.getContactList()
- .applyFilter(TreeContactList.presenceFilter);
+ new Thread()
+ {
+ public void run()
+ {
+ GuiActivator.getContactList()
+ .applyFilter(TreeContactList.presenceFilter);
+ }
+ }.start();
ConfigurationManager.setShowOffline(!isShowOffline);
diff --git a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
index 66b3c0b..8139b8e 100644
--- a/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
+++ b/src/net/java/sip/communicator/impl/gui/swing.ui.manifest.mf
@@ -32,6 +32,7 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.systray,
net.java.sip.communicator.service.neomedia,
net.java.sip.communicator.service.neomedia.device,
+ net.java.sip.communicator.service.contactsource,
net.java.sip.communicator.util,
net.java.sip.communicator.util.swing,
net.java.sip.communicator.util.swing.border,
diff --git a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
index 3648667..3f287cc 100644
--- a/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
+++ b/src/net/java/sip/communicator/impl/gui/utils/ImageLoader.java
@@ -278,6 +278,18 @@ public class ImageLoader
= new ImageID("service.gui.buttons.CHAT_CALL");
/**
+ * The call history button image.
+ */
+ public static final ImageID CALL_HISTORY_BUTTON
+ = new ImageID("service.gui.buttons.CALL_HISTORY_BUTTON");
+
+ /**
+ * The call history pressed button image.
+ */
+ public static final ImageID CALL_HISTORY_BUTTON_PRESSED
+ = new ImageID("service.gui.buttons.CALL_HISTORY_BUTTON_PRESSED");
+
+ /**
* The chat button small pressed image.
*/
public static final ImageID CHAT_BUTTON_SMALL_PRESSED
diff --git a/src/net/java/sip/communicator/impl/history/HistoryReaderImpl.java b/src/net/java/sip/communicator/impl/history/HistoryReaderImpl.java
index 1f2adcc..b6ddf02 100644
--- a/src/net/java/sip/communicator/impl/history/HistoryReaderImpl.java
+++ b/src/net/java/sip/communicator/impl/history/HistoryReaderImpl.java
@@ -10,6 +10,8 @@ import java.util.*;
import org.w3c.dom.*;
+import sun.nio.cs.ext.*;
+
import net.java.sip.communicator.service.history.*;
import net.java.sip.communicator.service.history.event.*;
import net.java.sip.communicator.service.history.records.*;
@@ -17,6 +19,7 @@ import net.java.sip.communicator.service.history.records.*;
/**
* @author Alexander Pelov
* @author Damian Minkov
+ * @author Yana Stamcheva
*/
public class HistoryReaderImpl
implements HistoryReader
@@ -30,6 +33,15 @@ public class HistoryReaderImpl
private static String REGEXP_SENSITIVE_START = "(?s)^.*";
private static String REGEXP_INSENSITIVE_START = "(?si)^.*";
+ /**
+ * Indicates if the current find should be canceled.
+ */
+ private boolean isFindCanceled = false;
+
+ /**
+ * Creates an instance of <tt>HistoryReaderImpl</tt>.
+ * @param historyImpl the parent History implementation
+ */
protected HistoryReaderImpl(HistoryImpl historyImpl)
{
this.historyImpl = historyImpl;
@@ -45,7 +57,8 @@ public class HistoryReaderImpl
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
- public synchronized QueryResultSet<HistoryRecord> findByStartDate(Date startDate)
+ public synchronized QueryResultSet<HistoryRecord> findByStartDate(
+ Date startDate)
throws RuntimeException
{
return find(startDate, null, null, null, false);
@@ -498,22 +511,26 @@ public class HistoryReaderImpl
TreeSet<HistoryRecord> result
= new TreeSet<HistoryRecord>(new HistoryRecordComparator());
- Vector<String> filelist =
- filterFilesByDate(this.historyImpl.getFileList(), startDate, endDate);
+ Vector<String> filelist
+ = filterFilesByDate(this.historyImpl.getFileList(),
+ startDate, endDate);
- double currentProgress = HistorySearchProgressListener.PROGRESS_MINIMUM_VALUE;
- double fileProgressStep = HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE;
+ double currentProgress
+ = HistorySearchProgressListener.PROGRESS_MINIMUM_VALUE;
+ double fileProgressStep
+ = HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE;
if(filelist.size() != 0)
- fileProgressStep =
- HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE / filelist.size();
+ fileProgressStep
+ = HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE
+ / filelist.size();
// start progress - minimum value
fireProgressStateChanged(startDate, endDate,
keywords, HistorySearchProgressListener.PROGRESS_MINIMUM_VALUE);
Iterator<String> fileIterator = filelist.iterator();
- while (fileIterator.hasNext())
+ while (fileIterator.hasNext() && !isFindCanceled)
{
String filename = fileIterator.next();
@@ -530,7 +547,7 @@ public class HistoryReaderImpl
nodesProgressStep = fileProgressStep / nodes.getLength();
Node node;
- for (int i = 0; i < nodes.getLength(); i++)
+ for (int i = 0; i < nodes.getLength() && !isFindCanceled; i++)
{
node = nodes.item(i);
@@ -560,13 +577,17 @@ public class HistoryReaderImpl
}
// if maximum value is not reached fire an event
- if((int)currentProgress < HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
+ if((int)currentProgress
+ < HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
{
fireProgressStateChanged(startDate, endDate, keywords,
HistorySearchProgressListener.
PROGRESS_MAXIMUM_VALUE);
}
+ // Before returning we want to reset the isFindCanceled to false.
+ isFindCanceled = false;
+
return new OrderedQueryResultSet<HistoryRecord>(result);
}
@@ -902,4 +923,12 @@ public class HistoryReaderImpl
compareTo(h2.getTimestamp());
}
}
+
+ /**
+ * Cancels the current find. If there's no find going on, then does nothing.
+ */
+ public void cancelCurrentFind()
+ {
+ isFindCanceled = true;
+ }
}
diff --git a/src/net/java/sip/communicator/service/callhistory/CallHistoryQuery.java b/src/net/java/sip/communicator/service/callhistory/CallHistoryQuery.java
new file mode 100644
index 0000000..6465c6c
--- /dev/null
+++ b/src/net/java/sip/communicator/service/callhistory/CallHistoryQuery.java
@@ -0,0 +1,53 @@
+/*
+ * 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.callhistory;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.callhistory.event.*;
+
+/**
+ * The <tt>CallHistoryQuery</tt> corresponds to a query made to the
+ * <tt>CallHistoryService</tt>. It allows to be canceled, to listen for changes
+ * in the results and to obtain initial results if available.
+ *
+ * @author Yana Stamcheva
+ */
+public interface CallHistoryQuery
+{
+ /**
+ * Cancels this query.
+ */
+ public void cancel();
+
+ /**
+ * Returns a collection of the initial results for this query. It's up to
+ * the implementation to determine, which and how many the initial results
+ * would be.
+ * <p>
+ * This method is meant to be used in order to return first fast initial
+ * results and then notify interested parties of additional results through
+ * the <tt>CallHistoryQueryListener</tt>, which should improve user
+ * experience when waiting for results.
+ * @return a collection of the initial results for this query
+ */
+ public Collection<CallRecord> getCallRecords();
+
+ /**
+ * Adds the given <tt>CallHistoryQueryListener</tt> to the list of
+ * listeners interested in query result changes.
+ * @param l the <tt>CallHistoryQueryListener</tt> to add
+ */
+ public void addCallRecordsListener(CallHistoryQueryListener l);
+
+ /**
+ * Removes the given <tt>CallHistoryQueryListener</tt> from the list of
+ * listeners interested in query result changes.
+ * @param l the <tt>CallHistoryQueryListener</tt> to remove
+ */
+ public void removeCallRecordsListener(CallHistoryQueryListener l);
+}
diff --git a/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java b/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java
index 93168bc..dc5f8ff 100644
--- a/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java
+++ b/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java
@@ -21,7 +21,7 @@ public interface CallHistoryService
{
/**
* Returns all the calls made by all the contacts
- * in the supplied metacontact after the given date
+ * in the supplied <tt>contact</tt> after the given date.
*
* @param contact MetaContact which contacts participate in
* the returned calls
@@ -29,12 +29,13 @@ public interface CallHistoryService
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
- public Collection<CallRecord> findByStartDate(MetaContact contact, Date startDate)
+ public Collection<CallRecord> findByStartDate( MetaContact contact,
+ Date startDate)
throws RuntimeException;
/**
* Returns all the calls made by all the contacts
- * in the supplied metacontact before the given date
+ * in the supplied <tt>contact</tt> before the given date.
*
* @param contact MetaContact which contacts participate in
* the returned calls
@@ -42,12 +43,13 @@ public interface CallHistoryService
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
- public Collection<CallRecord> findByEndDate(MetaContact contact, Date endDate)
+ public Collection<CallRecord> findByEndDate(MetaContact contact,
+ Date endDate)
throws RuntimeException;
/**
* Returns all the calls made by all the contacts
- * in the supplied metacontact between the given dates
+ * in the supplied <tt>contact</tt> between the given dates.
*
* @param contact MetaContact which contacts participate in
* the returned calls
@@ -56,12 +58,14 @@ public interface CallHistoryService
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
- public Collection<CallRecord> findByPeriod(MetaContact contact, Date startDate, Date endDate)
+ public Collection<CallRecord> findByPeriod( MetaContact contact,
+ Date startDate,
+ Date endDate)
throws RuntimeException;
/**
- * Returns all the calls made after the given date
+ * Returns all the calls made after the given date.
*
* @param startDate Date the start date of the calls
* @return Collection of CallRecords with CallPeerRecord
@@ -71,7 +75,7 @@ public interface CallHistoryService
throws RuntimeException;
/**
- * Returns all the calls made before the given date
+ * Returns all the calls made before the given date.
*
* @param endDate Date the end date of the calls
* @return Collection of CallRecords with CallPeerRecord
@@ -81,7 +85,7 @@ public interface CallHistoryService
throws RuntimeException;
/**
- * Returns all the calls made between the given dates
+ * Returns all the calls made between the given dates.
*
* @param startDate Date the start date of the calls
* @param endDate Date the end date of the calls
@@ -93,7 +97,7 @@ public interface CallHistoryService
/**
* Returns the supplied number of recent calls made by all the contacts
- * in the supplied metacontact
+ * in the supplied <tt>contact</tt>.
*
* @param contact MetaContact which contacts participate in
* the returned calls
@@ -105,8 +109,7 @@ public interface CallHistoryService
throws RuntimeException;
/**
- * Returns the supplied number of recent calls made by all the contacts
- * in the supplied metacontact
+ * Returns the supplied number of recent calls.
*
* @param count calls count
* @return Collection of CallRecords with CallPeerRecord
@@ -129,12 +132,19 @@ public interface CallHistoryService
*
* @param listener HistorySearchProgressListener
*/
- public void addSearchProgressListener(CallHistorySearchProgressListener listener);
+ public void addSearchProgressListener(
+ CallHistorySearchProgressListener listener);
/**
* Removing progress listener
*
* @param listener HistorySearchProgressListener
*/
- public void removeSearchProgressListener(CallHistorySearchProgressListener listener);
+ public void removeSearchProgressListener(
+ CallHistorySearchProgressListener listener);
+
+ /**
+ * Cancels the current find. If there's no find going on, then does nothing.
+ */
+ public void cancelCurrentFind();
}
diff --git a/src/net/java/sip/communicator/service/callhistory/CallRecord.java b/src/net/java/sip/communicator/service/callhistory/CallRecord.java
index 3308a44..31729ad 100644
--- a/src/net/java/sip/communicator/service/callhistory/CallRecord.java
+++ b/src/net/java/sip/communicator/service/callhistory/CallRecord.java
@@ -1,31 +1,62 @@
+/*
+ * 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.callhistory;
import java.util.*;
+import net.java.sip.communicator.service.protocol.*;
+
/**
* Structure used for encapsulating data when writing or reading
- * Call History Data. Also These records are uesd for returning data
- * from the Call History Service
+ * Call History Data. Also these records are used for returning data
+ * from the Call History Service.
*
* @author Damian Minkov
+ * @author Yana Stamcheva
*/
public class CallRecord
{
/**
- * Possible directions of the call
+ * The outgoing call direction.
*/
public final static String OUT = "out";
+
+ /**
+ * The incoming call direction.
+ */
public final static String IN = "in";
+ /**
+ * Indicates the direction of the call - IN or OUT.
+ */
protected String direction = null;
+ /**
+ * A list of all peer records corresponding to this call record.
+ */
protected final List<CallPeerRecord> peerRecords =
new Vector<CallPeerRecord>();
+ /**
+ * The start call date.
+ */
protected Date startTime = null;
+
+ /**
+ * The end call date.
+ */
protected Date endTime = null;
/**
+ * The protocol provider through which the call was made.
+ */
+ protected ProtocolProviderService protocolProvider;
+
+ /**
* Creates CallRecord
*/
public CallRecord()
@@ -101,4 +132,14 @@ public class CallRecord
{
return startTime;
}
+
+ /**
+ * Returns the protocol provider used for the call. Could be null if the
+ * record has not saved the provider.
+ * @return the protocol provider used for the call
+ */
+ public ProtocolProviderService getProtocolProvider()
+ {
+ return protocolProvider;
+ }
}
diff --git a/src/net/java/sip/communicator/service/callhistory/event/CallHistoryQueryListener.java b/src/net/java/sip/communicator/service/callhistory/event/CallHistoryQueryListener.java
new file mode 100644
index 0000000..b646036
--- /dev/null
+++ b/src/net/java/sip/communicator/service/callhistory/event/CallHistoryQueryListener.java
@@ -0,0 +1,26 @@
+/*
+ * 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.callhistory.event;
+
+/**
+ * The <tt>CallHistoryQueryListener</tt> listens for changes in the result of
+ * a given <tt>CallHistoryQuery</tt>. When a query to the call history is
+ * started, this listener would be notified every time new results are available
+ * for this query.
+ *
+ * @author Yana Stamcheva
+ */
+public interface CallHistoryQueryListener
+{
+ /**
+ * Indicates that new <tt>CallRecord</tt>s are received as a result of the
+ * query.
+ * @param event the <tt>CallRecordsEvent</tt> containing information about
+ * the query results.
+ */
+ public void callRecordsReceived(CallRecordsEvent event);
+}
diff --git a/src/net/java/sip/communicator/service/callhistory/event/CallRecordsEvent.java b/src/net/java/sip/communicator/service/callhistory/event/CallRecordsEvent.java
new file mode 100644
index 0000000..2e3b0f0
--- /dev/null
+++ b/src/net/java/sip/communicator/service/callhistory/event/CallRecordsEvent.java
@@ -0,0 +1,61 @@
+/*
+ * 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.callhistory.event;
+
+import java.util.*;
+
+import net.java.sip.communicator.service.callhistory.*;
+
+/**
+ * The <tt>CallRecordsEvent</tt> indicates that one or more
+ * <tt>CallRecord</tt>s has been received as a result of a
+ * <tt>CallHistoryQuery</tt>.
+ *
+ * @author Yana Stamcheva
+ */
+public class CallRecordsEvent
+ extends EventObject
+{
+ /**
+ * A collection of call records received as a result of a given
+ * <tt>query</tt>.
+ */
+ private final Collection<CallRecord> callRecords;
+
+ /**
+ * Creates a <tt>ContactReceivedEvent</tt> by specifying the contact search
+ * source and the received <tt>searchContact</tt>.
+ * @param query the source that triggered this event
+ * @param callRecords the call records received as a result from the given
+ * <tt>query</tt>
+ */
+ public CallRecordsEvent(CallHistoryQuery query,
+ Collection<CallRecord> callRecords)
+ {
+ super(query);
+
+ this.callRecords = callRecords;
+ }
+
+ /**
+ * Returns the <tt>ContactQuery</tt> that triggered this event.
+ * @return the <tt>ContactQuery</tt> that triggered this event
+ */
+ public CallHistoryQuery getQuerySource()
+ {
+ return (CallHistoryQuery) source;
+ }
+
+ /**
+ * Returns the collection of <tt>CallRecord</tt>s this event is about.
+ * @return the collection of <tt>CallRecord</tt>s this event is about
+ */
+ public Collection<CallRecord> getCallRecords()
+ {
+ return callRecords;
+ }
+}
diff --git a/src/net/java/sip/communicator/service/contactlist/MetaContactGroup.java b/src/net/java/sip/communicator/service/contactlist/MetaContactGroup.java
index f218b37..c27a762 100644
--- a/src/net/java/sip/communicator/service/contactlist/MetaContactGroup.java
+++ b/src/net/java/sip/communicator/service/contactlist/MetaContactGroup.java
@@ -291,4 +291,40 @@ public interface MetaContactGroup
* @return a String uniquely identifying this meta contact.
*/
public String getMetaUID();
+
+ /**
+ * Gets the user data associated with this instance and a specific key.
+ *
+ * @param key
+ * the key of the user data associated with this instance to be
+ * retrieved
+ * @return an <code>Object</code> which represents the value associated with
+ * this instance and the specified <code>key</code>; <tt>null</tt>
+ * if no association with the specified <code>key</code> exists in
+ * this instance
+ */
+ public Object getData(Object key);
+
+ /**
+ * Sets a user-specific association in this instance in the form of a
+ * key-value pair. If the specified <code>key</code> is already associated
+ * in this instance with a value, the existing value is overwritten with the
+ * specified <code>value</code>.
+ * <p>
+ * The user-defined association created by this method and stored in this
+ * instance is not serialized by this instance and is thus only meant for
+ * runtime use.
+ * </p>
+ * <p>
+ * The storage of the user data is implementation-specific and is thus not
+ * guaranteed to be optimized for execution time and memory use.
+ * </p>
+ *
+ * @param key
+ * the key to associate in this instance with the specified value
+ * @param value
+ * the value to be associated in this instance with the specified
+ * <code>key</code>
+ */
+ public void setData(Object key, Object value);
}
diff --git a/src/net/java/sip/communicator/service/contactsource/ContactDetail.java b/src/net/java/sip/communicator/service/contactsource/ContactDetail.java
index 1985a7f..d4f08e8 100644
--- a/src/net/java/sip/communicator/service/contactsource/ContactDetail.java
+++ b/src/net/java/sip/communicator/service/contactsource/ContactDetail.java
@@ -67,17 +67,14 @@ public class ContactDetail
* Creates a <tt>ContactDetail</tt> by specifying the corresponding contact
* address and a mapping of preferred <tt>ProtocolProviderServices</tt> for
* a specific <tt>OperationSet</tt>.
- * @param contactAddress the contact address corresponding to this detail
* @param preferredProviders a mapping of preferred
* <tt>ProtocolProviderService</tt>s for specific <tt>OperationSet</tt>
* classes
*/
- public ContactDetail(String contactAddress,
+ public void setPreferredProviders(
Map<Class<? extends OperationSet>, ProtocolProviderService>
preferredProviders)
{
- this(contactAddress);
-
this.preferredProviders = preferredProviders;
}
@@ -86,16 +83,13 @@ public class ContactDetail
* address and a list of all <tt>supportedOpSets</tt>, indicating what are
* the supporting actions with this contact detail (e.g. sending a message,
* making a call, etc.)
- * @param contactAddress the address of the contact
* @param supportedOpSets a list of all <tt>supportedOpSets</tt>, indicating
* what are the supporting actions with this contact detail (e.g. sending a
* message, making a call, etc.)
*/
- public ContactDetail( String contactAddress,
+ public void setSupportedOpSets(
List<Class<? extends OperationSet>> supportedOpSets)
{
- this(contactAddress);
-
this.supportedOpSets = supportedOpSets;
}
@@ -119,7 +113,10 @@ public class ContactDetail
public ProtocolProviderService getPreferredProtocolProvider(
Class<? extends OperationSet> opSetClass)
{
- return preferredProviders.get(opSetClass);
+ if (preferredProviders != null && preferredProviders.size() > 0)
+ return preferredProviders.get(opSetClass);
+
+ return null;
}
/**
@@ -132,30 +129,4 @@ public class ContactDetail
{
return supportedOpSets;
}
-
- /**
- * Sets the list of supported <tt>OperationSet</tt> classes. These are meant
- * to indicate what are the supported actions (sending an IM message,
- * making a call, etc.).
- * @param opSets the list of supported <tt>OperationSet</tt> classes
- */
- public void setSupportedOperationSets(
- List<Class<? extends OperationSet>> opSets)
- {
- this.supportedOpSets = opSets;
- }
-
- /**
- * Sets a mapping of preferred <tt>ProtocolProviderServices</tt> for
- * <tt>OperationSet</tt> classes.
- * @param preferredProviders a mapping of preferred
- * <tt>ProtocolProviderService</tt>s for specific <tt>OperationSet</tt>
- * classes
- */
- public void setPreferredProtocolProviders(
- Map<Class<? extends OperationSet>, ProtocolProviderService>
- preferredProviders)
- {
- this.preferredProviders = preferredProviders;
- }
}
diff --git a/src/net/java/sip/communicator/service/contactsource/ContactQuery.java b/src/net/java/sip/communicator/service/contactsource/ContactQuery.java
index fd33745..1da6d7a 100644
--- a/src/net/java/sip/communicator/service/contactsource/ContactQuery.java
+++ b/src/net/java/sip/communicator/service/contactsource/ContactQuery.java
@@ -6,6 +6,8 @@
*/
package net.java.sip.communicator.service.contactsource;
+import java.util.*;
+
/**
* The <tt>ContactQuery</tt> corresponds to a particular query made through the
* <tt>ContactSourceService</tt>. Each query once started could be
@@ -17,6 +19,20 @@ package net.java.sip.communicator.service.contactsource;
public interface ContactQuery
{
/**
+ * Returns the <tt>ContactSourceService</tt>, where this query was first
+ * initiated.
+ * @return the <tt>ContactSourceService</tt>, where this query was first
+ * initiated
+ */
+ public ContactSourceService getContactSource();
+
+ /**
+ * Returns the list of <tt>SourceContact</tt>s returned by this query.
+ * @return the list of <tt>SourceContact</tt>s returned by this query
+ */
+ public List<SourceContact> getQueryResults();
+
+ /**
* Cancels this query.
*/
public void cancel();
diff --git a/src/net/java/sip/communicator/service/contactsource/ContactSourceService.java b/src/net/java/sip/communicator/service/contactsource/ContactSourceService.java
index 78f1bbd..6d49947 100644
--- a/src/net/java/sip/communicator/service/contactsource/ContactSourceService.java
+++ b/src/net/java/sip/communicator/service/contactsource/ContactSourceService.java
@@ -6,8 +6,6 @@
*/
package net.java.sip.communicator.service.contactsource;
-import net.java.sip.communicator.service.protocol.*;
-
/**
* The <tt>ContactSourceService</tt> interface is meant to be implemented
* by modules supporting large lists of contacts and wanting to enable searching
@@ -17,6 +15,16 @@ import net.java.sip.communicator.service.protocol.*;
*/
public interface ContactSourceService
{
+ public static final String CALL_HISTORY = "CallHistory";
+
+ /**
+ * Returns the identifier of this contact source. Some of the common
+ * identifiers are defined here (For example the CALL_HISTORY identifier
+ * should be returned by all call history implementations of this interface)
+ * @return the identifier of this contact source
+ */
+ public String getIdentifier();
+
/**
* Returns a user-friendly string that identifies this contact source.
* @return the display name of this contact source
@@ -29,11 +37,4 @@ public interface ContactSourceService
* @return the created query
*/
public ContactQuery queryContactSource(String queryString);
-
- /**
- * Returns the telephony provider preferred for calling items from this
- * source.
- * @return the preferred telephony provider
- */
- public ProtocolProviderService getPreferredTelephonyProvider();
}
diff --git a/src/net/java/sip/communicator/service/contactsource/SourceContact.java b/src/net/java/sip/communicator/service/contactsource/SourceContact.java
index 24f33a1..99b47ca 100644
--- a/src/net/java/sip/communicator/service/contactsource/SourceContact.java
+++ b/src/net/java/sip/communicator/service/contactsource/SourceContact.java
@@ -51,6 +51,16 @@ public interface SourceContact
public List<ContactDetail> getContactDetails();
/**
+ * Returns a list of all <tt>ContactDetail</tt>s supporting the given
+ * <tt>OperationSet</tt> class.
+ * @param operationSet the <tt>OperationSet</tt> class we're looking for
+ * @return a list of all <tt>ContactDetail</tt>s supporting the given
+ * <tt>OperationSet</tt> class
+ */
+ public List<ContactDetail> getContactDetails(
+ Class<? extends OperationSet> operationSet);
+
+ /**
* Returns the preferred <tt>ContactDetail</tt> for a given
* <tt>OperationSet</tt> class.
* @param operationSet the <tt>OperationSet</tt> class, for which we would
diff --git a/src/net/java/sip/communicator/service/contactsource/contactsource.manifest.mf b/src/net/java/sip/communicator/service/contactsource/contactsource.manifest.mf
index acc02d3..cbb4aa2 100644
--- a/src/net/java/sip/communicator/service/contactsource/contactsource.manifest.mf
+++ b/src/net/java/sip/communicator/service/contactsource/contactsource.manifest.mf
@@ -3,5 +3,6 @@ Bundle-Description: ContactSource Service.
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
System-Bundle: yes
-Import-Package: org.osgi.framework
+Import-Package: org.osgi.framework,
+ net.java.sip.communicator.service.protocol
Export-Package: net.java.sip.communicator.service.contactsource
diff --git a/src/net/java/sip/communicator/service/history/HistoryReader.java b/src/net/java/sip/communicator/service/history/HistoryReader.java
index 235f3e3..94d2cab 100644
--- a/src/net/java/sip/communicator/service/history/HistoryReader.java
+++ b/src/net/java/sip/communicator/service/history/HistoryReader.java
@@ -28,7 +28,8 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
- public QueryResultSet<HistoryRecord> findByStartDate(Date startDate) throws RuntimeException;
+ public QueryResultSet<HistoryRecord> findByStartDate(Date startDate)
+ throws RuntimeException;
/**
* Searches the history for all records with timestamp before
@@ -40,7 +41,8 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
- public QueryResultSet<HistoryRecord> findByEndDate(Date endDate) throws RuntimeException;
+ public QueryResultSet<HistoryRecord> findByEndDate(Date endDate)
+ throws RuntimeException;
/**
* Searches the history for all records with timestamp between
@@ -53,7 +55,8 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
- public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate)
+ public QueryResultSet<HistoryRecord> findByPeriod( Date startDate,
+ Date endDate)
throws RuntimeException;
/**
@@ -66,7 +69,9 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
- public QueryResultSet<HistoryRecord> findByKeyword(String keyword, String field) throws RuntimeException;
+ public QueryResultSet<HistoryRecord> findByKeyword( String keyword,
+ String field)
+ throws RuntimeException;
/**
* Searches the history for all records containing the <tt>keyword</tt>.
@@ -79,7 +84,9 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
- public QueryResultSet<HistoryRecord> findByKeyword(String keyword, String field, boolean caseSensitive)
+ public QueryResultSet<HistoryRecord> findByKeyword( String keyword,
+ String field,
+ boolean caseSensitive)
throws RuntimeException;
/**
@@ -92,7 +99,9 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
- public QueryResultSet<HistoryRecord> findByKeywords(String[] keywords, String field) throws RuntimeException;
+ public QueryResultSet<HistoryRecord> findByKeywords(String[] keywords,
+ String field)
+ throws RuntimeException;
/**
* Searches the history for all records containing all <tt>keywords</tt>.
@@ -105,7 +114,9 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
- public QueryResultSet<HistoryRecord> findByKeywords(String[] keywords, String field, boolean caseSensitive)
+ public QueryResultSet<HistoryRecord> findByKeywords(String[] keywords,
+ String field,
+ boolean caseSensitive)
throws RuntimeException;
/**
@@ -121,8 +132,10 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
- public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate,
- String[] keywords, String field)
+ public QueryResultSet<HistoryRecord> findByPeriod( Date startDate,
+ Date endDate,
+ String[] keywords,
+ String field)
throws UnsupportedOperationException;
/**
@@ -139,8 +152,11 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
- public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate,
- String[] keywords, String field, boolean caseSensitive)
+ public QueryResultSet<HistoryRecord> findByPeriod( Date startDate,
+ Date endDate,
+ String[] keywords,
+ String field,
+ boolean caseSensitive)
throws UnsupportedOperationException;
/**
@@ -160,7 +176,9 @@ public interface HistoryReader {
* @return QueryResultSet the found records
* @throws RuntimeException
*/
- public QueryResultSet<HistoryRecord> findFirstRecordsAfter(Date date, int count) throws RuntimeException;
+ public QueryResultSet<HistoryRecord> findFirstRecordsAfter( Date date,
+ int count)
+ throws RuntimeException;
/**
* Returns the supplied number of recent messages before the given date
@@ -170,22 +188,26 @@ public interface HistoryReader {
* @return QueryResultSet the found records
* @throws RuntimeException
*/
- public QueryResultSet<HistoryRecord> findLastRecordsBefore(Date date, int count) throws RuntimeException;
+ public QueryResultSet<HistoryRecord> findLastRecordsBefore( Date date,
+ int count)
+ throws RuntimeException;
/**
* Adding progress listener for monitoring progress of search process
*
* @param listener HistorySearchProgressListener
*/
- public void addSearchProgressListener(HistorySearchProgressListener listener);
+ public void addSearchProgressListener(
+ HistorySearchProgressListener listener);
/**
* Removing progress listener
*
* @param listener HistorySearchProgressListener
*/
- public void removeSearchProgressListener(HistorySearchProgressListener listener);
-
+ public void removeSearchProgressListener(
+ HistorySearchProgressListener listener);
+
/**
* Total count of records that current history reader will read through
*
@@ -196,4 +218,9 @@ public interface HistoryReader {
*/
public int countRecords()
throws UnsupportedOperationException;
+
+ /**
+ * Cancels the current find. If there's no find going on, then does nothing.
+ */
+ public void cancelCurrentFind();
}
diff --git a/src/net/java/sip/communicator/util/swing/ExtendedTransferHandler.java b/src/net/java/sip/communicator/util/swing/ExtendedTransferHandler.java
index 0a693cc..8a16e1b 100644
--- a/src/net/java/sip/communicator/util/swing/ExtendedTransferHandler.java
+++ b/src/net/java/sip/communicator/util/swing/ExtendedTransferHandler.java
@@ -17,14 +17,13 @@ import java.io.*;
import javax.swing.*;
import javax.swing.text.*;
-import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.util.*;
/**
* A TransferHandler that we use to handle copying, pasting and DnD operations.
* The string handler is heavily inspired by Sun's
* <tt>DefaultTransferHandler</tt> with the main difference being that
- * we only accept pasting of plain text. We do this in order to avoid html
+ * we only accept pasting of plain text. We do this in order to avoid HTML
* support problems that appear when pasting formatted text into our editable
* area.
*
@@ -35,12 +34,6 @@ public class ExtendedTransferHandler
extends TransferHandler
{
/**
- * The data flavor used when transferring <tt>MetaContact</tt>s.
- */
- protected static final DataFlavor metaContactDataFlavor
- = new DataFlavor(MetaContact.class, "MetaContact");
-
- /**
* Returns the type of transfer actions supported by the source;
* any bitwise-OR combination of <tt>COPY</tt>, <tt>MOVE</tt>
* and <tt>LINK</tt>.
diff --git a/src/net/java/sip/communicator/util/swing/SIPCommTextButton.java b/src/net/java/sip/communicator/util/swing/SIPCommTextButton.java
index b2a7029..4ec8fb4 100644
--- a/src/net/java/sip/communicator/util/swing/SIPCommTextButton.java
+++ b/src/net/java/sip/communicator/util/swing/SIPCommTextButton.java
@@ -27,12 +27,14 @@ public class SIPCommTextButton extends JButton
private final float[] borderColor
= Color.DARK_GRAY.getRGBComponents(null);
+ private Image bgImage;
+
/**
* Creates a <tt>SIPCommTextButton</tt>.
*/
public SIPCommTextButton()
{
- this(null);
+ this("", null);
}
/**
@@ -41,8 +43,15 @@ public class SIPCommTextButton extends JButton
*/
public SIPCommTextButton(String text)
{
+ this(text, null);
+ }
+
+ public SIPCommTextButton(String text, Image bgImage)
+ {
super(text);
+ this.bgImage = bgImage;
+
MouseRolloverHandler mouseHandler = new MouseRolloverHandler();
this.addMouseListener(mouseHandler);
@@ -55,11 +64,15 @@ public class SIPCommTextButton extends JButton
* Explicitly remove all borders that may be set from the current look
* and feel.
*/
- this.setBorder(BorderFactory.createEmptyBorder(4, 10, 4, 10));
this.setContentAreaFilled(false);
this.setUI(new BasicButtonUI());
}
+ public void setBgImage(Image image)
+ {
+ this.bgImage = image;
+ }
+
/**
* Overrides the <code>paintComponent</code> method of <tt>JButton</tt> to
* paint the button background and icon, and all additional effects of this
@@ -101,19 +114,37 @@ public class SIPCommTextButton extends JButton
visibility /= 2;
- g.setColor(getBackground());
- g.fillRoundRect(1, 1,
- this.getWidth() - 2, this.getHeight() - 2,
- 20, 20);
-
if (visibility != 0.0f)
{
g.setColor(new Color(borderColor[0], borderColor[1],
borderColor[2], visibility));
- g.drawRoundRect(0, 0,
- this.getWidth() - 1, this.getHeight() - 1,
+
+ if (bgImage != null)
+ g.fillRoundRect((this.getWidth() - bgImage.getWidth(null))/2,
+ (this.getHeight() - bgImage.getHeight(null))/2,
+ bgImage.getWidth(null) - 1,
+ bgImage.getHeight(null) - 1,
+ 20, 20);
+ else
+ g.fillRoundRect(0, 0,
+ this.getWidth() - 1, this.getHeight() - 1,
+ 20, 20);
+ }
+
+ if (bgImage != null)
+ {
+ g.drawImage(bgImage,
+ (this.getWidth() - bgImage.getWidth(null))/2,
+ (this.getHeight() - bgImage.getHeight(null))/2, null);
+ }
+ else
+ {
+ g.setColor(getBackground());
+ g.fillRoundRect(1, 1,
+ this.getWidth() - 2, this.getHeight() - 2,
20, 20);
}
+
}
/**
diff --git a/src/net/java/sip/communicator/util/swing/SIPCommTextField.java b/src/net/java/sip/communicator/util/swing/SIPCommTextField.java
index b9d24bf..4af8d57 100644
--- a/src/net/java/sip/communicator/util/swing/SIPCommTextField.java
+++ b/src/net/java/sip/communicator/util/swing/SIPCommTextField.java
@@ -8,8 +8,12 @@ package net.java.sip.communicator.util.swing;
import java.awt.*;
import java.awt.event.*;
+import java.util.*;
import javax.swing.*;
+import javax.swing.event.*;
+
+import net.java.sip.communicator.util.swing.event.*;
/**
* The <tt>SIPCommTextField</tt> is a <tt>JTextField</tt> that offers the
@@ -21,9 +25,24 @@ public class SIPCommTextField
extends JTextField
implements MouseListener,
FocusListener,
- KeyListener
+ KeyListener,
+ DocumentListener
{
- private final String defaultText;
+ /**
+ * The default text.
+ */
+ private String defaultText;
+
+ /**
+ * A list of all listeners registered for text field change events.
+ */
+ private Collection<TextFieldChangeListener> changeListeners
+ = new LinkedList<TextFieldChangeListener>();
+
+ /**
+ * Indicates if the default text is currently visible.
+ */
+ private boolean isDefaultTextVisible;
/**
* Creates an instance of <tt>SIPCommTextField</tt> by specifying the text
@@ -34,7 +53,11 @@ public class SIPCommTextField
{
super(text);
- this.defaultText = text;
+ if (text != null && text.length() > 0)
+ {
+ this.defaultText = text;
+ isDefaultTextVisible = true;
+ }
this.setFont(getFont().deriveFont(10f));
this.setForeground(Color.GRAY);
@@ -43,6 +66,7 @@ public class SIPCommTextField
this.addFocusListener(this);
this.addKeyListener(this);
+ this.getDocument().addDocumentListener(this);
}
/**
@@ -154,4 +178,71 @@ public class SIPCommTextField
}
public void keyReleased(KeyEvent e){}
+
+ /**
+ * Adds the given <tt>TextFieldChangeListener</tt> to the list of listeners
+ * notified on changes of the text contained in this field.
+ * @param l the <tt>TextFieldChangeListener</tt> to add
+ */
+ public void addTextChangeListener(TextFieldChangeListener l)
+ {
+ synchronized (changeListeners)
+ {
+ changeListeners.add(l);
+ }
+ }
+
+ /**
+ * Removes the given <tt>TextFieldChangeListener</tt> from the list of
+ * listeners notified on changes of the text contained in this field.
+ * @param l the <tt>TextFieldChangeListener</tt> to add
+ */
+ public void removeTextChangeListener(TextFieldChangeListener l)
+ {
+ synchronized (changeListeners)
+ {
+ changeListeners.remove(l);
+ }
+ }
+
+ public void changedUpdate(DocumentEvent e) {}
+
+ /**
+ * Handles the change when a char has been inserted in the field.
+ * @param e the <tt>DocumentEvent</tt> that notified us
+ */
+ public void insertUpdate(DocumentEvent e)
+ {
+ if(!super.getText().equals(defaultText))
+ fireTextFieldChangeListener(0);
+ else
+ isDefaultTextVisible = true;
+ }
+
+ /**
+ * Handles the change when a char has been removed from the field.
+ * @param e the <tt>DocumentEvent</tt> that notified us
+ */
+ public void removeUpdate(DocumentEvent e)
+ {
+ if (!isDefaultTextVisible)
+ fireTextFieldChangeListener(1);
+ else
+ isDefaultTextVisible = false;
+ }
+
+ /**
+ * Notifies all registered <tt>TextFieldChangeListener</tt>s that a change
+ * has occurred in the text contained in this field.
+ * @param eventType the type of the event to transfer
+ */
+ private void fireTextFieldChangeListener(int eventType)
+ {
+ for (TextFieldChangeListener l : changeListeners)
+ switch (eventType)
+ {
+ case 0: l.textInserted(); break;
+ case 1: l.textRemoved(); break;
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/util/swing/event/TextFieldChangeListener.java b/src/net/java/sip/communicator/util/swing/event/TextFieldChangeListener.java
new file mode 100644
index 0000000..7a3c76b
--- /dev/null
+++ b/src/net/java/sip/communicator/util/swing/event/TextFieldChangeListener.java
@@ -0,0 +1,27 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.util.swing.event;
+
+/**
+ * The <tt>TextFieldChangeListener</tt> listens for any changes in the text
+ * contained in a <tt>SIPCommTextField</tt>. It is notified every time a char
+ * is inserted or removed from the field.
+ *
+ * @author Yana Stamcheva
+ */
+public interface TextFieldChangeListener
+{
+ /**
+ * Indicates that a text has been removed from the text field.
+ */
+ public void textRemoved();
+
+ /**
+ * Indicates that a text has been inserted to the text field.
+ */
+ public void textInserted();
+}