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 MetaContacts - * and MetaGroups 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); + } + } +}