diff --git a/src/net/java/sip/communicator/impl/callhistory/CallHistoryContactSource.java b/src/net/java/sip/communicator/impl/callhistory/CallHistoryContactSource.java
index 7648be65b..ac0304239 100644
--- a/src/net/java/sip/communicator/impl/callhistory/CallHistoryContactSource.java
+++ b/src/net/java/sip/communicator/impl/callhistory/CallHistoryContactSource.java
@@ -76,6 +76,12 @@ private class CallHistoryContactQuery
*/
private CallHistoryQuery callHistoryQuery;
+ /**
+ * Indicates the status of this query. When created this query is in
+ * progress.
+ */
+ private int status = QUERY_IN_PROGRESS;
+
/**
* Creates an instance of CallHistoryContactQuery by specifying
* the list of call records results.
@@ -86,13 +92,16 @@ public CallHistoryContactQuery(Collection callRecords)
{
Iterator recordsIter = callRecords.iterator();
- while (recordsIter.hasNext())
+ while (recordsIter.hasNext() && status != QUERY_CANCELED)
{
sourceContacts.add(
new CallHistorySourceContact(
CallHistoryContactSource.this,
recordsIter.next()));
}
+
+ if (status != QUERY_CANCELED)
+ status = QUERY_COMPLETED;
}
/**
@@ -118,7 +127,8 @@ public void callRecordReceived(CallRecordEvent event)
public void queryStatusChanged(
CallHistoryQueryStatusEvent event)
{
- fireQueryStatusEvent(event.getEventType());
+ status = event.getEventType();
+ fireQueryStatusEvent(status);
}
});
}
@@ -141,10 +151,22 @@ public void addContactQueryListener(ContactQueryListener l)
*/
public void cancel()
{
+ status = QUERY_CANCELED;
+
if (callHistoryQuery != null)
callHistoryQuery.cancel();
}
+ /**
+ * Returns the status of this query. One of the static constants defined
+ * in this class.
+ * @return the status of this query
+ */
+ public int getStatus()
+ {
+ return status;
+ }
+
/**
* Removes the given ContactQueryListener from the list of
* query listeners.
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
index 6ab29fc56..d23c4a9fc 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java
@@ -95,8 +95,6 @@ public void actionPerformed(ActionEvent e)
{
if (isHistoryVisible && !isMissedCallView)
{
- TreeContactList.searchFilter
- .setSearchSourceType(SearchFilter.DEFAULT_SOURCE);
GuiActivator.getContactList()
.setDefaultFilter(TreeContactList.presenceFilter);
GuiActivator.getContactList().applyDefaultFilter();
@@ -105,8 +103,6 @@ public void actionPerformed(ActionEvent e)
}
else
{
- TreeContactList.searchFilter
- .setSearchSourceType(SearchFilter.HISTORY_SOURCE);
GuiActivator.getContactList()
.setDefaultFilter(TreeContactList.historyFilter);
GuiActivator.getContactList().applyDefaultFilter();
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
index 565de912f..e2a3ede4e 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/CallHistoryFilter.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/CallHistoryFilter.java
@@ -11,7 +11,6 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.util.*;
/**
* The CallHistoryFilter is a filter over the history contact sources.
@@ -21,23 +20,13 @@
public class CallHistoryFilter
implements ContactListFilter
{
- /**
- * This class logger.
- */
- private final Logger logger = Logger.getLogger(CallHistoryFilter.class);
-
- /**
- * The current ContactQuery .
- */
- private ContactQuery currentQuery;
-
/**
* Applies this filter and stores the result in the given treeModel .
+ * @param filterQuery the FilterQuery that tracks the results of
+ * this filtering
*/
- public void applyFilter()
+ public void applyFilter(FilterQuery filterQuery)
{
- logger.debug("Call history filter applied.");
-
Collection contactSources
= TreeContactList.getContactSources();
@@ -51,13 +40,16 @@ public void applyFilter()
continue;
// We're in a case of call history contact source.
- currentQuery = sourceService.queryContactSource("");
+ ContactQuery query = sourceService.queryContactSource("");
+ filterQuery.addContactQuery(query);
// Add first available results.
- this.addMatching( currentQuery.getQueryResults(),
+ this.addMatching( query.getQueryResults(),
contactSource);
- currentQuery.addContactQueryListener(GuiActivator.getContactList());
+ // We know that this query should be finished here and we do not
+ // expect any further results from it.
+ filterQuery.removeQuery(query);
}
}
@@ -111,13 +103,4 @@ private void addMatching( List sourceContacts,
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/ContactListFilter.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/ContactListFilter.java
index e3c2e749c..d0ef466f6 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
@@ -34,11 +34,8 @@ public interface ContactListFilter
/**
* Applies this filter to any interested sources
+ * @param filterQuery the FilterQuery that tracks the results of
+ * this filtering
*/
- public void applyFilter();
-
- /**
- * Stops this filter current queries.
- */
- public void stopFilter();
+ public void applyFilter(FilterQuery filterQuery);
}
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 5b27c3e03..996a7b840 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
@@ -97,15 +97,7 @@ public void initList(MetaContactListService contactListService)
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();
+ contactList.applyFilter(TreeContactList.presenceFilter);
TransparentPanel transparentPanel
= new TransparentPanel(new BorderLayout());
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 1a1a4bc6f..b5a22e22e 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,6 +6,8 @@
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
+import java.lang.reflect.*;
+
import javax.swing.*;
import javax.swing.tree.*;
@@ -60,22 +62,38 @@ public ContactNode findFirstContactNode()
*/
public void clear()
{
- SwingUtilities.invokeLater(new Runnable()
- {
- public void run()
+ if (!SwingUtilities.isEventDispatchThread())
+ try
{
- int childCount = rootGroupNode.getChildCount();
- int[] removedIndexs = new int[childCount];
- Object[] removedNodes = new Object[childCount];
- for (int i = 0; i < childCount; i ++)
+ SwingUtilities.invokeAndWait(new Runnable()
{
- removedIndexs[i] = i;
- removedNodes[i] = rootGroupNode.getChildAt(i);
- }
- rootGroupNode.clear();
- nodesWereRemoved(rootGroupNode, removedIndexs, removedNodes);
+ public void run()
+ {
+ clear();
+ }
+ });
}
- });
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+
+ // The following code is always invoked in the swing thread.
+ int childCount = rootGroupNode.getChildCount();
+ int[] removedIndexs = new int[childCount];
+ Object[] removedNodes = new Object[childCount];
+ for (int i = 0; i < childCount; i ++)
+ {
+ removedIndexs[i] = i;
+ removedNodes[i] = rootGroupNode.getChildAt(i);
+ }
+
+ rootGroupNode.clear();
+ nodesWereRemoved(rootGroupNode, removedIndexs, removedNodes);
}
/**
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/FilterQuery.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/FilterQuery.java
index f5b414be5..3c8894026 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/FilterQuery.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/FilterQuery.java
@@ -8,6 +8,8 @@
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.*;
/**
@@ -16,7 +18,8 @@
* @author Yana Stamcheva
*/
public class FilterQuery
- implements ContactQueryListener
+ implements ContactQueryListener,
+ MetaContactQueryListener
{
/**
* A listener, which is notified when this query finishes.
@@ -37,17 +40,23 @@ public class FilterQuery
/**
* The list of filter queries.
*/
- private Collection filterQueries
- = new LinkedList();
+ private Collection filterQueries = new Vector();
+
+ private int runningQueries = 0;
/**
* Adds the given contactQuery to the list of filterQueries.
* @param contactQuery the ContactQuery to add
*/
- public void addContactQuery(ContactQuery contactQuery)
+ public void addContactQuery(Object contactQuery)
{
filterQueries.add(contactQuery);
- contactQuery.addContactQueryListener(this);
+ runningQueries++;
+
+ if (contactQuery instanceof ContactQuery)
+ ((ContactQuery) contactQuery).addContactQueryListener(this);
+ else if (contactQuery instanceof MetaContactQuery)
+ ((MetaContactQuery) contactQuery).addContactQueryListener(this);
}
/**
@@ -84,8 +93,31 @@ public boolean isCanceled()
public void cancel()
{
isCanceled = true;
- filterQueries.clear();
- fireFilterQueryEvent();
+
+ Iterator queriesIter = filterQueries.iterator();
+ while (queriesIter.hasNext())
+ {
+ Object query = queriesIter.next();
+ if (query instanceof ContactQuery)
+ {
+ ContactQuery contactQuery = ((ContactQuery) query);
+ contactQuery.cancel();
+
+ contactQuery.removeContactQueryListener(
+ GuiActivator.getContactList());
+ if (!isSucceeded && contactQuery.getQueryResults().size() > 0)
+ isSucceeded = true;
+ }
+ else if (query instanceof MetaContactQuery)
+ {
+ MetaContactQuery metaContactQuery = ((MetaContactQuery) query);
+ metaContactQuery.cancel();
+ metaContactQuery.removeContactQueryListener(
+ GuiActivator.getContactList());
+ if (!isSucceeded && metaContactQuery.getResultCount() > 0)
+ isSucceeded = true;
+ }
+ }
}
/**
@@ -121,22 +153,67 @@ public void queryStatusChanged(ContactQueryStatusEvent event)
ContactQuery query = event.getQuerySource();
// Check if this query is in our filter queries list.
- if (!filterQueries.contains(query))
+ if (!filterQueries.contains(query)
+ || event.getEventType() == ContactQuery.QUERY_IN_PROGRESS)
return;
+ removeQuery(query);
+ }
+
+ /**
+ * Removes the given query from this filter query, updates the related data
+ * and notifies interested parties if this was the last query to process.
+ * @param query the ContactQuery to remove.
+ */
+ public void removeQuery(ContactQuery query)
+ {
// First set the isSucceeded property.
if (!isSucceeded() && !query.getQueryResults().isEmpty())
setSucceeded(true);
// Then remove the wait result from the filterQuery.
- filterQueries.remove(query);
+ runningQueries--;
query.removeContactQueryListener(this);
// If no queries have rest we notify interested listeners that query
// has finished.
- if (filterQueries.isEmpty())
+ if (runningQueries == 0)
+ fireFilterQueryEvent();
+ }
+
+ /**
+ * Indicates that a query has changed its status.
+ * @param event the ContactQueryStatusEvent that notified us
+ */
+ public void metaContactQueryStatusChanged(MetaContactQueryStatusEvent event)
+ {
+ MetaContactQuery query = event.getQuerySource();
+
+ // Check if this query is in our filter queries list.
+ if (!filterQueries.contains(query))
+ return;
+
+ // First set the isSucceeded property.
+ if (!isSucceeded() && query.getResultCount() > 0)
+ setSucceeded(true);
+
+ // We don't remove the query from our list, because even if the query
+ // has finished its GUI part is scheduled in the Swing thread and we
+ // don't know anything about these events, so if someone calls cancel()
+ // we need to explicitly cancel all contained queries even they are
+ // finished.
+ runningQueries--;
+ query.removeContactQueryListener(this);
+
+ // If no queries have rest we notify interested listeners that query
+ // has finished.
+ if (runningQueries == 0)
fireFilterQueryEvent();
}
public void contactReceived(ContactReceivedEvent event) {}
+
+ public void metaContactReceived(MetaContactQueryEvent event) {}
+
+ public void metaGroupReceived(MetaGroupQueryEvent event) {}
}
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 be7030f6d..01214c222 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
@@ -72,7 +72,10 @@ public ContactNode addContact(UIContact uiContact)
add(contactNode);
- fireNodeInserted(getIndex(contactNode));
+ int contactIndex = getIndex(contactNode);
+
+ if (contactIndex > -1)
+ fireNodeInserted(contactIndex);
return contactNode;
}
@@ -94,7 +97,10 @@ public ContactNode sortedAddContact(UIContact uiContact)
// TODO: Optimize!
Collections.sort(children, nodeComparator);
- fireNodeInserted(getIndex(contactNode));
+ int contactIndex = getIndex(contactNode);
+
+ if (contactIndex > -1)
+ fireNodeInserted(contactIndex);
return contactNode;
}
@@ -136,7 +142,10 @@ public GroupNode addContactGroup(UIGroup uiGroup)
add(groupNode);
- fireNodeInserted(getIndex(groupNode));
+ int groupIndex = getIndex(groupNode);
+
+ if (groupIndex > -1)
+ fireNodeInserted(groupIndex);
return groupNode;
}
@@ -182,7 +191,10 @@ public GroupNode sortedAddContactGroup(UIGroup uiGroup)
// TODO: Optimize!
Collections.sort(children, nodeComparator);
- fireNodeInserted(getIndex(groupNode));
+ int contactIndex = getIndex(groupNode);
+
+ if (contactIndex > -1)
+ fireNodeInserted(contactIndex);
return groupNode;
}
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 5fbf9e56d..76768fc48 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
@@ -13,7 +13,6 @@
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 PresenceFilter is used to filter offline contacts from the
@@ -24,42 +23,49 @@
public class PresenceFilter
implements ContactListFilter
{
- /**
- * This class logger.
- */
- private final Logger logger = Logger.getLogger(PresenceFilter.class);
-
/**
* Indicates if this presence filter shows or hides the offline contacts.
*/
private boolean isShowOffline;
/**
- * Indicates if there's a presence filtering going on.
+ * The initial result count below which we insert all filter results
+ * directly to the contact list without firing events.
*/
- private boolean isFiltering = false;
+ private final int INITIAL_CONTACT_COUNT = 30;
/**
* Creates an instance of PresenceFilter .
*/
public PresenceFilter()
{
- this.setShowOffline(ConfigurationManager.isShowOffline());
+ isShowOffline = ConfigurationManager.isShowOffline();
}
/**
* Applies this filter. This filter is applied over the
* MetaContactListService .
+ * @param filterQuery the query which keeps track of the filtering results
*/
- public void applyFilter()
+ public void applyFilter(FilterQuery filterQuery)
{
- logger.debug("Presence filter applied.");
+ // Create the query that will track filtering.
+ MetaContactQuery query = new MetaContactQuery();
- isFiltering = true;
+ // Add this query to the filterQuery.
+ filterQuery.addContactQuery(query);
- addMatching(GuiActivator.getContactListService().getRoot());
+ query.addContactQueryListener(GuiActivator.getContactList());
- isFiltering = false;
+ int resultCount = 0;
+ addMatching(GuiActivator.getContactListService().getRoot(),
+ query,
+ resultCount);
+
+ if (!query.isCanceled())
+ query.fireQueryEvent(MetaContactQueryStatusEvent.QUERY_COMPLETED);
+ else
+ query.fireQueryEvent(MetaContactQueryStatusEvent.QUERY_CANCELED);
}
/**
@@ -99,6 +105,8 @@ public boolean isMatching(UIGroup uiGroup)
public void setShowOffline(boolean isShowOffline)
{
this.isShowOffline = isShowOffline;
+
+ ConfigurationManager.setShowOffline(isShowOffline);
}
/**
@@ -163,40 +171,65 @@ private boolean isContactOnline(MetaContact contact)
* matching the current filter and not contained in the contact list.
* @param metaGroup the MetaContactGroup , which matching contacts
* to add
+ * @param query the MetaContactQuery that notifies interested
+ * listeners of the results of this matching
+ * @param resultCount the initial result count we would insert directly to
+ * the contact list without firing events
*/
- private void addMatching(MetaContactGroup metaGroup)
+ private void addMatching( MetaContactGroup metaGroup,
+ MetaContactQuery query,
+ int resultCount)
{
Iterator childContacts = metaGroup.getChildContacts();
- while(childContacts.hasNext() && isFiltering)
+ while(childContacts.hasNext() && !query.isCanceled())
{
MetaContact metaContact = childContacts.next();
if(isMatching(metaContact))
{
- MetaContactListSource.fireQueryEvent(metaContact);
+ resultCount++;
+ if (resultCount <= INITIAL_CONTACT_COUNT)
+ {
+ UIGroup uiGroup = null;
+ if (!MetaContactListSource.isRootGroup(metaGroup))
+ {
+ uiGroup = MetaContactListSource
+ .getUIGroup(metaGroup);
+
+ if (uiGroup == null)
+ uiGroup = MetaContactListSource
+ .createUIGroup(metaGroup);
+ }
+
+ GuiActivator.getContactList().addContact(
+ MetaContactListSource.createUIContact(metaContact),
+ uiGroup,
+ true);
+
+ query.setInitialResultCount(resultCount);
+ }
+ else
+ query.fireQueryEvent(metaContact);
}
}
+ // If in the meantime the filtering has been stopped we return here.
+ if (query.isCanceled())
+ return;
+
Iterator subgroups = metaGroup.getSubgroups();
- while(subgroups.hasNext() && isFiltering)
+ while(subgroups.hasNext() && !query.isCanceled())
{
MetaContactGroup subgroup = subgroups.next();
if (subgroup.countChildContacts() == 0
&& subgroup.countSubgroups() == 0
&& isMatching(subgroup))
- MetaContactListSource.fireQueryEvent(subgroup);
+ GuiActivator.getContactList().addGroup(
+ MetaContactListSource.createUIGroup(subgroup), true);
else
- addMatching(subgroup);
+ addMatching(subgroup, query, resultCount);
}
}
-
- /**
- * 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 ba959cda0..c2a12fea7 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
@@ -22,8 +22,9 @@ public class SearchField
implements TextFieldChangeListener,
FilterQueryListener
{
- private final Logger logger = Logger.getLogger(SearchField.class);
-
+ /**
+ * The main application window.
+ */
private final MainFrame mainFrame;
/**
@@ -32,8 +33,6 @@ public class SearchField
*/
private boolean lastHasMatching = true;
- private SearchThread searchThread = null;
-
/**
* Creates the SearchField .
* @param frame the main application window
@@ -81,7 +80,7 @@ public void textInserted()
if (filterString == null || filterString.length() <= 0)
return;
- scheduleUpdate();
+ updateContactListView();
}
/**
@@ -89,7 +88,7 @@ public void textInserted()
*/
public void textRemoved()
{
- scheduleUpdate();
+ updateContactListView();
}
/**
@@ -101,92 +100,39 @@ public void changedUpdate(DocumentEvent e) {}
/**
* Schedules an update if necessary.
*/
- private void scheduleUpdate()
+ private void updateContactListView()
{
- GuiActivator.getContactList().stopFiltering();
+ String filterString = getText();
- if (searchThread == null)
+ FilterQuery filterQuery = null;
+
+ if (filterString != null && filterString.length() > 0)
{
- searchThread = new SearchThread();
- searchThread.start();
+ TreeContactList.searchFilter
+ .setFilterString(filterString);
+
+ filterQuery = GuiActivator.getContactList()
+ .applyFilter(TreeContactList.searchFilter);
}
else
- synchronized (searchThread)
- {
- searchThread.notify();
- }
- }
-
- /**
- * The SearchThread is meant to launch the search in a separate
- * thread.
- */
- private class SearchThread extends Thread
- {
- public void run()
{
- while (true)
- {
- String filterString = getText();
-
- if (filterString != null && filterString.length() > 0)
- {
- TreeContactList.searchFilter
- .setFilterString(filterString);
- }
-
- updateContactListView(filterString);
-
- synchronized (this)
- {
- try
- {
- if (filterString == getText() //both are null or equal
- || (filterString != null
- && filterString.equals(getText())))
- {
- //filter still has the same value as the one
- //we did a search for, so we can wait for a
- //while
- this.wait();
-
- filterString = getText();
- }
- }
- catch (InterruptedException e)
- {
- logger.debug("Search thread was interrupted.", e);
- }
- }
- }
+ filterQuery = GuiActivator.getContactList().applyDefaultFilter();
}
- }
-
- /**
- * Updates the current contact list view to match the given
- * filterString . If the filterString is null or
- * empty we reset the presence filter.
- * @param filterString the current filter string entered in
- * this search field
- */
- public void updateContactListView(String filterString)
- {
- TreeContactList contactList = GuiActivator.getContactList();
- if (filterString != null && filterString.length() > 0)
+ if (filterQuery != null && !filterQuery.isCanceled())
{
- FilterQuery filterQuery
- = contactList.applyFilter(TreeContactList.searchFilter);
-
- if (filterQuery != null)
+ // If we already have a result here we update the interface.
+ if (filterQuery.isSucceeded())
+ enableUnknownContactView(false);
+ else
+ // Otherwise we will listen for events for changes in status
+ // of this query.
filterQuery.setQueryListener(this);
}
else
- {
- contactList.applyDefaultFilter();
-
- enableUnknownContactView(false);
- }
+ // If the query is null or is canceled, we would simply check the
+ // contact list content.
+ enableUnknownContactView(GuiActivator.getContactList().isEmpty());
}
/**
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 8fe363542..49788463d 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
@@ -12,7 +12,6 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.util.*;
/**
* The SearchFilter is a ContactListFilter that filters the
@@ -21,13 +20,8 @@
* @author Yana Stamcheva
*/
public class SearchFilter
- implements ContactListSourceFilter
+ implements ContactListFilter
{
- /**
- * This class logger.
- */
- private final Logger logger = Logger.getLogger(SearchFilter.class);
-
/**
* The default contact source search type.
*/
@@ -58,17 +52,11 @@ public class SearchFilter
*/
private Collection contactSources;
- /**
- * The list of currently running queries.
- */
- private Collection currentQueries
- = new LinkedList();
-
/**
* The type of the search source. One of the above defined DEFAUT_SOURCE or
* HISTORY_SOURCE.
*/
- private int searchSourceType;
+ private int searchSourceType = DEFAULT_SOURCE;
/**
* Creates an instance of SearchFilter .
@@ -80,14 +68,40 @@ public SearchFilter()
/**
* Applies this filter to the default contact source.
+ * @param filterQuery the query that tracks this filter.
*/
- public void applyFilter()
+ public void applyFilter(FilterQuery filterQuery)
{
- logger.debug("Search filter applied on default source");
+ // If the filter has a default contact source, we apply it first.
if (searchSourceType == DEFAULT_SOURCE)
{
+ MetaContactQuery defaultQuery
+ = mclSource.queryMetaContactSource(filterPattern);
+
+ defaultQuery.addContactQueryListener(GuiActivator.getContactList());
+
// First add the MetaContactListSource
- mclSource.filter(filterPattern);
+ filterQuery.addContactQuery(defaultQuery);
+ }
+
+ // If we have stopped filtering in the mean time we return here.
+ if (filterQuery.isCanceled())
+ return;
+
+ Iterator filterSources
+ = getContactSources().iterator();
+
+ // Then we apply the filter on all its contact sources.
+ while (filterSources.hasNext())
+ {
+ final ExternalContactSource filterSource = filterSources.next();
+
+ // If we have stopped filtering in the mean time we return here.
+ if (filterQuery.isCanceled())
+ return;
+
+ filterQuery.addContactQuery(
+ applyFilter(filterSource));
}
}
@@ -100,9 +114,6 @@ public void applyFilter()
*/
public ContactQuery applyFilter(ExternalContactSource contactSource)
{
- logger.debug("Search filter applied on source: "
- + contactSource.getContactSourceService());
-
ContactSourceService sourceService
= contactSource.getContactSourceService();
@@ -117,7 +128,6 @@ public ContactQuery applyFilter(ExternalContactSource contactSource)
// Add first available results.
this.addMatching(contactQuery.getQueryResults());
- currentQueries.add(contactQuery);
contactQuery.addContactQueryListener(GuiActivator.getContactList());
return contactQuery;
@@ -174,26 +184,6 @@ public void setFilterString(String filter)
| Pattern.UNICODE_CASE);
}
- /**
- * Stops all currently running queries.
- */
- public void stopFilter()
- {
- mclSource.stopFiltering();
- Iterator queriesIter = currentQueries.iterator();
- while (queriesIter.hasNext())
- queriesIter.next().cancel();
- }
-
- /**
- * Removes the given query from the list of currently processed queries.
- * @param contactQuery the ContactQuery to remove
- */
- public void removeCurrentQuery(ContactQuery contactQuery)
- {
- currentQueries.remove(contactQuery);
- }
-
/**
* Checks if the given contact is matching the current filter.
* A SourceContact would be matching the filter if its display
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 07c69a9af..7d99c735f 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
@@ -114,10 +114,9 @@ public class TreeContactList
private static final Collection contactSources
= new LinkedList();
- /**
- * The filter query used to track advanced source filtering.
- */
- FilterQuery filterQuery;
+ private FilterQuery currentFilterQuery;
+
+ private FilterThread filterThread;
/**
* Creates the TreeContactList .
@@ -147,8 +146,6 @@ public TreeContactList()
this.initKeyActions();
this.initContactSources();
-
- MetaContactListSource.setMetaContactQueryListener(this);
}
/**
@@ -532,7 +529,9 @@ public void contactReceived(ContactReceivedEvent event)
if((contactSource instanceof ExtendedContactSourceService)
|| currentFilter.isMatching(uiContact))
{
- addContact(uiContact, sourceUI.getUIGroup(), false);
+ addContact(event.getQuerySource(),
+ uiContact,
+ sourceUI.getUIGroup(), false);
}
else
uiContact = null;
@@ -541,15 +540,13 @@ public void contactReceived(ContactReceivedEvent event)
/**
* Indicates that a MetaContact has been received for a search in
* the MetaContactListService .
- * @param metaContact the received MetaContact
+ * @param event the received MetaContactQueryEvent
*/
- public void metaContactReceived(MetaContact metaContact)
+ public void metaContactReceived(MetaContactQueryEvent event)
{
+ MetaContact metaContact = event.getMetaContact();
MetaContactGroup parentGroup = metaContact.getParentMetaContactGroup();
- if (filterQuery != null)
- filterQuery.setSucceeded(true);
-
UIGroup uiGroup = null;
if (!MetaContactListSource.isRootGroup(parentGroup))
{
@@ -561,21 +558,22 @@ public void metaContactReceived(MetaContact metaContact)
.createUIGroup(parentGroup);
}
- GuiActivator.getContactList().addContact(
- MetaContactListSource.createUIContact(metaContact),
- uiGroup,
- true);
+ addContact( event.getQuerySource(),
+ MetaContactListSource.createUIContact(metaContact),
+ uiGroup,
+ true);
}
/**
* Indicates that a MetaGroup has been received from a search in
* the MetaContactListService .
- * @param metaGroup the MetaGroup that has been received
+ * @param event the MetaContactGroupQueryEvent that has been
+ * received
*/
- public void metaGroupReceived(MetaContactGroup metaGroup)
+ public void metaGroupReceived(MetaGroupQueryEvent event)
{
GuiActivator.getContactList().addGroup(
- MetaContactListSource.createUIGroup(metaGroup), true);
+ MetaContactListSource.createUIGroup(event.getMetaGroup()), true);
}
/**
@@ -590,7 +588,21 @@ public void queryStatusChanged(ContactQueryStatusEvent event)
{
//TODO: Show the error to the user??
}
- searchFilter.removeCurrentQuery(event.getQuerySource());
+ event.getQuerySource().removeContactQueryListener(this);
+ }
+
+ /**
+ * Indicates that the status of a query has changed.
+ * @param event the ContactQueryStatusEvent that notified us
+ */
+ public void metaContactQueryStatusChanged(MetaContactQueryStatusEvent event)
+ {
+ int eventType = event.getEventType();
+
+ if (eventType == ContactQueryStatusEvent.QUERY_ERROR)
+ {
+ //TODO: Show the error to the user??
+ }
event.getQuerySource().removeContactQueryListener(this);
}
@@ -718,7 +730,62 @@ public void run()
if ((!currentFilter.equals(presenceFilter)
|| !groupNode.isCollapsed()))
- this.expandGroup(groupNode);
+ this.expandGroup(groupNode);
+ }
+
+ /**
+ * Adds the given contact to this list.
+ * @param query the MetaContactQuery that adds the given contact
+ * @param contact the UIContact to add
+ * @param group the UIGroup to add to
+ * @param isSorted indicates if the contact should be sorted regarding to
+ * the GroupNode policy
+ */
+ private void addContact(final MetaContactQuery query,
+ final UIContact contact,
+ final UIGroup group,
+ final boolean isSorted)
+ {
+ if (!SwingUtilities.isEventDispatchThread())
+ {
+ LowPriorityEventQueue.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ if (query != null && !query.isCanceled())
+ addContact(contact, group, isSorted);
+ }
+ });
+ return;
+ }
+ }
+
+ /**
+ * Adds the given contact to this list.
+ * @param query the ContactQuery that adds the given contact
+ * @param contact the UIContact to add
+ * @param group the UIGroup to add to
+ * @param isSorted indicates if the contact should be sorted regarding to
+ * the GroupNode policy
+ */
+ private void addContact( final ContactQuery query,
+ final UIContact contact,
+ final UIGroup group,
+ final boolean isSorted)
+ {
+ if (!SwingUtilities.isEventDispatchThread())
+ {
+ LowPriorityEventQueue.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ if (query != null
+ && query.getStatus() != ContactQuery.QUERY_CANCELED)
+ addContact(contact, group, isSorted);
+ }
+ });
+ return;
+ }
}
/**
@@ -870,23 +937,14 @@ public void setGroupClickConsumed(boolean isGroupClickConsumed)
this.isGroupClickConsumed = isGroupClickConsumed;
}
- /**
- * Stops the current filtering if there's one active.
- */
- public void stopFiltering()
- {
- if (currentFilter != null)
- currentFilter.stopFilter();
-
- if (filterQuery != null)
- filterQuery.cancel();
- }
-
/**
* Applies the default filter.
+ * @return the filter query that keeps track of the filtering results
*/
- public void applyDefaultFilter()
+ public FilterQuery applyDefaultFilter()
{
+ FilterQuery filterQuery = null;
+
final MainFrame mainFrame = GuiActivator.getUIService().getMainFrame();
String currentSearchText = mainFrame.getCurrentSearchText();
@@ -908,94 +966,88 @@ public void run()
}
else
{
- treeModel.clear();
- applyFilter(defaultFilter, null);
+ filterQuery = applyFilter(defaultFilter);
}
+
+ return filterQuery;
}
/**
* Applies the given filter .
* @param filter the ContactListFilter to apply.
+ * @return the filter query
*/
- public void applyFilter(ContactListFilter filter)
+ public FilterQuery applyFilter(ContactListFilter filter)
{
- treeModel.clear();
- applyFilter(filter, null);
- }
+ if (currentFilterQuery != null && !currentFilterQuery.isCanceled())
+ currentFilterQuery.cancel();
- /**
- * Applies the given ContactListSourceFilter .
- * @param filter the ContactListSourceFilter to apply
- * @return the FilterQuery through which the filter could be
- * tracked
- */
- public FilterQuery applyFilter(final ContactListSourceFilter filter)
- {
- filterQuery = new FilterQuery();
-
- treeModel.clear();
+ currentFilterQuery = new FilterQuery();
- // If the filter has a default contact source, we apply it first.
- if (filter.hasDefaultSource())
- applyFilter(filter, null);
-
- Iterator filterSources
- = filter.getContactSources().iterator();
-
- // Then we apply the filter on all its contact sources.
- while (filterSources.hasNext())
+ if (filterThread == null)
{
- final ExternalContactSource filterSource = filterSources.next();
-
- if (filterQuery.isCanceled())
- return filterQuery;
+ filterThread = new FilterThread();
+ filterThread.setFilter(filter);
+ filterThread.start();
+ }
+ else
+ {
+ filterThread.setFilter(filter);
- applyFilter(filter, filterSource);
+ synchronized (filterThread)
+ {
+ filterThread.notify();
+ }
}
- return filterQuery;
+
+ return currentFilterQuery;
}
/**
- * Applies the given filter and changes the content of the
- * contact list according to it.
- * @param filter the new filter to set
- * @param contactSource the ExternalContactSource to apply the
- * filter to
+ * The SearchThread is meant to launch the search in a separate
+ * thread.
*/
- private void applyFilter( final ContactListFilter filter,
- final ExternalContactSource contactSource)
+ private class FilterThread extends Thread
{
- // If we're in the event dispatch thread we move to another thread
- // for the filtering.
- if (SwingUtilities.isEventDispatchThread())
+ private ContactListFilter filter;
+
+ public void setFilter(ContactListFilter filter)
{
- new Thread()
- {
- public void run()
- {
- applyFilter(filter, contactSource);
- }
- }.start();
- return;
+ this.filter = filter;
}
- if (currentFilter == null || !currentFilter.equals(filter))
- this.currentFilter = filter;
-
- // If we have a specific contact source and we're dealing with
- // a ContactListSourceFilter then we would apply the filter only
- // to this source.
- if (contactSource != null
- && filter instanceof ContactListSourceFilter)
+ public void run()
{
- ContactQuery contactQuery
- = ((ContactListSourceFilter) currentFilter)
- .applyFilter(contactSource);
+ while (true)
+ {
+ FilterQuery filterQuery = currentFilterQuery;
- filterQuery.addContactQuery(contactQuery);
+ treeModel.clear();
+
+ if (!filterQuery.isCanceled())
+ {
+ if (currentFilter == null || !currentFilter.equals(filter))
+ currentFilter = filter;
+
+ currentFilter.applyFilter(filterQuery);
+ }
+
+ synchronized (this)
+ {
+ try
+ {
+ // If in the mean time someone has changed the filter
+ // we don't wait here.
+ if (filterQuery == currentFilterQuery)
+ this.wait();
+ }
+ catch (InterruptedException e)
+ {
+ logger.debug("Search thread was interrupted.", e);
+ }
+ }
+ }
}
- else
- currentFilter.applyFilter();
}
/**
@@ -1005,6 +1057,13 @@ public void run()
public void setDefaultFilter(ContactListFilter filter)
{
this.defaultFilter = filter;
+
+ if (defaultFilter.equals(presenceFilter))
+ TreeContactList.searchFilter
+ .setSearchSourceType(SearchFilter.DEFAULT_SOURCE);
+ else if (defaultFilter.equals(historyFilter))
+ TreeContactList.searchFilter
+ .setSearchSourceType(SearchFilter.HISTORY_SOURCE);
}
/**
@@ -1016,6 +1075,16 @@ public ContactListFilter getCurrentFilter()
return currentFilter;
}
+ /**
+ * Indicates if this contact list is empty.
+ * @return true if this contact list contains no children,
+ * otherwise returns false
+ */
+ public boolean isEmpty()
+ {
+ return (treeModel.getRoot().getChildCount() <= 0);
+ }
+
/**
* Selects the first found contact node from the beginning of the contact
* list.
@@ -1107,9 +1176,7 @@ public void run()
});
}
else
- {
expandPath(path);
- }
}
/**
@@ -1681,8 +1748,7 @@ public void contactPresenceStatusChanged(
if (currentFilter != null && !currentFilter.isMatching(uiContact))
removeContact(uiContact);
else
- treeModel
- .nodeChanged(uiContact.getContactNode());
+ treeModel.nodeChanged(uiContact.getContactNode());
}
}
}
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
index 9943f7528..fc4afedab 100644
--- 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
@@ -40,15 +40,10 @@ public class MetaContactListSource
= MetaUIGroup.class.getName() + ".uiGroupDescriptor";
/**
- * Indicates if we should be filtering.
+ * The initial result count below which we insert all filter results
+ * directly to the contact list without firing events.
*/
- private boolean isFiltering = false;
-
- /**
- * The MetaContactQueryListener listens for MetaContact s
- * and MetaGroup s received as a result of a filtering.
- */
- private static MetaContactQueryListener queryListener;
+ private final int INITIAL_CONTACT_COUNT = 30;
/**
* Returns the UIContact corresponding to the given
@@ -137,27 +132,38 @@ public static boolean isRootGroup(MetaContactGroup group)
return group.equals(GuiActivator.getContactListService().getRoot());
}
- /**
- * Stops the meta contact list filtering.
- */
- public void stopFiltering()
- {
- isFiltering = false;
- }
-
/**
* Filters the MetaContactListService to match the given
* filterPattern and stores the result in the given
* treeModel .
* @param filterPattern the pattern to filter through
+ * @return the created MetaContactQuery corresponding to the
+ * query this method does
*/
- public void filter(Pattern filterPattern)
+ public MetaContactQuery queryMetaContactSource(final Pattern filterPattern)
{
- isFiltering = true;
+ final MetaContactQuery query = new MetaContactQuery();
- filter(filterPattern, GuiActivator.getContactListService().getRoot());
+ new Thread()
+ {
+ public void run()
+ {
+ int resultCount = 0;
+ queryMetaContactSource( filterPattern,
+ GuiActivator.getContactListService().getRoot(),
+ query,
+ resultCount);
+
+ if (!query.isCanceled())
+ query.fireQueryEvent(
+ MetaContactQueryStatusEvent.QUERY_COMPLETED);
+ else
+ query.fireQueryEvent(
+ MetaContactQueryStatusEvent.QUERY_CANCELED);
+ }
+ }.start();
- isFiltering = false;
+ return query;
}
/**
@@ -166,28 +172,60 @@ public void filter(Pattern filterPattern)
* treeModel .
* @param filterPattern the pattern to filter through
* @param parentGroup the MetaContactGroup to filter
+ * @param query the object that tracks the query
+ * @param resultCount the initial result count we would insert directly to
+ * the contact list without firing events
*/
- private void filter(Pattern filterPattern,
- MetaContactGroup parentGroup)
+ private void queryMetaContactSource(Pattern filterPattern,
+ MetaContactGroup parentGroup,
+ MetaContactQuery query,
+ int resultCount)
{
Iterator childContacts = parentGroup.getChildContacts();
- while (childContacts.hasNext() && isFiltering)
+ while (childContacts.hasNext() && !query.isCanceled())
{
MetaContact metaContact = childContacts.next();
if (isMatching(filterPattern, metaContact))
{
- fireQueryEvent(metaContact);
+ resultCount++;
+
+ if (resultCount <= INITIAL_CONTACT_COUNT)
+ {
+ UIGroup uiGroup = null;
+ if (!MetaContactListSource.isRootGroup(parentGroup))
+ {
+ uiGroup = MetaContactListSource
+ .getUIGroup(parentGroup);
+
+ if (uiGroup == null)
+ uiGroup = MetaContactListSource
+ .createUIGroup(parentGroup);
+ }
+
+ GuiActivator.getContactList().addContact(
+ MetaContactListSource.createUIContact(metaContact),
+ uiGroup,
+ true);
+
+ query.setInitialResultCount(resultCount);
+ }
+ else
+ query.fireQueryEvent(metaContact);
}
}
+ // If in the meantime the query is canceled we return here.
+ if(query.isCanceled())
+ return;
+
Iterator subgroups = parentGroup.getSubgroups();
- while (subgroups.hasNext() && isFiltering)
+ while (subgroups.hasNext() && !query.isCanceled())
{
MetaContactGroup subgroup = subgroups.next();
- filter(filterPattern, subgroup);
+ queryMetaContactSource(filterPattern, subgroup, query, resultCount);
}
}
@@ -251,45 +289,4 @@ public boolean isMatching(Pattern filterPattern, MetaContactGroup metaGroup)
}
return false;
}
-
- /**
- * Sets the given MetaContactQueryListener to listen for query
- * events coming from MetaContactListService filtering.
- * @param l the MetaContactQueryListener to set
- */
- public static void setMetaContactQueryListener(MetaContactQueryListener l)
- {
- queryListener = l;
- }
-
- /**
- * Returns the currently registered MetaContactQueryListener .
- * @return the currently registered MetaContactQueryListener
- */
- public static MetaContactQueryListener getMetaContactQueryListener()
- {
- return queryListener;
- }
-
- /**
- * Notifies the MetaContactQueryListener that a new
- * MetaContact has been received as a result of a search.
- * @param metaContact the received MetaContact
- */
- public static void fireQueryEvent(MetaContact metaContact)
- {
- if (queryListener != null)
- queryListener.metaContactReceived(metaContact);
- }
-
- /**
- * Notifies the MetaContactQueryListener that a new
- * MetaGroup has been received as a result of a search.
- * @param metaGroup the received MetaGroup
- */
- public static void fireQueryEvent(MetaContactGroup metaGroup)
- {
- if (queryListener != null)
- queryListener.metaGroupReceived(metaGroup);
- }
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQuery.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQuery.java
new file mode 100644
index 000000000..34e8bf021
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQuery.java
@@ -0,0 +1,178 @@
+/*
+ * 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 net.java.sip.communicator.service.contactlist.*;
+
+/**
+ * The MetaContactQuery corresponds to a particular query made through
+ * the MetaContactListSource . Each query once started could be
+ * canceled. One could also register a listener in order to be notified for
+ * changes in query status and query contact results.
+ *
+ * @author Yana Stamcheva
+ */
+public class MetaContactQuery
+{
+ private boolean isCanceled = false;
+
+ private int resultCount = 0;
+
+ /**
+ * A list of all registered query listeners.
+ */
+ private final List queryListeners
+ = new LinkedList();
+
+ /**
+ * Cancels this query.
+ */
+ public void cancel()
+ {
+ isCanceled = true;
+ queryListeners.clear();
+ }
+
+ /**
+ * Returns true if this query has been canceled, otherwise returns
+ * false .
+ * @return true if this query has been canceled, otherwise returns
+ * false .
+ */
+ public boolean isCanceled()
+ {
+ return isCanceled;
+ }
+
+ /**
+ * Returns the current number of results received for this query.
+ * @return the current number of results received for this query
+ */
+ public int getResultCount()
+ {
+ return resultCount;
+ }
+
+ /**
+ * Sets the result count of this query. This method is meant to be used to
+ * set the initial result count which is before firing any events. The
+ * result count would be then augmented each time the fireQueryEvent is
+ * called.
+ * @param resultCount the initial result count to set
+ */
+ public void setInitialResultCount(int resultCount)
+ {
+ this.resultCount = resultCount;
+ }
+
+ /**
+ * Adds the given MetaContactQueryListener to the list of
+ * registered listeners. The MetaContactQueryListener would be
+ * notified each time a new MetaContactQuery result has been
+ * received or if the query has been completed or has been canceled by user
+ * or for any other reason.
+ * @param l the MetaContactQueryListener to add
+ */
+ public void addContactQueryListener(MetaContactQueryListener l)
+ {
+ synchronized (queryListeners)
+ {
+ queryListeners.add(l);
+ }
+ }
+
+ /**
+ * Removes the given MetaContactQueryListener to the list of
+ * registered listeners. The MetaContactQueryListener would be
+ * notified each time a new MetaContactQuery result has been
+ * received or if the query has been completed or has been canceled by user
+ * or for any other reason.
+ * @param l the MetaContactQueryListener to remove
+ */
+ public void removeContactQueryListener(MetaContactQueryListener l)
+ {
+ synchronized (queryListeners)
+ {
+ queryListeners.remove(l);
+ }
+ }
+
+ /**
+ * Notifies the MetaContactQueryListener that a new
+ * MetaContact has been received as a result of a search.
+ * @param metaContact the received MetaContact
+ */
+ public void fireQueryEvent(MetaContact metaContact)
+ {
+ resultCount++;
+
+ MetaContactQueryEvent event
+ = new MetaContactQueryEvent(this, metaContact);
+
+ List listeners;
+ synchronized (queryListeners)
+ {
+ listeners = new LinkedList(queryListeners);
+ }
+
+ Iterator listenersIter = listeners.iterator();
+ while (listenersIter.hasNext())
+ {
+ MetaContactQueryListener listener = listenersIter.next();
+
+ listener.metaContactReceived(event);
+ }
+ }
+
+ /**
+ * Notifies the MetaContactQueryListener that a new
+ * MetaGroup has been received as a result of a search.
+ * @param metaGroup the received MetaGroup
+ */
+ public void fireQueryEvent(MetaContactGroup metaGroup)
+ {
+ MetaGroupQueryEvent event
+ = new MetaGroupQueryEvent(this, metaGroup);
+
+ List listeners;
+ synchronized (queryListeners)
+ {
+ listeners = new LinkedList(queryListeners);
+ }
+
+ Iterator listenersIter = listeners.iterator();
+ while (listenersIter.hasNext())
+ {
+ listenersIter.next().metaGroupReceived(event);
+ }
+ }
+
+ /**
+ * Notifies the MetaContactQueryListener that this query has
+ * changed its status.
+ * @param queryStatus the new query status
+ */
+ public void fireQueryEvent(int queryStatus)
+ {
+ MetaContactQueryStatusEvent event
+ = new MetaContactQueryStatusEvent(this, queryStatus);
+
+ List listeners;
+ synchronized (queryListeners)
+ {
+ listeners = new LinkedList(queryListeners);
+ }
+
+ Iterator listenersIter = listeners.iterator();
+ while (listenersIter.hasNext())
+ {
+ listenersIter.next().metaContactQueryStatusChanged(event);
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQueryEvent.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQueryEvent.java
new file mode 100644
index 000000000..25984ce16
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQueryEvent.java
@@ -0,0 +1,59 @@
+/*
+ * 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 net.java.sip.communicator.service.contactlist.*;
+
+/**
+ * The MetaContactQueryEvent is triggered each time a
+ * MetaContact is received as a result of a MetaContactQuery .
+ *
+ * @author Yana Stamcheva
+ */
+public class MetaContactQueryEvent
+ extends EventObject
+{
+ /**
+ * The MetaContact this event is about.
+ */
+ private final MetaContact metaContact;
+
+ /**
+ * Creates an instance of MetaGroupQueryEvent by specifying the
+ * source query this event comes from and the metaContact
+ * this event is about.
+ *
+ * @param source the MetaContactQuery that triggered this event
+ * @param metaContact the MetaContact this event is about
+ */
+ public MetaContactQueryEvent( MetaContactQuery source,
+ MetaContact metaContact)
+ {
+ super(source);
+ this.metaContact = metaContact;
+ }
+
+ /**
+ * Returns the MetaContactQuery that triggered this event.
+ * @return the MetaContactQuery that triggered this event
+ */
+ public MetaContactQuery getQuerySource()
+ {
+ return (MetaContactQuery) source;
+ }
+
+ /**
+ * Returns the MetaContact this event is about.
+ * @return the MetaContact this event is about
+ */
+ public MetaContact getMetaContact()
+ {
+ return metaContact;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/MetaContactQueryListener.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQueryListener.java
similarity index 65%
rename from src/net/java/sip/communicator/impl/gui/main/contactlist/MetaContactQueryListener.java
rename to src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQueryListener.java
index 838c021d2..ff2422d1c 100644
--- a/src/net/java/sip/communicator/impl/gui/main/contactlist/MetaContactQueryListener.java
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQueryListener.java
@@ -4,7 +4,7 @@
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
-package net.java.sip.communicator.impl.gui.main.contactlist;
+package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
import net.java.sip.communicator.service.contactlist.*;
@@ -21,12 +21,18 @@ public interface MetaContactQueryListener
* the MetaContactListService .
* @param metaContact the received MetaContact
*/
- public void metaContactReceived(MetaContact metaContact);
+ public void metaContactReceived(MetaContactQueryEvent event);
/**
* Indicates that a MetaGroup has been received from a search in
* the MetaContactListService .
* @param metaGroup the MetaGroup that has been received
*/
- public void metaGroupReceived(MetaContactGroup metaGroup);
+ public void metaGroupReceived(MetaGroupQueryEvent event);
+
+ /**
+ * Indicates that a query has changed its status.
+ * @param event the MetaContactQueryStatusEvent that notified us
+ */
+ public void metaContactQueryStatusChanged(MetaContactQueryStatusEvent event);
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQueryStatusEvent.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQueryStatusEvent.java
new file mode 100644
index 000000000..ff84db1d2
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaContactQueryStatusEvent.java
@@ -0,0 +1,74 @@
+/*
+ * 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.*;
+
+/**
+ * The MetaContactQueryStatusEvent is triggered each time a
+ * MetaContactQuery changes its status. Possible statuses are:
+ * QUERY_COMPLETED, QUERY_CANCELED and QUERY_ERROR.
+ *
+ * @author Yana Stamcheva
+ */
+public class MetaContactQueryStatusEvent
+ extends EventObject
+{
+ /**
+ * Indicates that a query has been completed.
+ */
+ public static final int QUERY_COMPLETED = 0;
+
+ /**
+ * Indicates that a query has been canceled.
+ */
+ public static final int QUERY_CANCELED = 1;
+
+ /**
+ * Indicates that a query has been stopped because of an error.
+ */
+ public static final int QUERY_ERROR = 2;
+
+ /**
+ * Indicates the type of this event.
+ */
+ private final int eventType;
+
+ /**
+ * Creates a MetaContactQueryStatusEvent by specifying the source
+ * MetaContactQuery and the eventType indicating why
+ * initially this event occurred.
+ * @param source the initiator of the event
+ * @param eventType the type of the event. One of the QUERY_XXX constants
+ * defined in this class
+ */
+ public MetaContactQueryStatusEvent( MetaContactQuery source,
+ int eventType)
+ {
+ super(source);
+
+ this.eventType = eventType;
+ }
+
+ /**
+ * Returns the ContactQuery that triggered this event.
+ * @return the ContactQuery that triggered this event
+ */
+ public MetaContactQuery getQuerySource()
+ {
+ return (MetaContactQuery) source;
+ }
+
+ /**
+ * Returns the type of this event.
+ * @return the type of this event
+ */
+ public int getEventType()
+ {
+ return eventType;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaGroupQueryEvent.java b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaGroupQueryEvent.java
new file mode 100644
index 000000000..f16f6481d
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/contactlist/contactsource/MetaGroupQueryEvent.java
@@ -0,0 +1,60 @@
+/*
+ * 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 net.java.sip.communicator.service.contactlist.*;
+
+/**
+ * The MetaGroupQueryEvent is triggered each time a
+ * MetaContactGroup is received as a result of a
+ * MetaContactQuery .
+ *
+ * @author Yana Stamcheva
+ */
+public class MetaGroupQueryEvent
+ extends EventObject
+{
+ /**
+ * The MetaContactGroup this event is about.
+ */
+ private final MetaContactGroup metaGroup;
+
+ /**
+ * Creates an instance of MetaGroupQueryEvent by specifying the
+ * source query this event comes from and the metaGroup
+ * this event is about.
+ *
+ * @param source the MetaContactQuery that triggered this event
+ * @param metaGroup the MetaContactGroup this event is about
+ */
+ public MetaGroupQueryEvent( MetaContactQuery source,
+ MetaContactGroup metaGroup)
+ {
+ super(source);
+ this.metaGroup = metaGroup;
+ }
+
+ /**
+ * Returns the MetaContactQuery that triggered this event.
+ * @return the MetaContactQuery that triggered this event
+ */
+ public MetaContactQuery getQuerySource()
+ {
+ return (MetaContactQuery) source;
+ }
+
+ /**
+ * Returns the MetaContactGroup this event is about.
+ * @return the MetaContactGroup this event is about
+ */
+ public MetaContactGroup getMetaGroup()
+ {
+ return metaGroup;
+ }
+}
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 833aedabd..7fb1dc6c9 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,17 +119,9 @@ else if (itemName.equals("showHideOffline"))
TreeContactList.presenceFilter.setShowOffline(!isShowOffline);
- new Thread()
- {
- public void run()
- {
- GuiActivator.getContactList()
- .setDefaultFilter(TreeContactList.presenceFilter);
- GuiActivator.getContactList().applyDefaultFilter();
- }
- }.start();
-
- ConfigurationManager.setShowOffline(!isShowOffline);
+ GuiActivator.getContactList()
+ .setDefaultFilter(TreeContactList.presenceFilter);
+ GuiActivator.getContactList().applyDefaultFilter();
String itemTextKey = !isShowOffline
? "service.gui.HIDE_OFFLINE_CONTACTS"
diff --git a/src/net/java/sip/communicator/service/callhistory/event/CallHistoryQueryStatusEvent.java b/src/net/java/sip/communicator/service/callhistory/event/CallHistoryQueryStatusEvent.java
index 416837555..9dc9601bf 100644
--- a/src/net/java/sip/communicator/service/callhistory/event/CallHistoryQueryStatusEvent.java
+++ b/src/net/java/sip/communicator/service/callhistory/event/CallHistoryQueryStatusEvent.java
@@ -20,21 +20,6 @@
public class CallHistoryQueryStatusEvent
extends EventObject
{
- /**
- * Indicates that a query has been completed.
- */
- public static final int QUERY_COMPLETED = 0;
-
- /**
- * Indicates that a query has been canceled.
- */
- public static final int QUERY_CANCELED = 1;
-
- /**
- * Indicates that a query has been stopped because of an error.
- */
- public static final int QUERY_ERROR = 2;
-
/**
* Indicates the type of this event.
*/
@@ -46,7 +31,7 @@ public class CallHistoryQueryStatusEvent
* initially this event occurred.
* @param source the CallHistoryQuery this event is about
* @param eventType the type of the event. One of the QUERY_XXX constants
- * defined in this class
+ * defined in the CallHistoryQuery
*/
public CallHistoryQueryStatusEvent( CallHistoryQuery source,
int eventType)
diff --git a/src/net/java/sip/communicator/service/contactsource/ContactQuery.java b/src/net/java/sip/communicator/service/contactsource/ContactQuery.java
index 1da6d7a1e..f0beee313 100644
--- a/src/net/java/sip/communicator/service/contactsource/ContactQuery.java
+++ b/src/net/java/sip/communicator/service/contactsource/ContactQuery.java
@@ -18,6 +18,26 @@
*/
public interface ContactQuery
{
+ /**
+ * Indicates that this query has been completed.
+ */
+ public static final int QUERY_COMPLETED = 0;
+
+ /**
+ * Indicates that this query has been canceled.
+ */
+ public static final int QUERY_CANCELED = 1;
+
+ /**
+ * Indicates that this query has been stopped because of an error.
+ */
+ public static final int QUERY_ERROR = 2;
+
+ /**
+ * Indicates that this query is in progress.
+ */
+ public static final int QUERY_IN_PROGRESS = 3;
+
/**
* Returns the ContactSourceService , where this query was first
* initiated.
@@ -37,6 +57,13 @@ public interface ContactQuery
*/
public void cancel();
+ /**
+ * Returns the status of this query. One of the static constants QUERY_XXXX
+ * defined in this class.
+ * @return the status of this query
+ */
+ public int getStatus();
+
/**
* Adds the given ContactQueryListener to the list of registered
* listeners. The ContactQueryListener would be notified each
diff --git a/src/net/java/sip/communicator/util/swing/LowPriorityEventQueue.java b/src/net/java/sip/communicator/util/swing/LowPriorityEventQueue.java
new file mode 100644
index 000000000..88f4acac4
--- /dev/null
+++ b/src/net/java/sip/communicator/util/swing/LowPriorityEventQueue.java
@@ -0,0 +1,48 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.util.swing;
+
+import java.awt.*;
+import java.awt.event.*;
+
+/**
+ * The LowPriorityEventQueue schedules low priority events to be
+ * dispatched through the system event queue.
+ *
+ * @author Yana Stamcheva
+ */
+public class LowPriorityEventQueue
+{
+ /**
+ * Causes runnable to have its run
+ * method called in the event dispatch thread with low priority.
+ *
+ * @param runnable the Runnable whose run
+ * method should be executed synchronously on the EventQueue
+ */
+ public static void invokeLater(Runnable runnable)
+ {
+ Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(
+ new LowPriorityInvocationEvent(
+ Toolkit.getDefaultToolkit(), runnable));
+ }
+
+ /**
+ * The LowPriorityInvocationEvent is an InvocationEvent
+ * that replaces the default event id with the PaintEvent.UPDATE
+ * in order to indicate that this event should be dispatched with the same
+ * priority as an update paint event, which is normally with lower priority
+ * than other events.
+ */
+ private static class LowPriorityInvocationEvent extends InvocationEvent
+ {
+ public LowPriorityInvocationEvent(Object source, Runnable runnable)
+ {
+ super(source, PaintEvent.UPDATE, runnable, null, false);
+ }
+ }
+}