Implements "show more" functionality in contact list search.

cusax-fix
Yana Stamcheva 15 years ago
parent 7f49c4b20f
commit ef41596c0a

@ -112,6 +112,7 @@ service.gui.CHAT_ROOMS=Chat rooms
service.gui.CHAT_ROOM_SUBJECT_CHANGED={0} has changed the subject to {1}
service.gui.CHOOSE_CONTACT=Choose contact
service.gui.CHOOSE_ACCOUNT=Please select one of the listed accounts.
service.gui.SHOW_MORE_TOOLTIP=Click to show more results
service.gui.CLOSE=Cl&ose
service.gui.CLOSE_CHAT_AFTER_NEW_MESSAGE=You have received a new message less than 2 seconds ago. Are you sure you want to close this chat?
service.gui.CLOSE_CHAT_ACTIVE_FILE_TRANSFER=You have active file transfers. Are you sure you want to cancel them?
@ -395,6 +396,7 @@ service.gui.SHARE_FULL_SCREEN=Share full screen
service.gui.SHARE_REGION=Share region
service.gui.SHOW=Show
service.gui.SHOW_CONTACT_LIST_TOOL_TIP=Click here to switch off the history view and show your contact list.
service.gui.SHOW_MORE=show more...
service.gui.SHOW_OFFLINE_CONTACTS=Show offline contacts
service.gui.SIGN_IN=Sign in
service.gui.SMS_SUCCESSFULLY_SENT=SMS message successfully sent!

@ -40,15 +40,26 @@ public String getDisplayName()
* @return the created query
*/
public ContactQuery queryContactSource(String queryString)
{
return queryContactSource(queryString, 50);
}
/**
* Queries this contact source for the given <tt>searchString</tt>.
* @param queryString the string to search for
* @param contactCount the maximum count of result contacts
* @return the created query
*/
public ContactQuery queryContactSource(String queryString, int contactCount)
{
if (queryString != null && queryString.length() > 0)
return new CallHistoryContactQuery(
CallHistoryActivator.getCallHistoryService()
.findByPeer(queryString, 50));
.findByPeer(queryString, contactCount));
else
return new CallHistoryContactQuery(
CallHistoryActivator.getCallHistoryService()
.findLast(50));
.findLast(contactCount));
}
/**

@ -15,6 +15,9 @@
import javax.swing.plaf.basic.*;
import javax.swing.tree.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
/**
* SIPCommTreeUI implementation.
*
@ -135,6 +138,31 @@ protected AbstractLayoutCache createLayoutCache()
return layoutCache;
}
/**
* Do not select the <tt>ShowMoreContact</tt>.
*
* @param path the <tt>TreePath</tt> to select
* @param event the <tt>MouseEvent</tt> that provoked the select
*/
protected void selectPathForEvent(TreePath path, MouseEvent event)
{
Object lastComponent = path.getLastPathComponent();
// Open right button menu when right mouse is pressed.
if (lastComponent instanceof ContactNode)
{
UIContact uiContact
= ((ContactNode) lastComponent).getContactDescriptor();
if (!(uiContact instanceof ShowMoreContact))
{
super.selectPathForEvent(path, event);
}
}
else
super.selectPathForEvent(path, event);
}
/**
* A custom layout cache that recalculates the width of the cell the match
* the width of the tree (i.e. expands the cell to the right).

@ -50,7 +50,7 @@ public void applyFilter(FilterQuery filterQuery)
continue;
// We're in a case of call history contact source.
ContactQuery query = sourceService.queryContactSource("");
ContactQuery query = sourceService.queryContactSource("", 50);
filterQuery.addContactQuery(query);
// Add first available results.

@ -23,8 +23,6 @@
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.neomedia.device.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.skin.*;
@ -202,7 +200,6 @@ public ContactListTreeCellRenderer()
this.displayDetailsLabel.setFont(getFont().deriveFont(9f));
this.displayDetailsLabel.setForeground(Color.GRAY);
this.rightLabel.setFont(rightLabel.getFont().deriveFont(9f));
this.rightLabel.setHorizontalAlignment(JLabel.RIGHT);
statusLabel.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 2));
@ -365,7 +362,9 @@ public Component getTreeCellRendererComponent(JTree tree, Object value,
String displayName = contact.getDisplayName();
if (displayName == null || displayName.trim().length() < 1)
if ((displayName == null
|| displayName.trim().length() < 1)
&& !(contact instanceof ShowMoreContact))
{
displayName = GuiActivator.getResources()
.getI18NString("service.gui.UNKNOWN");
@ -382,7 +381,16 @@ public Component getTreeCellRendererComponent(JTree tree, Object value,
this.nameLabel.setFont(this.getFont().deriveFont(Font.PLAIN));
if (contactForegroundColor != null)
this.nameLabel.setForeground(contactForegroundColor);
nameLabel.setForeground(contactForegroundColor);
if (contact instanceof ShowMoreContact)
{
rightLabel.setFont(rightLabel.getFont().deriveFont(12f));
rightLabel.setForeground(Color.GRAY);
rightLabel.setText((String)contact.getDescriptor());
}
else
rightLabel.setText("");
// Initializes status message components if the given meta contact
// contains a status message.
@ -410,7 +418,6 @@ public Component getTreeCellRendererComponent(JTree tree, Object value,
{
this.rightLabel.setIcon(avatar);
}
this.rightLabel.setText("");
this.setToolTipText(contact.getDescriptor().toString());
}
@ -440,10 +447,15 @@ else if (value instanceof GroupNode)
// We have no photo icon for groups.
this.rightLabel.setIcon(null);
this.rightLabel.setText("");
if (groupItem.countChildContacts() >= 0)
{
rightLabel.setFont(rightLabel.getFont().deriveFont(9f));
this.rightLabel.setForeground(Color.BLACK);
this.rightLabel.setText( groupItem.countOnlineChildContacts()
+ "/" + groupItem.countChildContacts());
}
this.setToolTipText(groupItem.getDescriptor().toString());
}
@ -599,10 +611,17 @@ public Dimension getPreferredSize()
Dimension preferredSize = new Dimension();
if (treeNode instanceof ContactNode)
if (isSelected)
{
UIContact contact
= ((ContactNode) treeNode).getContactDescriptor();
if (contact instanceof ShowMoreContact)
preferredSize.height = 18;
else if (isSelected)
preferredSize.height = 55;
else
preferredSize.height = 30;
}
else
preferredSize.height = 18;

@ -21,6 +21,11 @@ public class FilterQuery
implements ContactQueryListener,
MetaContactQueryListener
{
/**
* The maximum result count for each contact source.
*/
public static final int MAX_EXTERNAL_RESULT_COUNT = 10;
/**
* A listener, which is notified when this query finishes.
*/
@ -48,8 +53,9 @@ public class FilterQuery
/**
* The list of filter queries.
*/
private Collection<Object> filterQueries
= Collections.synchronizedCollection(new Vector<Object>());
private Map<Object, List<SourceContact>> filterQueries
= Collections.synchronizedMap(new Hashtable<Object,
List<SourceContact>>());
/**
* Indicates the number of running queries.
@ -73,13 +79,27 @@ public void addContactQuery(Object contactQuery)
return;
}
filterQueries.add(contactQuery);
runningQueries++;
List<SourceContact> queryResults = new ArrayList<SourceContact>();
if (contactQuery instanceof ContactQuery)
((ContactQuery) contactQuery).addContactQueryListener(this);
{
ContactQuery externalQuery = (ContactQuery) contactQuery;
List<SourceContact> externalResults
= externalQuery.getQueryResults();
if (externalResults != null && externalResults.size() > 0)
queryResults = new ArrayList<SourceContact>(externalResults);
externalQuery.addContactQueryListener(this);
}
else if (contactQuery instanceof MetaContactQuery)
{
((MetaContactQuery) contactQuery).addContactQueryListener(this);
}
filterQueries.put(contactQuery, queryResults);
runningQueries++;
}
}
@ -124,7 +144,7 @@ public void cancel()
{
isCanceled = true;
queriesIter = filterQueries.iterator();
queriesIter = filterQueries.keySet().iterator();
while (queriesIter.hasNext())
{
cancelQuery(queriesIter.next());
@ -177,7 +197,7 @@ 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.containsKey(query)
|| event.getEventType() == ContactQuery.QUERY_IN_PROGRESS)
return;
@ -214,7 +234,7 @@ public void metaContactQueryStatusChanged(MetaContactQueryStatusEvent event)
MetaContactQuery query = event.getQuerySource();
// Check if this query is in our filter queries list.
if (!filterQueries.contains(query))
if (!filterQueries.containsKey(query))
return;
// First set the isSucceeded property.
@ -270,10 +290,49 @@ else if (query instanceof MetaContactQuery)
*/
public boolean containsQuery(Object query)
{
return filterQueries.contains(query);
return filterQueries.containsKey(query);
}
public void contactReceived(ContactReceivedEvent event) {}
/**
* Cancels asynchronous queries after the maximum desired result count is
* reached.
*
* @param query the query we're interested in
* @param contact the source contact we just received as a result of the
* given query
*/
private void contactReceived(ContactQuery query, SourceContact contact)
{
List<SourceContact> queryResults = filterQueries.get(query);
queryResults.add(contact);
if (queryResults.size() == MAX_EXTERNAL_RESULT_COUNT)
{
query.removeContactQueryListener(GuiActivator.getContactList());
ShowMoreContact moreInfoContact
= new ShowMoreContact(query, queryResults);
ContactSourceService contactSource = query.getContactSource();
GuiActivator.getContactList().addContact(
query,
moreInfoContact,
TreeContactList.getContactSource(contactSource).getUIGroup(),
false);
}
}
/**
* Indicates that a contact has been received as a result of a query.
*
* @param event the <tt>ContactReceivedEvent</tt> that notified us
*/
public void contactReceived(ContactReceivedEvent event)
{
contactReceived(event.getQuerySource(), event.getContact());
}
public void metaContactReceived(MetaContactQueryEvent event) {}

@ -805,14 +805,16 @@ private void addContact(final MetaContactQuery query,
{
public void run()
{
// If in the meantime the corresponding query was canceled
// we don't proceed with adding.
if (query != null && !query.isCanceled())
addContact(contact, group, isSorted, true);
addContact(query, contact, group, isSorted);
}
});
return;
}
// If in the meantime the corresponding query was canceled
// we don't proceed with adding.
if (query != null && !query.isCanceled())
addContact(contact, group, isSorted, true);
}
/**
@ -823,7 +825,7 @@ public void run()
* @param isSorted indicates if the contact should be sorted regarding to
* the <tt>GroupNode</tt> policy
*/
private void addContact(final ContactQuery query,
public void addContact(final ContactQuery query,
final UIContact contact,
final UIGroup group,
final boolean isSorted)
@ -834,17 +836,19 @@ private void addContact(final ContactQuery query,
{
public void run()
{
// If in the meantime the filter has changed we don't
// add the contact.
if (query != null
&& currentFilterQuery.containsQuery(query))
{
addContact(contact, group, isSorted, true);
}
addContact(query, contact, group, isSorted);
}
});
return;
}
// If in the meantime the filter has changed we don't
// add the contact.
if (query != null
&& currentFilterQuery.containsQuery(query))
{
addContact(contact, group, isSorted, true);
}
}
/**
@ -1362,22 +1366,21 @@ public void mousePressed(MouseEvent e)
if (path == null)
return;
Object lastComponent = path.getLastPathComponent();
// We're interested only if the mouse is clicked over a tree node.
if (!(lastComponent instanceof TreeNode))
return;
// Select the node under the right button click.
if (!path.equals(getSelectionPath())
&& (e.getModifiers() & InputEvent.BUTTON1_MASK) != 0
|| (e.getModifiers() & InputEvent.BUTTON2_MASK) != 0
&& (e.getModifiers() & InputEvent.BUTTON2_MASK) != 0
|| (e.getModifiers() & InputEvent.BUTTON3_MASK) != 0
|| (e.isControlDown() && !e.isMetaDown()))
{
this.setSelectionPath(path);
}
Object lastComponent = path.getLastPathComponent();
// We're interested only if the mouse is clicked over a tree node.
if (!(lastComponent instanceof TreeNode))
return;
// Open right button menu when right mouse is pressed.
if (lastComponent instanceof ContactNode)
{
@ -1852,9 +1855,8 @@ public static void setSourceContactImage( String contactString,
if (contactSource instanceof ExtendedContactSourceService)
{
ContactQuery query
= ((ExtendedContactSourceService)
contactSource).queryContactSource(filterPattern);
ContactQuery query = ((ExtendedContactSourceService)
contactSource).queryContactSource(filterPattern);
loadedQueries.add(query);

@ -0,0 +1,282 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
import java.util.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.protocol.*;
/**
*
* @author Yana Stamcheva
*/
public class ShowMoreContact
implements UIContact,
ContactListListener
{
/**
* The string associated with this contact.
*/
private final String showMoreString
= GuiActivator.getResources().getI18NString("service.gui.SHOW_MORE");
/**
* The parent group.
*/
private UIGroup parentGroup;
/**
* The contact node corresponding to this contact.
*/
private ContactNode contactNode;
/**
* The parent contact query, which added the contact.
*/
private final ContactQuery contactQuery;
/**
* The query results.
*/
private final List<SourceContact> queryResults;
/**
* The count of shown contacts corresponding to the underlying query.
*/
private int shownResultsCount = FilterQuery.MAX_EXTERNAL_RESULT_COUNT;
/**
* Creates an instance of <tt>MoreInfoContact</tt>.
*
* @param contactQuery
* @param queryResults
*/
public ShowMoreContact( ContactQuery contactQuery,
List<SourceContact> queryResults)
{
this.contactQuery = contactQuery;
this.queryResults = queryResults;
GuiActivator.getContactList().addContactListListener(this);
}
/**
* Returns the descriptor of this contact.
*
* @return the descriptor of this contact
*/
public Object getDescriptor()
{
return showMoreString;
}
/**
* Returns an empty string to indicate that this contact has no display
* name.
*
* @return an empty string
*/
public String getDisplayName()
{
return "";
}
/**
* Returns null to indicate that there are no display details.
*
* @return null
*/
public String getDisplayDetails()
{
return null;
}
/**
* Returns Integer.MAX_VALUE to indicate that this contact should be placed
* at the end of its parent group.
*
* @return Integer.MAX_VALUE
*/
public int getSourceIndex()
{
return Integer.MAX_VALUE;
}
/**
* Returns null to indicate that this contact has no avatar.
*
* @param isSelected indicates if the contact is selected
* @param width avatar width
* @param height avatar height
* @return null
*/
public ImageIcon getAvatar(boolean isSelected, int width, int height)
{
return null;
}
/**
* Returns null to indicate that this contact has no status icon.
*
* @return null
*/
public ImageIcon getStatusIcon()
{
return null;
}
/**
* Returns an extended tooltip for this contact.
*
* @return the created tooltip
*/
public ExtendedTooltip getToolTip()
{
ExtendedTooltip tooltip = new ExtendedTooltip(false);
tooltip.setTitle(GuiActivator.getResources()
.getI18NString("service.gui.SHOW_MORE_TOOLTIP"));
return tooltip;
}
/**
* Returns null to indicate that this contact has no right button menu.
*
* @return null
*/
public JPopupMenu getRightButtonMenu()
{
return null;
}
/**
* Returns the parent group of this contact.
*
* @return the parent group of this contact
*/
public UIGroup getParentGroup()
{
return parentGroup;
}
/**
* Sets the parent group of this contact
*
* @param parentGroup the parent group of this contact
*/
public void setParentGroup(UIGroup parentGroup)
{
this.parentGroup = parentGroup;
}
/**
* Returns null to indicate that this contact cannot be searched.
*
* @return null
*/
public Iterator<String> getSearchStrings()
{
return null;
}
/**
* Returns the corresponding contact node.
*
* @return the corresponding contact node
*/
public ContactNode getContactNode()
{
return contactNode;
}
/**
* Sets the corresponding contact node.
*
* @param contactNode the contact node to set
*/
public void setContactNode(ContactNode contactNode)
{
this.contactNode = contactNode;
}
/**
* Returns null to indicate that this contact has no contact details.
*
* @param opSetClass the <tt>OperationSet</tt> class, which details we're
* looking for
* @return null
*/
public UIContactDetail getDefaultContactDetail(
Class<? extends OperationSet> opSetClass)
{
return null;
}
/**
* Returns null to indicate that this contact has no contact details.
*
* @param opSetClass the <tt>OperationSet</tt> class, which details we're
* looking for
* @return null
*/
public List<UIContactDetail> getContactDetailsForOperationSet(
Class<? extends OperationSet> opSetClass)
{
return null;
}
/**
* Indicates that a contact has been clicked in the contact list. Show some
* more contacts after the "show more" has been clicked
*
* @param evt the <tt>ContactListEvent</tt> that notified us
*/
public void contactClicked(ContactListEvent evt)
{
if (evt.getSourceContact().equals(this))
{
List<SourceContact> contacts
= new ArrayList<SourceContact>(queryResults);
int newCount
= shownResultsCount + FilterQuery.MAX_EXTERNAL_RESULT_COUNT;
int resultSize = contacts.size();
int maxCount = (resultSize > newCount) ? newCount : resultSize;
GuiActivator.getContactList().removeContact(this);
for (int i = shownResultsCount; i < maxCount; i++)
{
GuiActivator.getContactList().contactReceived(
new ContactReceivedEvent(contactQuery, contacts.get(i)));
}
shownResultsCount = maxCount;
if (shownResultsCount < resultSize
|| contactQuery.getStatus() != ContactQuery.QUERY_COMPLETED)
GuiActivator.getContactList().addContact(
contactQuery,
this,
TreeContactList.getContactSource(
contactQuery.getContactSource()).getUIGroup(),
false);
}
}
public void groupClicked(ContactListEvent evt) {}
}

@ -33,11 +33,27 @@ public abstract class AsyncContactSourceService
*/
public ContactQuery queryContactSource(String query)
{
return
queryContactSource(
Pattern.compile(
query,
Pattern.CASE_INSENSITIVE | Pattern.LITERAL));
return queryContactSource(
Pattern.compile(query, Pattern.CASE_INSENSITIVE | Pattern.LITERAL));
}
/**
* Queries this <tt>ContactSourceService</tt> for <tt>SourceContact</tt>s
* which match a specific <tt>query</tt> <tt>String</tt>.
*
* @param query the <tt>String</tt> which this <tt>ContactSourceService</tt>
* is being queried for
* @param contactCount the maximum count of result contacts
* @return a <tt>ContactQuery</tt> which represents the query of this
* <tt>ContactSourceService</tt> implementation for the specified
* <tt>String</tt> and via which the matching <tt>SourceContact</tt>s (if
* any) will be returned
* @see ContactSourceService#queryContactSource(String)
*/
public ContactQuery queryContactSource(String query, int contactCount)
{
return queryContactSource(
Pattern.compile(query, Pattern.CASE_INSENSITIVE | Pattern.LITERAL));
}
/**

@ -40,4 +40,13 @@ public interface ContactSourceService
* @return the created query
*/
public ContactQuery queryContactSource(String queryString);
/**
* Queries this search source for the given <tt>queryString</tt>.
*
* @param queryString the string to search for
* @param contactCount the maximum count of result contacts
* @return the created query
*/
public ContactQuery queryContactSource(String queryString, int contactCount);
}

Loading…
Cancel
Save