- Introduces new contact list data model that allows adding of external contact sources and hence the search in such sources.

- As part of the support for external contact sources, implements a call history external source and its user interface.
- Addresses issue #706 Indicate missed calls
cusax-fix
Yana Stamcheva 16 years ago
parent c454284e43
commit afcafe3061

@ -186,3 +186,5 @@ service.gui.CALL_MEMBER_NAME_BACKGROUND=BDFFA3
# Conference call local user title panel background color.
service.gui.CALL_LOCAL_USER_BACKGROUND=A6D6FF
service.gui.SEARCH_BACKGROUND=4A9BDB

@ -68,6 +68,7 @@ service.gui.icons.BROWSER_ICON=resources/images/impl/gui/common/browser16x16.png
service.gui.icons.AUTHORIZATION_ICON=resources/images/impl/gui/common/padlock.gif
service.gui.icons.INCOMING_CALL=resources/images/impl/gui/common/incomingCall.png
service.gui.icons.OUTGOING_CALL=resources/images/impl/gui/common/outgoingCall.png
service.gui.icons.MISSED_CALL=resources/images/impl/gui/common/missedCall.png
service.gui.icons.EXCLAMATION_MARK=resources/images/impl/gui/common/exclamationMark.png
service.gui.icons.OPENED_GROUP=resources/images/impl/gui/common/openedGroup.png
service.gui.icons.CLOSED_GROUP=resources/images/impl/gui/common/closedGroup.png
@ -173,6 +174,8 @@ service.gui.buttons.HIDE_ACTIONS_ROLLOVER_BUTTON=resources/images/impl/gui/butto
service.gui.buttons.CALL_PEER_TOOLS=resources/images/impl/gui/buttons/tools.png
service.gui.buttons.CHAT_ROOM_CONFIG=resources/images/impl/gui/buttons/chatRoomConfig.png
service.gui.buttons.CHAT_CALL=resources/images/impl/gui/buttons/chatCall.png
service.gui.buttons.CALL_HISTORY_BUTTON=resources/images/impl/gui/buttons/callHistoryButton.png
service.gui.buttons.CALL_HISTORY_BUTTON_PRESSED=resources/images/impl/gui/buttons/callHistoryButtonPressed.png
# Sound level icons
service.gui.soundlevel.SOUND_LEVEL_ACTIVE=resources/images/impl/gui/common/soundlevel/soundActive.png
@ -390,6 +393,7 @@ plugin.callhistorysearch.HISTORY_MENU_ICON=resources/images/plugin/extendedcallh
plugin.callhistorysearch.HISTORY_BUTTON=resources/images/plugin/extendedcallhistorysearch/callHistory.png
plugin.callhistorysearch.INCOMING_CALL=resources/images/plugin/extendedcallhistorysearch/incomingCall.png
plugin.callhistorysearch.OUTGOING_CALL=resources/images/plugin/extendedcallhistorysearch/outgoingCall.png
plugin.callhistorysearch.MISSED_CALL=resources/images/plugin/extendedcallhistorysearch/missedCall.png
plugin.callhistorysearch.CALENDAR_ICON=resources/images/plugin/extendedcallhistorysearch/calendarIcon.png
# whiteboard plugin icons

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1023 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
@ -13,10 +14,10 @@
height="1052.3622047"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docbase="/home/yana/images"
inkscape:version="0.47 r22583"
sodipodi:docname="incomingCall.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1">
<defs
id="defs4">
<inkscape:perspective
@ -31,7 +32,7 @@
<stop
id="stop13637"
offset="0"
style="stop-color:#fcf8a7;stop-opacity:1;" />
style="stop-color:#bf0913;stop-opacity:0.98823529;" />
<stop
style="stop-color:white;stop-opacity:1;"
offset="1"
@ -144,7 +145,7 @@
y2="253.88161" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13635"
xlink:href="#linearGradient13635-6"
id="linearGradient13657"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-0.658391,-0.561477,-0.484531,0.56816,280.0423,280.8889)"
@ -172,6 +173,76 @@
y1="418.1972"
x2="181.13237"
y2="440.79761" />
<inkscape:perspective
id="perspective6867"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient15050-9"
id="linearGradient13654-5"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-0.460625,-0.392816,-0.346379,0.406163,285.53351,327.21874)"
x1="-34.269634"
y1="250.85629"
x2="-1.3157908"
y2="251.95337" />
<linearGradient
id="linearGradient15050-9"
inkscape:collect="always">
<stop
id="stop15052-0"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop15054-1"
offset="1"
style="stop-color:#ffffff;stop-opacity:0;" />
</linearGradient>
<linearGradient
id="linearGradient13635-6">
<stop
id="stop13637-9"
offset="0"
style="stop-color:#fcf8a7;stop-opacity:1;" />
<stop
style="stop-color:white;stop-opacity:1;"
offset="1"
id="stop13639-0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5608-0"
id="linearGradient13660-4"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.938914,0,0,0.938914,44.273352,29.88622)"
x1="158.55054"
y1="418.1972"
x2="181.13237"
y2="440.79761" />
<linearGradient
id="linearGradient5608-0">
<stop
style="stop-color:white;stop-opacity:1;"
offset="0"
id="stop5610-2" />
<stop
style="stop-color:#f9ffc5;stop-opacity:0;"
offset="1"
id="stop5612-4" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient13635"
id="linearGradient7442"
x1="189.36821"
y1="420.99265"
x2="215.59256"
y2="445.39755"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
@ -183,16 +254,17 @@
objecttolerance="10"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4.8750832"
inkscape:cx="219.40637"
inkscape:cy="617.51596"
inkscape:zoom="3.4472044"
inkscape:cx="206.52909"
inkscape:cy="618.66359"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:window-width="1280"
inkscape:window-height="800"
inkscape:window-height="778"
inkscape:window-x="0"
inkscape:window-y="22"
showgrid="false" />
showgrid="false"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
@ -201,6 +273,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@ -233,7 +306,7 @@
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:url(#linearGradient13657);fill-opacity:1;fill-rule:evenodd;stroke:#ffd24a;stroke-width:0.56267327;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
style="fill:url(#linearGradient13657);fill-opacity:1.0;fill-rule:evenodd;stroke:#ffd24a;stroke-width:0.56267327000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 178.35631,433.59705 L 179.36031,444.45935 L 168.5839,444.98867 L 171.58005,441.40948 L 161.45214,432.84952 L 165.34516,428.28456 L 175.4677,436.95565 L 178.35631,433.59705 z "
id="path12740"
sodipodi:nodetypes="cccccccc"
@ -248,5 +321,45 @@
inkscape:export-filename="/home/yana/workspace/sip-communicator-1-0-draft/src/net/java/sip/communicator/impl/gui/resources/common/incomingCall.png"
inkscape:export-xdpi="90.000000"
inkscape:export-ydpi="90.000000" />
<rect
style="opacity:0.98999999000000005;fill:#bf0913;fill-opacity:1;stroke:none"
id="rect12736-4"
width="32"
height="32"
x="190.7657"
y="420.9881"
rx="6.9999971"
ry="6.9999971"
inkscape:export-filename="/Users/yanastamcheva/workspace/filter/resources/images/impl/gui/common/missedCall.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<rect
style="opacity:0.98999999;fill:url(#linearGradient13660-4);fill-opacity:1;stroke:none"
id="rect12738-1"
width="28.826275"
height="28.826275"
x="192.63022"
y="423.05389"
rx="6.9999971"
ry="6.9999971"
inkscape:export-filename="/Users/yanastamcheva/workspace/filter/resources/images/impl/gui/common/missedCall.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90" />
<path
style="fill:url(#linearGradient7442);fill-opacity:1;fill-rule:evenodd;stroke:#bf0913;stroke-width:0.56267327000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.98823529000000021;stroke-dasharray:none"
d="m 214.30722,433.72459 1.004,10.8623 -10.77641,0.52932 2.99615,-3.57919 -10.12791,-8.55996 3.89302,-4.56496 10.12254,8.67109 2.88861,-3.3586 z"
id="path12740-9"
sodipodi:nodetypes="cccccccc"
inkscape:export-filename="/Users/yanastamcheva/workspace/filter/resources/images/impl/gui/common/missedCall.png"
inkscape:export-xdpi="90.000000"
inkscape:export-ydpi="90.000000" />
<path
style="fill:url(#linearGradient13654-5);fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 212.58917,436.17039 0.62704,7.68783 -7.61253,0.45614 2.26839,-2.93444 -9.00383,-7.57054 2.99174,-3.73964 9.17761,7.8267 1.55158,-1.72605 z"
id="path12742-0"
sodipodi:nodetypes="cccccccc"
inkscape:export-filename="/Users/yanastamcheva/workspace/filter/resources/images/impl/gui/common/missedCall.png"
inkscape:export-xdpi="90.000000"
inkscape:export-ydpi="90.000000" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 13 KiB

@ -75,6 +75,7 @@ service.gui.BROWSE=Browse
service.gui.BUSY_MESSAGE=Sorry, I'm busy right now.
service.gui.CALL=Call
service.gui.CALL_CONTACT=Call contact
service.gui.CALL_HISTORY_TOOL_TIP=Click on the button to show call history
service.gui.CALL_VIA=Call via:
service.gui.CALL_NOT_SUPPORTING_PARTICIPANT=This call only supports participants from the {0} network and your {1} account. {2} does not contain any address for this network or account.
service.gui.CANCEL=&Cancel
@ -136,7 +137,7 @@ service.gui.DIALPAD=Dialpad
service.gui.DND_STATUS=Do not disturb
service.gui.DO_NOT_ASK_AGAIN=Don't ask again
service.gui.DO_NOT_SHOW_AGAIN=Don't show this message again
service.gui.DURATION=Duration
service.gui.DURATION=duration
service.gui.EDIT=&Edit
service.gui.EMPTY_HISTORY=&Empty history
service.gui.ENABLE_TYPING_NOTIFICATIONS=Enable &typing notifications
@ -240,6 +241,7 @@ service.gui.LOGIN_WINDOW_TITLE=Login {0}
service.gui.LOGOFF_NOT_SUCCEEDED=An error occured while logging off with the following account: User name: {0}, Server name: {1}.
service.gui.ME=me
service.gui.MEMBER=member
service.gui.MISSED_CALLS_TOOL_TIP=Click on the button to see your missed calls
service.gui.MODERATOR=moderator
service.gui.MORE=See more
service.gui.MOVE=Move
@ -356,6 +358,7 @@ service.gui.SET_GLOBAL_STATUS=Set global status
service.gui.SET_STATUS_MESSAGE=Set status message
service.gui.SETTINGS=&Options
service.gui.SHOW=Show
service.gui.SHOW_CONTACT_LIST_TOOL_TIP=Click on this button to switch off the history view and show your contact list.
service.gui.SHOW_OFFLINE_CONTACTS=Show offline contacts
service.gui.SIGN_IN=Sign in
service.gui.SMS_SUCCESSFULLY_SENT=SMS message successfully sent!

@ -6,15 +6,22 @@
*/
package net.java.sip.communicator.impl.callhistory;
import java.util.*;
import org.osgi.framework.*;
import net.java.sip.communicator.service.history.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.service.callhistory.*;
import net.java.sip.communicator.service.contactsource.*;
/**
* Activates the CallHistoryService
* Activates the <tt>CallHistoryService</tt>.
*
* @author Damian Minkov
* @author Yana Stamcheva
*/
public class CallHistoryActivator
implements BundleActivator
@ -22,18 +29,39 @@ public class CallHistoryActivator
private static Logger logger =
Logger.getLogger(CallHistoryActivator.class);
private CallHistoryServiceImpl callHistoryService = null;
/**
* The bundle context.
*/
public static BundleContext bundleContext;
/**
* The <tt>CallHistoryServiceImpl</tt> instantiated in the start method
* of this bundle.
*/
private static CallHistoryServiceImpl callHistoryService = null;
/**
* The service responsible for resources.
*/
private static ResourceManagementService resourcesService;
/**
* The map containing all registered
*/
private static final Map<Object, ProtocolProviderFactory>
providerFactoriesMap = new Hashtable<Object, ProtocolProviderFactory>();
/**
* Initialize and start call history
*
* @param bundleContext BundleContext
* @param bc the <tt>BundleContext</tt>
* @throws Exception
*/
public void start(BundleContext bundleContext) throws Exception
public void start(BundleContext bc) throws Exception
{
try{
bundleContext = bc;
try{
logger.logEntry();
ServiceReference refHistory = bundleContext.getServiceReference(
@ -53,6 +81,10 @@ public void start(BundleContext bundleContext) throws Exception
bundleContext.registerService(
CallHistoryService.class.getName(), callHistoryService, null);
bundleContext.registerService(
ContactSourceService.class.getName(),
new CallHistoryContactSource(), null);
logger.info("Call History Service ...[REGISTERED]");
}
finally
@ -62,9 +94,89 @@ public void start(BundleContext bundleContext) throws Exception
}
/**
* Stops this bundle.
* @param bundleContext the <tt>BundleContext</tt>
* @throws Exception if the stop operation goes wrong
*/
public void stop(BundleContext bundleContext) throws Exception
{
if(callHistoryService != null)
callHistoryService.stop(bundleContext);
}
/**
* Returns the instance of <tt>CallHistoryService</tt> created in this
* activator.
* @return the instance of <tt>CallHistoryService</tt> created in this
* activator
*/
public static CallHistoryService getCallHistoryService()
{
return callHistoryService;
}
/**
* Returns the <tt>ResourceManagementService</tt>, through which we will
* access all resources.
*
* @return the <tt>ResourceManagementService</tt>, through which we will
* access all resources.
*/
public static ResourceManagementService getResources()
{
if (resourcesService == null)
{
ServiceReference serviceReference = bundleContext
.getServiceReference(ResourceManagementService.class.getName());
if(serviceReference == null)
return null;
resourcesService = (ResourceManagementService) bundleContext
.getService(serviceReference);
}
return resourcesService;
}
/**
* Returns all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
* context.
*
* @return all <tt>ProtocolProviderFactory</tt>s obtained from the bundle
* context
*/
public static Map<Object, ProtocolProviderFactory>
getProtocolProviderFactories()
{
ServiceReference[] serRefs = null;
try
{
// get all registered provider factories
serRefs =
bundleContext.getServiceReferences(
ProtocolProviderFactory.class.getName(), null);
}
catch (InvalidSyntaxException e)
{
logger.error("LoginManager : " + e);
}
if (serRefs != null)
{
for (int i = 0; i < serRefs.length; i++)
{
ProtocolProviderFactory providerFactory
= (ProtocolProviderFactory) bundleContext
.getService(serRefs[i]);
providerFactoriesMap.put(serRefs[i]
.getProperty(ProtocolProviderFactory.PROTOCOL),
providerFactory);
}
}
return providerFactoriesMap;
}
}

@ -0,0 +1,153 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.callhistory;
import java.util.*;
import net.java.sip.communicator.service.callhistory.*;
import net.java.sip.communicator.service.contactsource.*;
/**
* The <tt>CallHistoryContactSource</tt> is the contact source for the call
* history.
*
* @author Yana Stamcheva
*/
public class CallHistoryContactSource implements ContactSourceService
{
/**
* The display name of this contact source.
*/
private static final String CALL_HISTORY_NAME = "Call history";
/**
* Returns the display name of this contact source.
* @return the display name of this contact source
*/
public String getDisplayName()
{
return CALL_HISTORY_NAME;
}
/**
* Queries this contact source for the given <tt>searchString</tt>.
* @param queryString the string to search for
* @return the created query
*/
public ContactQuery queryContactSource(String queryString)
{
if (queryString != null && queryString.length() > 0)
return new CallHistoryQuery(
CallHistoryActivator.getCallHistoryService()
.findByPeer(queryString));
else
return new CallHistoryQuery(
CallHistoryActivator.getCallHistoryService()
.findLast(50));
}
/**
* The <tt>CallHistoryQuery</tt> contains information about a current query
* to the contact source.
*/
private class CallHistoryQuery implements ContactQuery
{
/**
* A list of all registered query listeners.
*/
private final List<ContactQueryListener> queryListeners
= new LinkedList<ContactQueryListener>();
/**
* A list of all source contact results.
*/
private final List<SourceContact> sourceContacts
= new LinkedList<SourceContact>();
/**
* Creates a <tt>CallHistoryQuery</tt>.
* @param callRecords a collection of the result call records
*/
public CallHistoryQuery(Collection<CallRecord> callRecords)
{
Iterator<CallRecord> recordsIter = callRecords.iterator();
while (recordsIter.hasNext())
{
sourceContacts.add(
new CallHistorySourceContact(
CallHistoryContactSource.this,
recordsIter.next()));
}
}
/**
* Adds the given <tt>ContactQueryListener</tt> to the list of query
* listeners.
* @param l the <tt>ContactQueryListener</tt> to add
*/
public void addContactQueryListener(ContactQueryListener l)
{
synchronized (queryListeners)
{
queryListeners.add(l);
}
}
/**
* This query could not be canceled.
*/
public void cancel()
{
}
/**
* Removes the given <tt>ContactQueryListener</tt> from the list of
* query listeners.
* @param l the <tt>ContactQueryListener</tt> to remove
*/
public void removeContactQueryListener(ContactQueryListener l)
{
synchronized (queryListeners)
{
queryListeners.remove(l);
}
}
/**
* Returns a list containing the results of this query.
* @return a list containing the results of this query
*/
public List<SourceContact> getQueryResults()
{
return sourceContacts;
}
/**
* Returns the <tt>ContactSourceService</tt>, where this query was first
* initiated.
* @return the <tt>ContactSourceService</tt>, where this query was first
* initiated
*/
public ContactSourceService getContactSource()
{
return CallHistoryContactSource.this;
}
}
/**
* Returns the identifier of this contact source. Some of the common
* identifiers are defined here (For example the CALL_HISTORY identifier
* should be returned by all call history implementations of this interface)
* @return the identifier of this contact source
*/
public String getIdentifier()
{
return CALL_HISTORY;
}
}

@ -42,8 +42,9 @@ public class CallHistoryServiceImpl
Logger.getLogger(CallHistoryServiceImpl.class);
private static String[] STRUCTURE_NAMES =
new String[] { "callStart", "callEnd", "dir", "callParticipantIDs",
"callParticipantStart", "callParticipantEnd","callParticipantStates" };
new String[] { "accountUID", "callStart", "callEnd", "dir",
"callParticipantIDs", "callParticipantStart",
"callParticipantEnd", "callParticipantStates" };
private static HistoryRecordStructure recordStructure =
new HistoryRecordStructure(STRUCTURE_NAMES);
@ -59,8 +60,10 @@ public class CallHistoryServiceImpl
private Object syncRoot_HistoryService = new Object();
private final Map<CallHistorySearchProgressListener, SearchProgressWrapper> progressListeners =
new Hashtable<CallHistorySearchProgressListener, SearchProgressWrapper>();
private final Map<CallHistorySearchProgressListener, SearchProgressWrapper>
progressListeners
= new Hashtable<CallHistorySearchProgressListener,
SearchProgressWrapper>();
private final List<CallRecordImpl> currentCallRecords =
new Vector<CallRecordImpl>();
@ -68,22 +71,29 @@ public class CallHistoryServiceImpl
private final CallChangeListener historyCallChangeListener =
new HistoryCallChangeListener();
private HistoryReader historyReader;
/**
* Returns the underlying history service.
* @return the underlying history service
*/
public HistoryService getHistoryService()
{
return historyService;
}
/**
* Returns all the calls made by all the contacts
* in the supplied metacontact after the given date
* Returns all the calls made by all the contacts in the supplied
* <tt>contact</tt> after the given date.
*
* @param contact MetaContact which contacts participate in
* the returned calls
* @param startDate Date the start date of the calls
* @return Collection of CallRecords with CallPeerRecord
* @return the <tt>CallHistoryQuery</tt>, corresponding to this find
* @throws RuntimeException
*/
public Collection<CallRecord> findByStartDate(MetaContact contact, Date startDate)
public Collection<CallRecord> findByStartDate(
MetaContact contact, Date startDate)
throws RuntimeException
{
throw new UnsupportedOperationException("Not implemented yet!");
@ -93,7 +103,7 @@ public Collection<CallRecord> findByStartDate(MetaContact contact, Date startDat
* Returns all the calls made after the given date
*
* @param startDate Date the start date of the calls
* @return Collection of CallRecords with CallPeerRecord
* @return the <tt>CallHistoryQuery</tt>, corresponding to this find
* @throws RuntimeException
*/
public Collection<CallRecord> findByStartDate(Date startDate)
@ -104,16 +114,16 @@ public Collection<CallRecord> findByStartDate(Date startDate)
{
// the default ones
History history = this.getHistory(null, null);
HistoryReader reader = history.getReader();
addHistorySearchProgressListeners(reader, 1);
historyReader = history.getReader();
addHistorySearchProgressListeners(historyReader, 1);
QueryResultSet<HistoryRecord> rs
= reader.findByStartDate(startDate);
= historyReader.findByStartDate(startDate);
while (rs.hasNext())
{
HistoryRecord hr = rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
removeHistorySearchProgressListeners(reader);
removeHistorySearchProgressListeners(historyReader);
}
catch (IOException ex)
{
@ -133,7 +143,8 @@ public Collection<CallRecord> findByStartDate(Date startDate)
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
public Collection<CallRecord> findByEndDate(MetaContact contact, Date endDate)
public Collection<CallRecord> findByEndDate(MetaContact contact,
Date endDate)
throws RuntimeException
{
throw new UnsupportedOperationException("Not implemented yet!");
@ -146,22 +157,25 @@ public Collection<CallRecord> findByEndDate(MetaContact contact, Date endDate)
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
public Collection<CallRecord> findByEndDate(Date endDate) throws RuntimeException
public Collection<CallRecord> findByEndDate(Date endDate)
throws RuntimeException
{
TreeSet<CallRecord> result = new TreeSet<CallRecord>(new CallRecordComparator());
TreeSet<CallRecord> result
= new TreeSet<CallRecord>(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
HistoryReader reader = history.getReader();
addHistorySearchProgressListeners(reader, 1);
QueryResultSet<HistoryRecord> rs = reader.findByEndDate(endDate);
historyReader = history.getReader();
addHistorySearchProgressListeners(historyReader, 1);
QueryResultSet<HistoryRecord> rs
= historyReader.findByEndDate(endDate);
while (rs.hasNext())
{
HistoryRecord hr = rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
removeHistorySearchProgressListeners(reader);
removeHistorySearchProgressListeners(historyReader);
}
catch (IOException ex)
{
@ -181,7 +195,8 @@ public Collection<CallRecord> findByEndDate(Date endDate) throws RuntimeExceptio
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
public Collection<CallRecord> findByPeriod(MetaContact contact, Date startDate, Date endDate)
public Collection<CallRecord> findByPeriod(MetaContact contact,
Date startDate, Date endDate)
throws RuntimeException
{
throw new UnsupportedOperationException("Not implemented yet!");
@ -195,24 +210,25 @@ public Collection<CallRecord> findByPeriod(MetaContact contact, Date startDate,
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
public Collection<CallRecord> findByPeriod(Date startDate, Date endDate) throws
RuntimeException
public Collection<CallRecord> findByPeriod(Date startDate, Date endDate)
throws RuntimeException
{
TreeSet<CallRecord> result = new TreeSet<CallRecord>(new CallRecordComparator());
TreeSet<CallRecord> result
= new TreeSet<CallRecord>(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
HistoryReader reader = history.getReader();
addHistorySearchProgressListeners(reader, 1);
historyReader = history.getReader();
addHistorySearchProgressListeners(historyReader, 1);
QueryResultSet<HistoryRecord> rs
= reader.findByPeriod(startDate, endDate);
= historyReader.findByPeriod(startDate, endDate);
while (rs.hasNext())
{
HistoryRecord hr = rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
removeHistorySearchProgressListeners(reader);
removeHistorySearchProgressListeners(historyReader);
}
catch (IOException ex)
{
@ -247,13 +263,14 @@ public Collection<CallRecord> findLast(MetaContact contact, int count)
*/
public Collection<CallRecord> findLast(int count) throws RuntimeException
{
TreeSet<CallRecord> result = new TreeSet<CallRecord>(new CallRecordComparator());
TreeSet<CallRecord> result
= new TreeSet<CallRecord>(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
QueryResultSet<HistoryRecord> rs
= history.getReader().findLast(count);
historyReader = history.getReader();
QueryResultSet<HistoryRecord> rs = historyReader.findLast(count);
while (rs.hasNext())
{
HistoryRecord hr = rs.next();
@ -277,21 +294,22 @@ public Collection<CallRecord> findLast(int count) throws RuntimeException
public Collection<CallRecord> findByPeer(String address)
throws RuntimeException
{
TreeSet<CallRecord> result = new TreeSet<CallRecord>(new CallRecordComparator());
TreeSet<CallRecord> result
= new TreeSet<CallRecord>(new CallRecordComparator());
try
{
// the default ones
History history = this.getHistory(null, null);
HistoryReader reader = history.getReader();
addHistorySearchProgressListeners(reader, 1);
historyReader = history.getReader();
addHistorySearchProgressListeners(historyReader, 1);
QueryResultSet<HistoryRecord> rs
= reader.findByKeyword(address, "callParticipantIDs");
= historyReader.findByKeyword(address, "callParticipantIDs");
while (rs.hasNext())
{
HistoryRecord hr = rs.next();
result.add(convertHistoryRecordToCallRecord(hr));
}
removeHistorySearchProgressListeners(reader);
removeHistorySearchProgressListeners(historyReader);
}
catch (IOException ex)
{
@ -364,19 +382,21 @@ private CallRecord convertHistoryRecordToCallRecord(HistoryRecord hr)
String propName = hr.getPropertyNames()[i];
String value = hr.getPropertyValues()[i];
if(propName.equals(STRUCTURE_NAMES[0]))
result.setStartTime(new Date(Long.parseLong(value)));
if (propName.equals(STRUCTURE_NAMES[0]))
result.setProtocolProvider(getProtocolProvider(value));
else if(propName.equals(STRUCTURE_NAMES[1]))
result.setEndTime(new Date(Long.parseLong(value)));
result.setStartTime(new Date(Long.parseLong(value)));
else if(propName.equals(STRUCTURE_NAMES[2]))
result.setDirection(value);
result.setEndTime(new Date(Long.parseLong(value)));
else if(propName.equals(STRUCTURE_NAMES[3]))
callPeerIDs = getCSVs(value);
result.setDirection(value);
else if(propName.equals(STRUCTURE_NAMES[4]))
callPeerStart = getCSVs(value);
callPeerIDs = getCSVs(value);
else if(propName.equals(STRUCTURE_NAMES[5]))
callPeerEnd = getCSVs(value);
callPeerStart = getCSVs(value);
else if(propName.equals(STRUCTURE_NAMES[6]))
callPeerEnd = getCSVs(value);
else if(propName.equals(STRUCTURE_NAMES[7]))
callPeerStates = getStates(value);
}
@ -557,10 +577,12 @@ public void stop(BundleContext bc)
* @param source Contact
* @param destination Contact
*/
private void writeCall(CallRecord callRecord, Contact source,
Contact destination)
private void writeCall( CallRecordImpl callRecord,
Contact source,
Contact destination)
{
try {
try
{
History history = this.getHistory(source, destination);
HistoryWriter historyWriter = history.getWriter();
@ -589,6 +611,8 @@ private void writeCall(CallRecord callRecord, Contact source,
}
historyWriter.addRecord(new String[] {
callRecord.getSourceCall().getProtocolProvider()
.getAccountID().getAccountUniqueID(),
String.valueOf(callRecord.getStartTime().getTime()),
String.valueOf(callRecord.getEndTime().getTime()),
callRecord.getDirection(),
@ -596,8 +620,10 @@ private void writeCall(CallRecord callRecord, Contact source,
callPeerStartTime.toString(),
callPeerEndTime.toString(),
callPeerStates.toString()},
new Date()); // this date is when the history record is written
} catch (IOException e)
new Date()); // this date is when the history
// record is written
}
catch (IOException e)
{
logger.error("Could not add call to history", e);
}
@ -646,9 +672,11 @@ public void unsetHistoryService(HistoryService hService)
*/
public void serviceChanged(ServiceEvent serviceEvent)
{
Object sService = bundleContext.getService(serviceEvent.getServiceReference());
Object sService
= bundleContext.getService(serviceEvent.getServiceReference());
logger.trace("Received a service event for: " + sService.getClass().getName());
logger.trace("Received a service event for: "
+ sService.getClass().getName());
// we don't care if the source service is not a protocol provider
if (! (sService instanceof ProtocolProviderService))
@ -667,13 +695,12 @@ else if (serviceEvent.getType() == ServiceEvent.UNREGISTERING)
{
this.handleProviderRemoved( (ProtocolProviderService) sService);
}
}
/**
* Used to attach the Call History Service to existing or
* just registered protocol provider. Checks if the provider has implementation
* of OperationSetBasicTelephony
* just registered protocol provider. Checks if the provider has
* implementation of OperationSetBasicTelephony
*
* @param provider ProtocolProviderService
*/
@ -778,34 +805,6 @@ private void removeHistorySearchProgressListeners(HistoryReader reader)
}
}
/**
* Gets all the history readers for the contacts in the given MetaContact
*
* @param contact MetaContact
* @return Hashtable
*/
private Map<Contact, HistoryReader> getHistoryReaders(MetaContact contact)
{
Map<Contact, HistoryReader> readers =
new Hashtable<Contact, HistoryReader>();
Iterator<Contact> iter = contact.getContacts();
while (iter.hasNext())
{
Contact item = iter.next();
try
{
History history = this.getHistory(null, item);
readers.put(item, history.getReader());
}
catch (IOException e)
{
logger.error("Could not read history", e);
}
}
return readers;
}
/**
* CallListener implementation for incoming calls
* @param event CallEvent
@ -830,17 +829,8 @@ public void outgoingCallCreated(CallEvent event)
*/
public void callEnded(CallEvent event)
{
CallRecordImpl callRecord = findCallRecord(event.getSourceCall());
// no such call
if (callRecord == null)
return;
callRecord.setEndTime(new Date());
writeCall(callRecord, null, null);
currentCallRecords.remove(callRecord);
// We store the call in the callStateChangeEvent where we
// have more information on the previous state of the call.
}
/**
@ -1027,9 +1017,11 @@ public void progressChanged(ProgressEvent evt)
*/
private int getProgressMapping(int historyProgress)
{
currentProgress += (historyProgress - lastHistoryProgress)/contactCount;
currentProgress
+= (historyProgress - lastHistoryProgress)/contactCount;
if(historyProgress == HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
if(historyProgress
== HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
{
currentContactCount++;
lastHistoryProgress = 0;
@ -1074,18 +1066,93 @@ public int compare(CallRecord o1, CallRecord o2)
/**
* Receive events for adding or removing peers from a call
*/
private class HistoryCallChangeListener
extends CallChangeAdapter
private class HistoryCallChangeListener implements CallChangeListener
{
/**
* Indicates that a new call peer has joined the source call.
*
* @param evt the <tt>CallPeerEvent</tt> containing the source call
* and call peer.
*/
public void callPeerAdded(CallPeerEvent evt)
{
handlePeerAdded(evt.getSourceCallPeer());
}
/**
* Indicates that a call peer has left the source call.
*
* @param evt the <tt>CallPeerEvent</tt> containing the source call
* and call peer.
*/
public void callPeerRemoved(CallPeerEvent evt)
{
handlePeerRemoved(evt.getSourceCallPeer(),
evt.getSourceCall());
}
/**
* A dummy implementation of this listener's callStateChanged() method.
*
* @param evt the <tt>CallChangeEvent</tt> instance containing the source
* calls and its old and new state.
*/
public void callStateChanged(CallChangeEvent evt)
{
CallRecordImpl callRecord = findCallRecord(evt.getSourceCall());
// no such call
if (callRecord == null)
return;
if (evt.getNewValue().equals(CallState.CALL_ENDED)
&& evt.getOldValue().equals(CallState.CALL_INITIALIZATION))
{
callRecord.setEndTime(callRecord.getStartTime());
}
else
callRecord.setEndTime(new Date());
writeCall(callRecord, null, null);
currentCallRecords.remove(callRecord);
}
}
/**
* Returns the <tt>ProtocolProviderService</tt> corresponding to the given
* account identifier.
* @param accountUID the identifier of the account.
* @return the <tt>ProtocolProviderService</tt> corresponding to the given
* account identifier
*/
private ProtocolProviderService getProtocolProvider(String accountUID)
{
for (ProtocolProviderFactory providerFactory
: CallHistoryActivator.getProtocolProviderFactories().values())
{
ServiceReference serRef;
for (AccountID accountID : providerFactory.getRegisteredAccounts())
{
if (accountID.getAccountUniqueID().equals(accountUID))
{
serRef = providerFactory.getProviderForAccount(accountID);
return (ProtocolProviderService) CallHistoryActivator
.bundleContext.getService(serRef);
}
}
}
return null;
}
/**
* Cancels the current find. If there's no find going on, then does nothing.
*/
public void cancelCurrentFind()
{
if (historyReader != null)
historyReader.cancelCurrentFind();
}
}

@ -0,0 +1,266 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.callhistory;
import java.util.*;
import net.java.sip.communicator.service.callhistory.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
/**
* The <tt>CallHistorySourceContact</tt> is an implementation of the
* <tt>SourceContact</tt> interface based on a <tt>CallRecord</tt>.
*
* @author Yana Stamcheva
*/
public class CallHistorySourceContact implements SourceContact
{
/**
* The parent <tt>CallHistoryContactSource</tt>, where this contact is
* contained.
*/
private final CallHistoryContactSource contactSource;
/**
* The corresponding call record.
*/
private final CallRecord callRecord;
/**
* The incoming call icon.
*/
private static final byte[] incomingIcon
= CallHistoryActivator.getResources()
.getImageInBytes("service.gui.icons.INCOMING_CALL");
/**
* The outgoing call icon.
*/
private static byte[] outgoingIcon
= CallHistoryActivator.getResources()
.getImageInBytes("service.gui.icons.OUTGOING_CALL");
/**
* The missed call icon.
*/
private static byte[] missedCallIcon
= CallHistoryActivator.getResources()
.getImageInBytes("service.gui.icons.MISSED_CALL");
/**
* A list of all contact details.
*/
private final List<ContactDetail> contactDetails
= new LinkedList<ContactDetail>();
/**
* The display name of this contact.
*/
private String displayName = "";
/**
* The display details of this contact.
*/
private final String displayDetails;
/**
* Creates an instance of <tt>CallHistorySourceContact</tt>
* @param contactSource
* @param callRecord
*/
public CallHistorySourceContact(CallHistoryContactSource contactSource,
CallRecord callRecord)
{
this.contactSource = contactSource;
this.callRecord = callRecord;
this.initPeerDetails();
this.displayDetails
= CallHistoryActivator.getResources()
.getI18NString("service.gui.AT") + ": "
+ getDateString(callRecord.getStartTime().getTime())
+ " " + CallHistoryActivator.getResources()
.getI18NString("service.gui.DURATION") + ": "
+ GuiUtils.formatTime(
GuiUtils.substractDates(
callRecord.getEndTime(), callRecord.getStartTime()));
}
/**
* Initializes peer details.
*/
private void initPeerDetails()
{
Iterator<CallPeerRecord> recordsIter
= callRecord.getPeerRecords().iterator();
while (recordsIter.hasNext())
{
String peerAddress = recordsIter.next().getPeerAddress();
if (displayName.length() > 0)
displayName += "," + peerAddress;
else
displayName += peerAddress;
if (peerAddress != null)
{
ContactDetail contactDetail = new ContactDetail(peerAddress);
Map<Class<? extends OperationSet>, ProtocolProviderService>
preferredProviders = null;
if (callRecord.getProtocolProvider() != null)
{
preferredProviders
= new Hashtable<Class<? extends OperationSet>,
ProtocolProviderService>();
preferredProviders.put( OperationSetBasicTelephony.class,
callRecord.getProtocolProvider());
contactDetail
.setPreferredProviders(preferredProviders);
}
// Set supported operation sets.
LinkedList<Class<? extends OperationSet>> supportedOpSets
= new LinkedList<Class<? extends OperationSet>>();
supportedOpSets.add(OperationSetBasicTelephony.class);
contactDetail.setSupportedOpSets(supportedOpSets);
contactDetails.add(contactDetail);
}
}
}
/**
* Returns a list of available contact details.
* @return a list of available contact details
*/
public List<ContactDetail> getContactDetails()
{
return new LinkedList<ContactDetail>(contactDetails);
}
/**
* Returns the parent <tt>ContactSourceService</tt> from which this contact
* came from.
* @return the parent <tt>ContactSourceService</tt> from which this contact
* came from
*/
public ContactSourceService getContactSource()
{
return contactSource;
}
/**
* Returns the display details of this search contact. This could be any
* important information that should be shown to the user.
*
* @return the display details of the search contact
*/
public String getDisplayDetails()
{
return displayDetails;
}
/**
* Returns the display name of this search contact. This is a user-friendly
* name that could be shown in the user interface.
*
* @return the display name of this search contact
*/
public String getDisplayName()
{
return displayName;
}
/**
* An image (or avatar) corresponding to this search contact. If such is
* not available this method will return null.
*
* @return the byte array of the image or null if no image is available
*/
public byte[] getImage()
{
if (callRecord.getDirection().equals(CallRecord.IN))
{
if (callRecord.getStartTime().equals(callRecord.getEndTime()))
return missedCallIcon;
else
return incomingIcon;
}
else if (callRecord.getDirection().equals(CallRecord.OUT))
return outgoingIcon;
return null;
}
/**
* Returns a list of all <tt>ContactDetail</tt>s supporting the given
* <tt>OperationSet</tt> class.
* @param operationSet the <tt>OperationSet</tt> class we're looking for
* @return a list of all <tt>ContactDetail</tt>s supporting the given
* <tt>OperationSet</tt> class
*/
public List<ContactDetail> getContactDetails(
Class<? extends OperationSet> operationSet)
{
// We support only call details.
if (!operationSet.equals(OperationSetBasicTelephony.class))
return null;
return new LinkedList<ContactDetail>(contactDetails);
}
/**
* Returns the preferred <tt>ContactDetail</tt> for a given
* <tt>OperationSet</tt> class.
* @param operationSet the <tt>OperationSet</tt> class, for which we would
* like to obtain a <tt>ContactDetail</tt>
* @return the preferred <tt>ContactDetail</tt> for a given
* <tt>OperationSet</tt> class
*/
public ContactDetail getPreferredContactDetail(
Class<? extends OperationSet> operationSet)
{
// We support only call details.
if (!operationSet.equals(OperationSetBasicTelephony.class))
return null;
return contactDetails.get(0);
}
/**
* Returns the date string to show for the given date.
*
* @param date the date to format
* @return the date string to show for the given date
*/
public static String getDateString(long date)
{
String time = GuiUtils.formatTime(date);
// If the current date we don't go in there and we'll return just the
// time.
if (GuiUtils.compareDatesOnly(date, System.currentTimeMillis()) < 0)
{
StringBuffer dateStrBuf = new StringBuffer();
GuiUtils.formatDate(date, dateStrBuf);
dateStrBuf.append(" ");
dateStrBuf.append(time);
return dateStrBuf.toString();
}
return time;
}
}

@ -96,4 +96,13 @@ public void setDirection(String direction)
{
this.direction = direction;
}
/**
* Sets the given <tt>ProtocolProviderService</tt> used for the call.
* @param pps the <tt>ProtocolProviderService</tt> to set
*/
public void setProtocolProvider(ProtocolProviderService pps)
{
this.protocolProvider = pps;
}
}

@ -14,6 +14,9 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.icqconstants,
net.java.sip.communicator.service.protocol.event,
net.java.sip.communicator.service.contactsource,
net.java.sip.communicator.service.resources
Export-Package: net.java.sip.communicator.service.callhistory,
net.java.sip.communicator.service.callhistory.event
Metadata-Location: /net/java/sip/communicator/impl/msghistory/callhistory.metadata.xml
Metadata-Location: net.java.sip.communicator.impl.msghistory,
callhistory.metadata.xml

@ -67,6 +67,22 @@ public class MetaContactGroupImpl
private final MetaContactListServiceImpl mclServiceImpl;
/**
* The user-specific key-value associations stored in this instance.
* <p>
* Like the Widget implementation of Eclipse SWT, the storage type takes
* into account that there are likely to be many
* <code>MetaContactGroupImpl</code> instances and <code>Map</code>s are
* thus likely to impose increased memory use. While an array may very well
* perform worse than a <code>Map</code> with respect to search, the
* mechanism of user-defined key-value associations explicitly states that
* it is not guaranteed to be optimized for any particular use and only
* covers the most basic cases and performance-savvy code will likely
* implement a more optimized solution anyway.
* </p>
*/
private Object[] data;
/**
* Creates an instance of the root meta contact group.
*
@ -85,6 +101,8 @@ public class MetaContactGroupImpl
* specified meta contact uid. This constructor MUST NOT be used for nothing
* purposes else but restoring contacts extracted from the contactlist.xml
*
* @param mclServiceImpl the implementation of the
* <tt>MetaContactListService</tt>, to which this group belongs
* @param groupName the name of the group to create
* @param metaUID a UID that has been stored earlier or null when a new
* UID needs to be created.
@ -932,8 +950,98 @@ boolean removeSubgroup(MetaContactGroup group)
return subgroups.remove(group);
}
/**
* Returns the implementation of the <tt>MetaContactListService</tt>, to
* which this group belongs.
* @return the implementation of the <tt>MetaContactListService</tt>
*/
final MetaContactListServiceImpl getMclServiceImpl()
{
return mclServiceImpl;
}
/**
* Implements {@link MetaContactGroup#getData(Object)}.
* @return the data value corresponding to the given key
*/
public Object getData(Object key)
{
if (key == null)
throw new NullPointerException("key");
int index = dataIndexOf(key);
return (index == -1) ? null : data[index + 1];
}
/**
* Implements {@link MetaContactGroup#setData(Object, Object)}.
* @param key the of the data
* @param value the value of the data
*/
public void setData(Object key, Object value)
{
if (key == null)
throw new NullPointerException("key");
int index = dataIndexOf(key);
if (index == -1)
{
/*
* If value is null, remove the association with key (or just don't
* add it).
*/
if (data == null)
if (value != null)
data = new Object[] { key, value };
else if (value == null)
{
int length = data.length - 2;
if (length > 0)
{
Object[] newData = new Object[length];
System.arraycopy(data, 0, newData, 0, index);
System.arraycopy(
data, index + 2, newData, index, length - index);
data = newData;
}
else
data = null;
}
else
{
int length = data.length;
Object[] newData = new Object[length + 2];
System.arraycopy(data, 0, newData, 0, length);
data[length++] = key;
data[length++] = value;
data = newData;
}
}
else
data[index + 1] = value;
}
/**
* Determines the index in <code>#data</code> of a specific key.
*
* @param key
* the key to retrieve the index in <code>#data</code> of
* @return the index in <code>#data</code> of the specified <code>key</code>
* if it is contained; <tt>-1</tt> if <code>key</code> is not
* contained in <code>#data</code>
*/
private int dataIndexOf(Object key)
{
if (data != null)
for (int index = 0; index < data.length; index += 2)
if (key.equals(data[index]))
return index;
return -1;
}
}

@ -16,6 +16,7 @@
import net.java.sip.communicator.service.callhistory.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.desktop.*;
import net.java.sip.communicator.service.fileaccess.*;
import net.java.sip.communicator.service.gui.*;
@ -71,6 +72,8 @@ public class GuiActivator implements BundleActivator
private static MediaService mediaService;
private static List<ContactSourceService> contactSources;
private static final Map<Object, ProtocolProviderFactory>
providerFactoriesMap = new Hashtable<Object, ProtocolProviderFactory>();
@ -548,6 +551,44 @@ public void serviceChanged(ServiceEvent event)
}
}
/**
* Returns a list of all registered contact sources.
* @return a list of all registered contact sources
*/
public static List<ContactSourceService> getContactSources()
{
if (contactSources != null)
return contactSources;
contactSources = new Vector<ContactSourceService>();
ServiceReference[] serRefs = null;
try
{
// get all registered provider factories
serRefs =
bundleContext.getServiceReferences(
ContactSourceService.class.getName(), null);
}
catch (InvalidSyntaxException e)
{
logger.error("GuiActivator : " + e);
}
if (serRefs != null)
{
for (int i = 0; i < serRefs.length; i++)
{
ContactSourceService contactSource
= (ContactSourceService) bundleContext
.getService(serRefs[i]);
contactSources.add(contactSource);
}
}
return contactSources;
}
/**
* Sets the <tt>contactList</tt> component currently used to show the
* contact list.

@ -39,7 +39,7 @@
import org.osgi.framework.*;
/**
* The main application window. This class is the core of this ui
* The main application window. This class is the core of this UI
* implementation. It stores all available protocol providers and their
* operation sets, as well as all registered accounts, the
* <tt>MetaContactListService</tt> and all sent messages that aren't
@ -166,10 +166,20 @@ public MainFrame()
*/
this.addWindowListener(new WindowAdapter()
{
/**
* Invoked when a window has been closed.
*/
public void windowClosed(WindowEvent event)
{
MainFrame.this.windowClosed(event);
}
/**
* Invoked when a window has been opened.
*/
public void windowOpened(WindowEvent e)
{
GuiActivator.getContactList().requestFocusInWindow();
}
});
this.initTitleFont();
@ -223,9 +233,11 @@ private void init()
northPanel.add(accountStatusPanel, BorderLayout.CENTER);
TransparentPanel searchPanel = new TransparentPanel(new BorderLayout());
TransparentPanel searchPanel
= new TransparentPanel(new BorderLayout(2, 0));
searchPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
searchPanel.add(searchField);
searchPanel.add(new CallHistoryButton(), BorderLayout.EAST);
centerPanel.add(searchPanel, BorderLayout.NORTH);
centerPanel.add(contactListPanel, BorderLayout.CENTER);
@ -359,7 +371,7 @@ public void addProtocolSupportedOperationSets(
presence.addProviderPresenceStatusListener(
new GUIProviderPresenceStatusListener());
presence.addContactPresenceStatusListener(
contactListPanel.getContactList());
TreeContactList.presenceFilter);
}
// Obtain the basic instant messaging operation set.

@ -0,0 +1,177 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.call;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.util.swing.*;
/**
* The <tt>CallHistoryButton</tt> is the button shown on the top of the contact
* list.
* @author Yana Stamcheva
*/
public class CallHistoryButton
extends SIPCommTextButton
implements MissedCallsListener
{
/**
* The history icon.
*/
private final Image historyImage
= ImageLoader.getImage(ImageLoader.CALL_HISTORY_BUTTON);
/**
* The history pressed icon.
*/
private final Image pressedImage
= ImageLoader.getImage(ImageLoader.CALL_HISTORY_BUTTON_PRESSED);
/**
* Indicates if the history is visible.
*/
private boolean isHistoryVisible = false;
/**
* Indicates if this button currently shows the number of missed calls or
* the just the history icon.
*/
private boolean isMissedCallView = false;
/**
* The tool tip shown when there are missed calls.
*/
private final static String missedCallsToolTip
= GuiActivator.getResources().getI18NString(
"service.gui.MISSED_CALLS_TOOL_TIP");
/**
* The tool tip shown by default over the history button.
*/
private final static String callHistoryToolTip
= GuiActivator.getResources().getI18NString(
"service.gui.CALL_HISTORY_TOOL_TIP");
/**
* The tool tip shown when we're in history view.
*/
private final static String showContactListToolTip
= GuiActivator.getResources().getI18NString(
"service.gui.SHOW_CONTACT_LIST_TOOL_TIP");
/**
* Creates a <tt>CallHistoryButton</tt>.
*/
public CallHistoryButton()
{
super("");
this.setBgImage(historyImage);
CallManager.setMissedCallsListener(this);
this.setPreferredSize(new Dimension(29, 22));
this.setForeground(Color.WHITE);
this.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
this.setFont(getFont().deriveFont(Font.BOLD, 10f));
this.setToolTipText(callHistoryToolTip);
this.setBackground(new Color(255, 255, 255, 160));
this.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if (isHistoryVisible && !isMissedCallView)
{
TreeContactList.searchFilter
.setSearchSourceType(SearchFilter.DEFAULT_SOURCE);
GuiActivator.getContactList()
.setDefaultFilter(TreeContactList.presenceFilter);
GuiActivator.getContactList().applyFilter(
TreeContactList.presenceFilter);
isHistoryVisible = false;
}
else
{
TreeContactList.searchFilter
.setSearchSourceType(SearchFilter.HISTORY_SOURCE);
GuiActivator.getContactList()
.setDefaultFilter(TreeContactList.historyFilter);
GuiActivator.getContactList()
.applyFilter(TreeContactList.historyFilter);
CallManager.clearMissedCalls();
isHistoryVisible = true;
}
setHistoryView();
GuiActivator.getContactList().requestFocusInWindow();
repaint();
}
});
}
/**
* Indicates that missed calls count has changed.
* @param newCallCount the new call count
*/
public void missedCallCountChanged(int newCallCount)
{
if (newCallCount > 0)
{
setMissedCallsView(newCallCount);
}
else if (newCallCount <= 0)
{
setHistoryView();
}
this.revalidate();
this.repaint();
}
/**
* Sets the history view.
*/
private void setHistoryView()
{
isMissedCallView = false;
if (isHistoryVisible)
{
setBgImage(pressedImage);
setToolTipText(showContactListToolTip);
}
else
{
setBgImage(historyImage);
setToolTipText(callHistoryToolTip);
}
setText("");
}
/**
* Sets the missed calls view of this button.
* @param callCount the missed calls count
*/
private void setMissedCallsView(int callCount)
{
isMissedCallView = true;
this.setBgImage(null);
this.setToolTipText(missedCallsToolTip);
this.setBackground(new Color(200, 0, 0));
this.setText(new Integer(callCount).toString());
}
}

@ -39,6 +39,17 @@ public class CallManager
private static Hashtable<Call, CallDialog> activeCalls
= new Hashtable<Call, CallDialog>();
/**
* Indicates the number of missed calls that the user should be notified
* about.
*/
private static int missedCalls = 0;
/**
* Listener notified for changes in missed calls count.
*/
private static MissedCallsListener missedCallsListener;
/**
* A call listener.
*/
@ -214,7 +225,7 @@ public static void createCall(String contact)
ProtocolProviderService telProvider = null;
int status = 0;
Vector<ProtocolProviderService> telProviders = getTelephonyProviders();
List<ProtocolProviderService> telProviders = getTelephonyProviders();
for (ProtocolProviderService provider : telProviders)
{
@ -398,10 +409,10 @@ public static CallDialog openCallDialog(Call call)
* Returns a list of all currently registered telephony providers.
* @return a list of all currently registered telephony providers
*/
public static Vector<ProtocolProviderService> getTelephonyProviders()
public static List<ProtocolProviderService> getTelephonyProviders()
{
Vector<ProtocolProviderService> telephonyProviders
= new Vector<ProtocolProviderService>();
List<ProtocolProviderService> telephonyProviders
= new LinkedList<ProtocolProviderService>();
for (ProtocolProviderFactory providerFactory : GuiActivator
.getProtocolProviderFactories().values())
@ -428,6 +439,44 @@ public static Vector<ProtocolProviderService> getTelephonyProviders()
return telephonyProviders;
}
/**
* Sets the given <tt>MissedCallsListener</tt> that would be notified on
* any changes in missed calls count.
* @param l the listener to set
*/
public static void setMissedCallsListener(MissedCallsListener l)
{
missedCallsListener = l;
}
/**
* Adds a missed call.
*/
public static void addMissedCall()
{
missedCalls ++;
fireMissedCallCountChangeEvent(missedCalls);
}
/**
* Clears the count of missed calls. Sets it to 0.
*/
public static void clearMissedCalls()
{
missedCalls = 0;
}
/**
* Notifies interested <tt>MissedCallListener</tt> that the count has
* changed.
* @param missedCallsCount the new missed calls count
*/
private static void fireMissedCallCountChangeEvent(int missedCallsCount)
{
if (missedCallsListener != null)
missedCallsListener.missedCallCountChanged(missedCallsCount);
}
/**
* Creates a call from a given Contact or a given String.
*/

@ -16,13 +16,13 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.customcontrols.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
/**
* A TransferHandler that we use to handle dropping of <tt>MetaContact</tt>s or
* A TransferHandler that we use to handle dropping of <tt>UIContact</tt>s or
* simple string addresses to an existing <tt>Call</tt>. Dropping of a such data
* in the <tt>CallDialog</tt> would result in the creation of a call conference.
*
@ -31,9 +31,21 @@
public class CallTransferHandler
extends ExtendedTransferHandler
{
/**
* The data flavor used when transferring <tt>UIContact</tt>s.
*/
protected static final DataFlavor uiContactDataFlavor
= new DataFlavor(UIContact.class, "UIContact");
/**
* The logger.
*/
private static final Logger logger
= Logger.getLogger(CallTransferHandler.class);
/**
* The call corresponding to the transfer.
*/
private final Call call;
/**
@ -63,7 +75,7 @@ public boolean canImport(JComponent comp, DataFlavor flavor[])
for (int i = 0, n = flavor.length; i < n; i++)
{
if (flavor[i].equals(DataFlavor.stringFlavor)
|| flavor[i].equals(metaContactDataFlavor))
|| flavor[i].equals(uiContactDataFlavor))
{
if (comp instanceof JPanel)
{
@ -89,13 +101,13 @@ public boolean canImport(JComponent comp, DataFlavor flavor[])
*/
public boolean importData(JComponent comp, Transferable t)
{
if (t.isDataFlavorSupported(metaContactDataFlavor))
if (t.isDataFlavorSupported(uiContactDataFlavor))
{
Object o = null;
try
{
o = t.getTransferData(metaContactDataFlavor);
o = t.getTransferData(uiContactDataFlavor);
}
catch (UnsupportedFlavorException e)
{
@ -110,16 +122,29 @@ public boolean importData(JComponent comp, Transferable t)
if (o instanceof ContactNode)
{
MetaContact metaContact = ((ContactNode) o).getMetaContact();
UIContact uiContact
= ((ContactNode) o).getContactDescriptor();
ProtocolProviderService callProvider
= call.getProtocolProvider();
Iterator<Contact> contacts
= metaContact.getContactsForProvider(callProvider);
Iterator<UIContactDetail> contactDetails
= uiContact.getContactDetailsForOperationSet(
OperationSetBasicTelephony.class).iterator();
String callee = null;
if (contacts.hasNext())
callee = contacts.next().getAddress();
while (contactDetails.hasNext())
{
UIContactDetail detail = contactDetails.next();
ProtocolProviderService detailProvider
= detail.getPreferredProtocolProvider(
OperationSetBasicTelephony.class);
if (detailProvider != null
&& detailProvider.equals(callProvider))
callee = detail.getAddress();
}
if (callee != null)
{
@ -137,7 +162,7 @@ public boolean importData(JComponent comp, Transferable t)
new String[]{
callProvider.getAccountID().getService(),
callProvider.getAccountID().getUserID(),
metaContact.getDisplayName()}))
uiContact.getDisplayName()}))
.showDialog();
}
}

@ -15,6 +15,7 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.protocol.*;
@ -28,6 +29,9 @@
public class ChooseCallAccountPopupMenu
extends JPopupMenu
{
/**
* The invoker component.
*/
private final JComponent invoker;
/**
@ -64,8 +68,8 @@ public ChooseCallAccountPopupMenu( JComponent invoker,
for (Object o : telephonyObjects)
{
if (o instanceof Contact)
this.addTelephonyContactItem((Contact) o);
if (o instanceof UIContactDetail)
this.addTelephonyContactItem((UIContactDetail) o);
else if (o instanceof ChatTransport)
this.addTelephonyChatTransportItem((ChatTransport) o);
}
@ -116,7 +120,7 @@ public void actionPerformed(ActionEvent e)
* telephony contact.
* @param telephonyContact the telephony contact to add
*/
private void addTelephonyContactItem(final Contact telephonyContact)
private void addTelephonyContactItem(final UIContactDetail telephonyContact)
{
final ContactMenuItem contactItem
= new ContactMenuItem(telephonyContact);
@ -125,8 +129,10 @@ private void addTelephonyContactItem(final Contact telephonyContact)
{
public void actionPerformed(ActionEvent e)
{
CallManager.createCall( telephonyContact.getProtocolProvider(),
telephonyContact);
CallManager.createCall(
telephonyContact.getPreferredProtocolProvider(
OperationSetBasicTelephony.class),
telephonyContact.getAddress());
ChooseCallAccountPopupMenu.this.setVisible(false);
}
});
@ -229,21 +235,26 @@ public ProtocolProviderService getProtocolProvider()
*/
private class ContactMenuItem extends JMenuItem
{
private final Contact contact;
private final UIContactDetail contact;
public ContactMenuItem(Contact contact)
public ContactMenuItem(UIContactDetail contact)
{
this.contact = contact;
this.setText(contact.getDisplayName());
BufferedImage contactIcon
= Constants.getStatusIcon(contact.getPresenceStatus());
BufferedImage contactIcon = null;
PresenceStatus status = contact.getPresenceStatus();
if (status != null)
contactIcon = Constants.getStatusIcon(status);
else
contactIcon = Constants.getStatusIcon(Constants.OFFLINE_STATUS);
if (contactIcon != null)
this.setIcon(new ImageIcon(contactIcon));
}
public Contact getContact()
public UIContactDetail getContact()
{
return contact;
}

@ -0,0 +1,23 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.call;
/**
* The <tt>MissedCallsListener</tt> listens for changes in the missed calls
* count. It is notified each time when a missed calls is registered by the
* <tt>CallManager</tt>.
*
* @author Yana Stamcheva
*/
public interface MissedCallsListener
{
/**
* Indicates the missed calls count has changed.
* @param newCallCount the new missed calls count
*/
public void missedCallCountChanged(int newCallCount);
}

@ -230,6 +230,8 @@ public void callEnded(CallEvent event)
if (sourceCall.equals(incomingCall))
{
CallManager.addMissedCall();
this.dispose();
}
}

@ -17,6 +17,7 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.customcontrols.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
@ -26,7 +27,7 @@
* A TransferHandler that we use to handle copying, pasting and DnD operations
* in our <tt>ChatPanel</tt>. The string handler is heavily inspired
* by Sun's <tt>DefaultTransferHandler</tt> with the main difference being that
* we only accept pasting of plain text. We do this in order to avoid html
* we only accept pasting of plain text. We do this in order to avoid HTML
* support problems that appear when pasting formatted text into our editable
* area.
*
@ -36,6 +37,12 @@
public class ChatTransferHandler
extends ExtendedTransferHandler
{
/**
* The data flavor used when transferring <tt>UIContact</tt>s.
*/
protected static final DataFlavor uiContactDataFlavor
= new DataFlavor(UIContact.class, "UIContact");
/**
* This class logger.
*/
@ -74,7 +81,7 @@ public boolean canImport(JComponent comp, DataFlavor flavor[])
{
for (int i = 0, n = flavor.length; i < n; i++)
{
if (flavor[i].equals(metaContactDataFlavor))
if (flavor[i].equals(uiContactDataFlavor))
{
return true;
}
@ -124,13 +131,13 @@ public boolean importData(JComponent comp, Transferable t)
logger.debug("Failed to drop files.", e);
}
}
else if (t.isDataFlavorSupported(metaContactDataFlavor))
else if (t.isDataFlavorSupported(uiContactDataFlavor))
{
Object o = null;
try
{
o = t.getTransferData(metaContactDataFlavor);
o = t.getTransferData(uiContactDataFlavor);
}
catch (UnsupportedFlavorException e)
{
@ -143,14 +150,18 @@ else if (t.isDataFlavorSupported(metaContactDataFlavor))
if (o instanceof ContactNode)
{
MetaContact metaContact
= ((ContactNode) o).getMetaContact();
UIContact uiContact
= ((ContactNode) o).getContactDescriptor();
// We only support drag&drop for MetaContacts for now.
if (!(uiContact instanceof MetaUIContact))
return false;
ChatTransport currentChatTransport
= chatPanel.getChatSession().getCurrentChatTransport();
Iterator<Contact> contacts = metaContact
.getContactsForProvider(
Iterator<Contact> contacts = ((MetaContact) uiContact
.getDescriptor()).getContactsForProvider(
currentChatTransport.getProtocolProvider());
String contact = null;
@ -173,7 +184,7 @@ else if (t.isDataFlavorSupported(metaContactDataFlavor))
"service.gui.ERROR"),
GuiActivator.getResources().getI18NString(
"service.gui.CONTACT_NOT_SUPPORTING_CHAT_CONF",
new String[]{metaContact.getDisplayName()}))
new String[]{uiContact.getDisplayName()}))
.showDialog();
}
}

@ -0,0 +1,154 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
import java.util.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.service.contactsource.*;
/**
* The <tt>CallHistoryFilter</tt> is a filter over the history contact sources.
*
* @author Yana Stamcheva
*/
public class CallHistoryFilter
implements ContactListFilter,
ContactQueryListener
{
/**
* The <tt>ContactListTreeModel</tt>, where the results of this filter are
* stored
*/
private ContactListTreeModel resultTreeModel;
/**
* The current <tt>ContactQuery</tt>.
*/
private ContactQuery currentQuery;
/**
* Applies this filter and stores the result in the given <tt>treeModel</tt>.
*
* @param treeModel the <tt>ContactListTreeModel</tt>, where the results
* of this filter are stored
*/
public void applyFilter(ContactListTreeModel treeModel)
{
this.resultTreeModel = treeModel;
Collection<ExternalContactSource> contactSources
= TreeContactList.getContactSources();
for (ExternalContactSource contactSource : contactSources)
{
ContactSourceService sourceService
= contactSource.getContactSourceService();
if (!sourceService.getIdentifier()
.equals(ContactSourceService.CALL_HISTORY))
continue;
// We're in a case of call history contact source.
currentQuery = sourceService.queryContactSource("");
// Add first available results.
this.addMatching( currentQuery.getQueryResults(),
contactSource);
currentQuery.addContactQueryListener(this);
}
}
public boolean isMatching(UIContact uiContact)
{
return false;
}
public boolean isMatching(UIGroup uiGroup)
{
return false;
}
/**
* Adds matching <tt>sourceContacts</tt> to the result tree model.
* @param sourceContacts the list of <tt>SourceContact</tt>s to add
* @param uiSource the <tt>ExternalContactSource</tt>, which contacts
* we're adding
*/
private void addMatching( List<SourceContact> sourceContacts,
ExternalContactSource uiSource)
{
Iterator<SourceContact> contactsIter = sourceContacts.iterator();
while (contactsIter.hasNext())
{
addHistoryContact(contactsIter.next(), uiSource);
}
}
/**
* Indicates that a contact has been received for a query.
* @param event the <tt>ContactReceivedEvent</tt> that notified us
*/
public void contactReceived(ContactReceivedEvent event)
{
synchronized (resultTreeModel)
{
ExternalContactSource sourceUI
= TreeContactList.getContactSource(
event.getQuerySource().getContactSource());
addHistoryContact(event.getContact(), sourceUI);
}
}
/**
* Indicates that the query status has changed.
* @param event the <tt>ContactQueryStatusEvent</tt> that notified us
*/
public void queryStatusChanged(ContactQueryStatusEvent event)
{
int eventType = event.getEventType();
// Remove the current query when it's stopped for some reason.
// QUERY_COMPLETED, QUERY_COMPLETED, QUERY_ERROR
currentQuery = null;
if (eventType == ContactQueryStatusEvent.QUERY_ERROR)
{
//TODO: Show the error to the user??
}
event.getQuerySource().removeContactQueryListener(this);
}
/**
* Adds the given <tt>sourceContact</tt> to the contact list.
* @param sourceContact the <tt>SourceContact</tt> to add
* @param uiSource the UI adapter for the original contact source
*/
private void addHistoryContact( SourceContact sourceContact,
ExternalContactSource uiSource)
{
GuiActivator.getContactList()
.addContact(resultTreeModel,
uiSource.getUIContact(sourceContact),
false,
false);
}
/**
* Stops this filter current queries.
*/
public void stopFilter()
{
if (currentQuery != null)
currentQuery.cancel();
}
}

@ -66,7 +66,7 @@ public class ContactList
private GroupRightButtonMenu groupRightButtonMenu;
private ContactRightButtonMenu contactRightButtonMenu;
private MetaContactRightButtonMenu contactRightButtonMenu;
/**
* A list of all contacts that are currently "active". An "active" contact
@ -391,15 +391,16 @@ public void fireContactListEvent(Object source, int eventID, int clickCount)
* <tt>ContactListListener</tt>s that a contact is selected.
*
* @param sourceContact the contact that this event is about
* @param protocolContact the protocol contact the this event is about
* @param eventID the id indicating the exact type of the event to fire.
* @param clickCount
*/
public void fireContactListEvent(MetaContact sourceContact,
Contact protocolContact, int eventID)
public void fireContactListEvent( MetaContact sourceContact,
int eventID,
int clickCount)
{
fireContactListEvent(
contactListListeners,
new ContactListEvent(sourceContact, protocolContact, eventID));
new ContactListEvent(sourceContact, eventID, clickCount));
}
/**
@ -420,11 +421,8 @@ protected void fireContactListEvent(
case ContactListEvent.CONTACT_CLICKED:
listener.contactClicked(event);
break;
case ContactListEvent.PROTOCOL_CONTACT_CLICKED:
listener.protocolContactClicked(event);
break;
case ContactListEvent.GROUP_CLICKED:
listener.groupSelected(event);
listener.groupClicked(event);
break;
default:
logger.error("Unknown event type " + event.getEventID());
@ -542,10 +540,6 @@ else if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0)
{
MetaContact contact = (MetaContact) selectedValue;
// get the component under the mouse
Component component = this.getHorizontalComponent(renderer,
translatedX);
// Right click and Ctrl+LeftClick on the contact label opens
// Popup menu
if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0
@ -629,40 +623,6 @@ public void setDisableOpenClose(boolean disableOpenClose)
this.disableOpenClose = disableOpenClose;
}
/**
* Runs the info window for the specified contact at the appropriate
* position.
*/
private class RunInfoWindow
implements Runnable
{
private final MetaContact contactItem;
private final Point p;
private RunInfoWindow(Point p, MetaContact contactItem)
{
this.p = p;
this.contactItem = contactItem;
}
public void run()
{
ContactInfoDialog contactInfoPanel
= new ContactInfoDialog(mainFrame, contactItem);
SwingUtilities.convertPointToScreen(p, ContactList.this);
// TODO: to calculate popup window position properly.
contactInfoPanel.setPopupLocation(p.x - 140, p.y - 15);
contactInfoPanel.setVisible(true);
contactInfoPanel.requestFocusInWindow();
}
}
/**
* Takes care of keeping the contact list up to date.
*/
@ -977,7 +937,7 @@ public void setSelectedValue(Object o)
*
* @return the right button menu for a contact
*/
public ContactRightButtonMenu getContactRightButtonMenu()
public MetaContactRightButtonMenu getContactRightButtonMenu()
{
return contactRightButtonMenu;
}

@ -8,12 +8,14 @@
import java.util.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
/**
* The <tt>ContactListEvent</tt> is triggered when a contact or a group is
* clicked in the contact list.
* @author Yana Stamcheva
*/
public class ContactListEvent
extends EventObject{
extends EventObject
{
private int eventID = -1;
/**
@ -22,29 +24,17 @@ public class ContactListEvent
*/
public static final int CONTACT_CLICKED = 1;
/**
* Indicates that the ContactListEvent instance was triggered by
* selecting a protocol contact in the contact list.
*/
public static final int PROTOCOL_CONTACT_CLICKED = 2;
/**
* Indicates that the ContactListEvent instance was triggered by selecting
* a group in the contact list.
*/
public static final int GROUP_CLICKED = 3;
public static final int GROUP_CLICKED = 2;
/**
* Indicated the number of click accompanying the event
*/
private int clickCount;
/**
* Specific <tt>Contact</tt> of this <tt>MetaContact</tt> involved in
* the event if any, null otherwise.
*/
private Contact sourceProtoContact;
/**
* Creates a new ContactListEvent according to the specified parameters.
* @param source the MetaContact which was selected
@ -56,26 +46,11 @@ public class ContactListEvent
public ContactListEvent(Object source, int eventID, int clickCount)
{
super(source);
this.eventID = eventID;
this.clickCount = clickCount;
}
/**
* Creates a new ContactListEvent according to the specified parameters.
* @param source the MetaContact which was selected
* @param protocolContact the protocol specifique contact which was selected
* @param eventID one of the XXX_SELECTED static fields indicating the
* nature of the event.
*/
public ContactListEvent(Object source,
Contact protocolContact, int eventID)
{
super(source);
this.eventID = eventID;
this.sourceProtoContact = protocolContact;
}
/**
* Returns an event id specifying whether the type of this event
* (CONTACT_SELECTED or PROTOCOL_CONTACT_SELECTED)
@ -87,36 +62,27 @@ public int getEventID()
}
/**
* Returns the MetaContact for which this event occured.
* @return the MetaContact for which this event occured
* Returns the <tt>UIContactDescriptor</tt> for which this event occured.
* @return the </tt>UIContactDescriptor</tt> for which this event occured
*/
public MetaContact getSourceContact()
public UIContact getSourceContact()
{
if(getSource() instanceof MetaContact)
return (MetaContact)getSource();
if(getSource() instanceof UIContact)
return (UIContact) getSource();
return null;
}
/**
* Returns the MetaContactGroup for which this event occured.
* @return the MetaContactGroup for which this event occured
* Returns the <tt>UIGroupDescriptor</tt> for which this event occured.
* @return the <tt>UIGroupDescriptor</tt> for which this event occured
*/
public MetaContactGroup getSourceGroup()
public UIGroup getSourceGroup()
{
if(getSource() instanceof MetaContactGroup)
return (MetaContactGroup)getSource();
return null;
}
if(getSource() instanceof UIGroup)
return (UIGroup) getSource();
/**
* Returns the protocol contact for which this event occured.
* @return the protocol contact for which this event occured
*/
public Contact getSourceProtoContact()
{
return sourceProtoContact;
return null;
}
/**

@ -6,33 +6,41 @@
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
import net.java.sip.communicator.service.contactlist.*;
/**
* The <tt>ContactListFilter</tt> is an interface meant to be implemented by
* modules interested in filtering the contact list. An implementation of this
* interface should be able answer if a <tt>MetaContact</tt> or a
* <tt>MetaContactGroup</tt> is matching the corresponding filter.
* interface should be able to answer if an <tt>UIContact</tt> or an
* <tt>UIGroup</tt> is matching the corresponding filter.
*
* @author Yana Stamcheva
*/
public interface ContactListFilter
{
/**
* Returns <tt>true</tt> if the given <tt>metaContact</tt> is matching this
* filter, otherwise returns <tt>false</tt>
* @param metaContact the <tt>MetaContact</tt> to check
* @return <tt>true</tt> if the given <tt>metaContact</tt> is matching this
* filter, otherwise returns <tt>false</tt>
* Indicates if the given <tt>uiGroup</tt> is matching the current filter.
* @param uiContact the <tt>UIContact</tt> to check
* @return <tt>true</tt> to indicate that the given <tt>uiContact</tt>
* matches this filter, <tt>false</tt> - otherwise
*/
public boolean isMatching(UIContact uiContact);
/**
* Indicates if the given <tt>uiGroup</tt> is matching the current filter.
* @param uiGroup the <tt>UIGroup</tt> to check
* @return <tt>true</tt> to indicate that the given <tt>uiGroup</tt>
* matches this filter, <tt>false</tt> - otherwise
*/
public boolean isMatching(UIGroup uiGroup);
/**
* Applies this filter to any interested sources and stores the result in
* the given <tt>treeModel</tt>.
* @param treeModel the <tt>ContactListTreeModel</tt> to store the result in
*/
public boolean isMatching(MetaContact metaContact);
public void applyFilter(ContactListTreeModel treeModel);
/**
* Returns <tt>true</tt> if the given <tt>metaGroup</tt> is matching this
* filter, otherwise returns <tt>false</tt>
* @param metaGroup the <tt>MetaContactGroup</tt> to check
* @return <tt>true</tt> if the given <tt>metaGroup</tt> is matching this
* filter, otherwise returns <tt>false</tt>
* Stops this filter current queries.
*/
public boolean isMatching(MetaContactGroup metaGroup);
public void stopFilter();
}

@ -9,28 +9,24 @@
import java.util.*;
/**
* Listens for events coming from the contact list.
*
* Listens for events coming from mouse events over the contact list. For
* example a contact been clicked or a group been selected.
*
* @author Yana Stamcheva
*/
public interface ContactListListener extends EventListener
{
/**
*
* @param evt
* Indicates that a group has been selected.
* @param evt the <tt>ContactListEvent</tt> that has been triggered from
* the user selection
*/
public void groupSelected(ContactListEvent evt);
public void groupClicked(ContactListEvent evt);
/**
*
* @param evt
* Indicates that a contact has been clicked.
* @param evt the <tt>ContactListEvent</tt> that has been triggered from
* the user click
*/
public void contactClicked(ContactListEvent evt);
/**
*
* @param evt
*/
public void protocolContactClicked(ContactListEvent evt);
}

@ -7,9 +7,9 @@
package net.java.sip.communicator.impl.gui.main.contactlist;
/**
* The <tt>ContactListNode</tt> represents a node in the contact list. An
* implementation of this interface should be able to determine the index of
* this node in the <tt>MetaContactListService</tt>.
* The <tt>ContactListNode</tt> represents a node in the contact list data
* model. An implementation of this interface should be able to determine the
* index of this node in its contact source.
*
* @author Yana Stamcheva
*/
@ -19,5 +19,5 @@ public interface ContactListNode
* Returns the index of this node in the <tt>MetaContactListService</tt>.
* @return the index of this node in the <tt>MetaContactListService</tt>
*/
public int getMetaContactListIndex();
public int getSourceIndex();
}

@ -92,9 +92,21 @@ public ContactListPane(MainFrame mainFrame)
public void initList(MetaContactListService contactListService)
{
this.contactList = new TreeContactList();
// We should first set the contact list to the GuiActivator, so that
// anybody could get it from there.
GuiActivator.setContactList(contactList);
// By default we set the current filter to be the presence filter.
new Thread()
{
public void run()
{
TreeContactList.presenceFilter
.setShowOffline(ConfigurationManager.isShowOffline());
contactList.applyFilter(TreeContactList.presenceFilter);
}
}.start();
TransparentPanel transparentPanel
= new TransparentPanel(new BorderLayout());
@ -146,7 +158,13 @@ public void contactClicked(ContactListEvent evt)
if (evt.getClickCount() < 2)
return;
MetaContact metaContact = evt.getSourceContact();
UIContact descriptor = evt.getSourceContact();
// We're currently only interested in MetaContacts.
if (!(descriptor.getDescriptor() instanceof MetaContact))
return;
MetaContact metaContact = (MetaContact) descriptor.getDescriptor();
// Searching for the right proto contact to use as default for the
// chat conversation.
@ -200,23 +218,9 @@ public void contactClicked(ContactListEvent evt)
* Implements the ContactListListener.groupSelected method.
* @param evt the <tt>ContactListEvent</tt> that notified us
*/
public void groupSelected(ContactListEvent evt)
public void groupClicked(ContactListEvent evt)
{}
/**
* Implements the ContactListListener.protocolContactSelected method.
* @param evt the <tt>ContactListEvent</tt> that notified us
*/
public void protocolContactClicked(ContactListEvent evt)
{
Contact protoContact = evt.getSourceProtoContact();
ContactEventHandler contactHandler = mainFrame
.getContactHandler(protoContact.getProtocolProvider());
contactHandler.contactClicked(protoContact, evt.getClickCount());
}
/**
* When a message is received determines whether to open a new chat window
* or chat window tab, or to indicate that a message is received from a
@ -660,26 +664,6 @@ private void setMetaContact(MetaContact metaContact)
}
}
/**
* Opens chat window when the selected value is a MetaContact and opens a
* group when the selected value is a MetaContactGroup.
*/
private class ContactListPanelEnterAction extends AbstractAction
{
public void actionPerformed(ActionEvent e)
{
Object selectedValue
= contactList.getSelectionPath().getLastPathComponent();
if (selectedValue instanceof MetaContact)
{
MetaContact contact = (MetaContact) selectedValue;
chatWindowManager.startChat(contact);
}
}
}
private void initPluginComponents()
{
// Search for plugin components registered through the OSGI bundle

@ -15,6 +15,7 @@
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
@ -28,9 +29,21 @@
public class ContactListTransferHandler
extends ExtendedTransferHandler
{
/**
* The data flavor used when transferring <tt>UIContact</tt>s.
*/
protected static final DataFlavor uiContactDataFlavor
= new DataFlavor(UIContact.class, "UIContact");
/**
* The logger.
*/
private static final Logger logger
= Logger.getLogger(ContactListTransferHandler.class);
/**
* The contact list, where this transfer happens.
*/
private final DefaultTreeContactList contactList;
/**
@ -81,7 +94,7 @@ public boolean canImport(JComponent comp, DataFlavor flavor[])
{
for (int i = 0, n = flavor.length; i < n; i++)
{
if (flavor[i].equals(metaContactDataFlavor))
if (flavor[i].equals(uiContactDataFlavor))
{
return true;
}
@ -140,13 +153,13 @@ public boolean importData(JComponent comp, Transferable t)
}
}
}
else if (t.isDataFlavorSupported(metaContactDataFlavor))
else if (t.isDataFlavorSupported(uiContactDataFlavor))
{
Object o = null;
try
{
o = t.getTransferData(metaContactDataFlavor);
o = t.getTransferData(uiContactDataFlavor);
}
catch (UnsupportedFlavorException e)
{
@ -162,8 +175,13 @@ else if (t.isDataFlavorSupported(metaContactDataFlavor))
if (o instanceof ContactNode
&& comp instanceof TreeContactList)
{
MetaContact transferredContact
= ((ContactNode) o).getMetaContact();
UIContact transferredContact
= ((ContactNode) o).getContactDescriptor();
// We support darg&drop for MetaContacts only.
if (!(transferredContact instanceof MetaUIContact))
return false;
TreeContactList list = (TreeContactList) comp;
Object dest
@ -173,25 +191,36 @@ else if (t.isDataFlavorSupported(metaContactDataFlavor))
{
if (dest instanceof ContactNode)
{
MetaContact destContact
= ((ContactNode) dest).getMetaContact();
UIContact destContact
= ((ContactNode) dest).getContactDescriptor();
// We support darg&drop for MetaContacts only for now.
if (!(destContact instanceof MetaUIContact))
return false;
if (transferredContact != destContact)
{
MetaContactListManager.moveMetaContactToMetaContact(
transferredContact, destContact);
(MetaContact) transferredContact.getDescriptor(),
(MetaContact) destContact.getDescriptor());
}
return true;
}
else if (dest instanceof GroupNode)
{
MetaContactGroup destGroup
= ((GroupNode) dest).getMetaContactGroup();
UIGroup destGroup
= ((GroupNode) dest).getGroupDescriptor();
// We support darg&drop for MetaContacts only for now.
if (!(destGroup instanceof MetaUIGroup))
return false;
if (transferredContact.getParentMetaContactGroup()
!= destGroup)
if (!transferredContact
.getParentGroup().equals(destGroup))
{
MetaContactListManager.moveMetaContactToGroup(
transferredContact, destGroup);
(MetaContact) transferredContact.getDescriptor(),
(MetaContactGroup) destGroup.getDescriptor());
}
return true;
}
@ -225,7 +254,7 @@ public Icon getVisualRepresentation(Transferable t)
contactList.getCellRenderer()
.getTreeCellRendererComponent(
contactList,
transferable.getTransferData(metaContactDataFlavor),
transferable.getTransferData(uiContactDataFlavor),
true, // is selected
false, // is expanded
true, // is leaf
@ -303,7 +332,7 @@ public ContactListTransferable(int index, Object o)
*/
public DataFlavor[] getTransferDataFlavors()
{
return new DataFlavor[] { metaContactDataFlavor,
return new DataFlavor[] { uiContactDataFlavor,
DataFlavor.stringFlavor};
}
@ -316,7 +345,7 @@ public DataFlavor[] getTransferDataFlavors()
*/
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return metaContactDataFlavor.equals(flavor)
return uiContactDataFlavor.equals(flavor)
|| DataFlavor.stringFlavor.equals(flavor);
}
@ -333,7 +362,7 @@ public Object getTransferData(DataFlavor flavor)
throws UnsupportedFlavorException,
IOException
{
if (metaContactDataFlavor.equals(flavor))
if (uiContactDataFlavor.equals(flavor))
{
return transferredObject;
}
@ -341,7 +370,7 @@ else if (DataFlavor.stringFlavor.equals(flavor))
{
if (transferredObject instanceof ContactNode)
return ((ContactNode) transferredObject)
.getMetaContact().getDisplayName();
.getContactDescriptor().getDisplayName();
}
else
throw new UnsupportedFlavorException(flavor);

@ -10,7 +10,6 @@
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
@ -71,13 +70,6 @@ public class ContactListTreeCellRenderer
*/
private static final int EXTENDED_AVATAR_WIDTH = 45;
/**
* The key of the user data in <tt>MetaContact</tt> which specifies
* the avatar cached from previous invocations.
*/
private static final String AVATAR_DATA_KEY
= ContactListCellRenderer.class.getName() + ".avatar";
/**
* The icon used for opened groups.
*/
@ -108,7 +100,7 @@ public class ContactListTreeCellRenderer
/**
* The status message label.
*/
private final JLabel statusMessageLabel = new JLabel();
private final JLabel displayDetailsLabel = new JLabel();
/**
* The call button.
@ -148,7 +140,7 @@ public class ContactListTreeCellRenderer
/**
* The icon showing the contact status.
*/
protected final ImageIcon statusIcon = new ImageIcon();
protected ImageIcon statusIcon = new ImageIcon();
/**
* Indicates if the current list cell is selected.
@ -194,8 +186,8 @@ public ContactListTreeCellRenderer()
this.setOpaque(false);
this.nameLabel.setOpaque(false);
this.statusMessageLabel.setFont(getFont().deriveFont(9f));
this.statusMessageLabel.setForeground(Color.GRAY);
this.displayDetailsLabel.setFont(getFont().deriveFont(9f));
this.displayDetailsLabel.setForeground(Color.GRAY);
this.rightLabel.setFont(rightLabel.getFont().deriveFont(9f));
this.rightLabel.setHorizontalAlignment(JLabel.RIGHT);
@ -239,27 +231,56 @@ public ContactListTreeCellRenderer()
{
public void actionPerformed(ActionEvent e)
{
ChooseCallAccountPopupMenu chooseAccountDialog = null;
if (treeNode != null && treeNode instanceof ContactNode)
{
List<Contact> telephonyContacts
= ((ContactNode) treeNode).getMetaContact()
.getContactsForOperationSet(
List<UIContactDetail> telephonyContacts
= ((ContactNode) treeNode).getContactDescriptor()
.getContactDetailsForOperationSet(
OperationSetBasicTelephony.class);
if (telephonyContacts.size() == 1)
{
Contact contact = telephonyContacts.get(0);
CallManager.createCall(
telephonyContacts.get(0).getProtocolProvider(),
contact);
UIContactDetail detail
= telephonyContacts.get(0);
ProtocolProviderService preferredProvider
= detail.getPreferredProtocolProvider(
OperationSetBasicTelephony.class);
if (preferredProvider != null)
CallManager.createCall(
preferredProvider,
detail.getAddress());
else
{
List<ProtocolProviderService> providers
= CallManager.getTelephonyProviders();
int providersCount = providers.size();
if (providersCount == 1)
CallManager.createCall(
providers.get(0),
detail.getAddress());
else if (providersCount > 1)
chooseAccountDialog
= new ChooseCallAccountPopupMenu(
tree, providers);
}
}
else if (telephonyContacts.size() > 1)
{
ChooseCallAccountPopupMenu chooseAccountDialog
chooseAccountDialog
= new ChooseCallAccountPopupMenu(
tree,
telephonyContacts);
}
// If the choose dialog is created we're going to show it.
if (chooseAccountDialog != null)
{
Point location = new Point(callButton.getX(),
callButton.getY() + callButton.getHeight());
@ -281,8 +302,16 @@ public void actionPerformed(ActionEvent e)
{
if (treeNode != null && treeNode instanceof ContactNode)
{
GuiActivator.getUIService().getChatWindowManager()
.startChat(((ContactNode) treeNode).getMetaContact());
UIContact contactDescriptor
= ((ContactNode) treeNode).getContactDescriptor();
if (contactDescriptor.getDescriptor()
instanceof MetaContact)
{
GuiActivator.getUIService().getChatWindowManager()
.startChat(
(MetaContact) contactDescriptor.getDescriptor());
}
}
}
});
@ -318,9 +347,10 @@ public Component getTreeCellRendererComponent(JTree tree, Object value,
if (value instanceof ContactNode)
{
MetaContact metaContact = ((ContactNode) value).getMetaContact();
UIContact contact
= ((ContactNode) value).getContactDescriptor();
String displayName = metaContact.getDisplayName();
String displayName = contact.getDisplayName();
if (displayName == null || displayName.length() < 1)
{
@ -330,14 +360,13 @@ public Component getTreeCellRendererComponent(JTree tree, Object value,
this.nameLabel.setText(displayName);
if(contactList.isContactActive(metaContact))
if(contactList.isContactActive(contact))
{
statusIcon.setImage(msgReceivedImage);
}
else
{
statusIcon.setImage(Constants.getStatusIcon(
contactList.getMetaContactStatus(metaContact)));
statusIcon = contact.getStatusIcon();
}
this.statusLabel.setIcon(statusIcon);
@ -348,30 +377,35 @@ public Component getTreeCellRendererComponent(JTree tree, Object value,
// Initializes status message components if the given meta contact
// contains a status message.
this.initStatusMessage(metaContact);
this.initDisplayDetails(contact);
this.initButtonsPanel(metaContact);
this.initButtonsPanel(contact);
ImageIcon avatar = isSelected
? contact.getAvatar(
isSelected, EXTENDED_AVATAR_WIDTH, EXTENDED_AVATAR_HEIGHT)
: contact.getAvatar(
isSelected, AVATAR_WIDTH, AVATAR_HEIGHT);
ImageIcon avatar = getAvatar(metaContact);
if (avatar != null)
this.rightLabel.setIcon(avatar);
this.rightLabel.setText("");
this.setToolTipText(metaContact.getMetaUID());
this.setToolTipText(contact.getDescriptor().toString());
}
else if (value instanceof GroupNode)
{
MetaContactGroup groupItem
= ((GroupNode) value).getMetaContactGroup();
UIGroup groupItem
= ((GroupNode) value).getGroupDescriptor();
this.nameLabel.setText(groupItem.getGroupName());
this.nameLabel.setText(groupItem.getDisplayName());
this.nameLabel.setFont(this.getFont().deriveFont(Font.BOLD));
if (groupForegroundColor != null)
this.nameLabel.setForeground(groupForegroundColor);
this.remove(statusMessageLabel);
this.remove(displayDetailsLabel);
this.remove(callButton);
this.remove(chatButton);
@ -382,73 +416,17 @@ else if (value instanceof GroupNode)
// We have no photo icon for groups.
this.rightLabel.setIcon(null);
this.rightLabel.setText( groupItem.countOnlineChildContacts()
+ "/" + groupItem.countChildContacts());
if (groupItem.countChildContacts() >= 0)
this.rightLabel.setText( groupItem.countOnlineChildContacts()
+ "/" + groupItem.countChildContacts());
this.setToolTipText(groupItem.getMetaUID());
this.setToolTipText(groupItem.getDescriptor().toString());
}
return this;
}
/**
* Gets the avatar of a specific <tt>MetaContact</tt> in the form of an
* <tt>ImageIcon</tt> value.
*
* @param metaContact the <tt>MetaContact</tt> to retrieve the avatar of
* @return an <tt>ImageIcon</tt> which represents the avatar of the
* specified <tt>MetaContact</tt>
*/
private ImageIcon getAvatar(MetaContact metaContact)
{
byte[] avatarBytes = metaContact.getAvatar(true);
ImageIcon avatar = null;
// If there'rs no avatar we have nothing more to do here.
if((avatarBytes == null) || (avatarBytes.length <= 0))
return null;
// If the cell is selected we return a zoomed version of the avatar
// image.
if (isSelected)
return ImageUtils.getScaledRoundedIcon(
avatarBytes,
EXTENDED_AVATAR_WIDTH,
EXTENDED_AVATAR_HEIGHT);
// In any other case try to get the avatar from the cache.
Object[] avatarCache = (Object[]) metaContact.getData(AVATAR_DATA_KEY);
if ((avatarCache != null) && (avatarCache[0] == avatarBytes))
avatar = (ImageIcon) avatarCache[1];
// Just
int avatarWidth = AVATAR_WIDTH;
int avatarHeight = AVATAR_HEIGHT;
// If the avatar isn't available or it's not up-to-date, create it.
if (avatar == null)
avatar = ImageUtils.getScaledRoundedIcon(
avatarBytes,
avatarWidth,
avatarHeight);
// Cache the avatar in case it has changed.
if (avatarCache == null)
{
if (avatar != null)
metaContact.setData(
AVATAR_DATA_KEY,
new Object[] { avatarBytes, avatar });
}
else
{
avatarCache[0] = avatarBytes;
avatarCache[1] = avatar;
}
return avatar;
}
/**
* Paints a customized background.
*
@ -571,35 +549,25 @@ public Dimension getPreferredSize()
}
/**
* Returns the first found status message for the given
* <tt>metaContact</tt>.
* @param metaContact the <tt>MetaContact</tt>, for which we'd like to
* obtain a status message
* Initializes the display details component for the given
* <tt>UIContact</tt>.
* @param contact the <tt>UIContact</tt>, for which we initialize the
* details component
*/
private void initStatusMessage(MetaContact metaContact)
private void initDisplayDetails(UIContact contact)
{
statusMessageLabel.setText("");
this.remove(statusMessageLabel);
displayDetailsLabel.setText("");
this.remove(displayDetailsLabel);
String statusMessage = null;
Iterator<Contact> protoContacts = metaContact.getContacts();
while (protoContacts.hasNext())
{
Contact protoContact = protoContacts.next();
statusMessage = protoContact.getStatusMessage();
if (statusMessage != null && statusMessage.length() > 0)
break;
}
String displayDetails = contact.getDisplayDetails();
if (statusMessage != null && statusMessage.length() > 0)
if (displayDetails != null && displayDetails.length() > 0)
{
// Replace all occurrences of new line with slash.
statusMessage = Html2Text.extractText(statusMessage);
statusMessage = statusMessage.replaceAll("\n|<br>|<br/>", " / ");
displayDetails = Html2Text.extractText(displayDetails);
displayDetails = displayDetails.replaceAll("\n|<br>|<br/>", " / ");
statusMessageLabel.setText(statusMessage);
displayDetailsLabel.setText(displayDetails);
constraints.anchor = GridBagConstraints.WEST;
constraints.fill = GridBagConstraints.NONE;
@ -610,16 +578,16 @@ private void initStatusMessage(MetaContact metaContact)
constraints.gridwidth = 2;
constraints.gridheight = 1;
this.add(statusMessageLabel, constraints);
this.add(displayDetailsLabel, constraints);
}
}
/**
* Initializes buttons panel.
* @param metaContact the <tt>MetaContact</tt> for which we initialize the
* @param uiContact the <tt>UIContact</tt> for which we initialize the
* button panel
*/
private void initButtonsPanel(MetaContact metaContact)
private void initButtonsPanel(UIContact uiContact)
{
this.remove(callButton);
this.remove(chatButton);
@ -628,13 +596,13 @@ private void initButtonsPanel(MetaContact metaContact)
return;
int statusMessageLabelHeight = 0;
if (statusMessageLabel.getText().length() > 0)
if (displayDetailsLabel.getText().length() > 0)
statusMessageLabelHeight = 20;
else
statusMessageLabelHeight = 15;
Contact imContact = metaContact.getDefaultContact(
OperationSetBasicInstantMessaging.class);
UIContactDetail imContact = uiContact.getDefaultContactDetail(
OperationSetBasicInstantMessaging.class);
if (imContact != null)
{
@ -656,8 +624,8 @@ private void initButtonsPanel(MetaContact metaContact)
28, 28);
}
Contact telephonyContact
= metaContact.getDefaultContact(OperationSetBasicTelephony.class);
UIContactDetail telephonyContact
= uiContact.getDefaultContactDetail(OperationSetBasicTelephony.class);
if (telephonyContact != null)
{

@ -6,10 +6,9 @@
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
import javax.swing.*;
import javax.swing.tree.*;
import net.java.sip.communicator.service.contactlist.*;
/**
* The data model of the contact list.
*
@ -18,91 +17,78 @@
public class ContactListTreeModel
extends DefaultTreeModel
{
private GroupNode rootGroupNode;
/**
* The root node.
*/
private final GroupNode rootGroupNode;
/**
* Creates the <tt>ContactListTreeModel</tt> by specifying the
* <tt>rootMetaGroup</tt>, which would correspond to the root of our data
* model.
* @param rootMetaGroup the root <tt>MetaContactGroup</tt>, which would
* correspond to our root node
* Creates an instance of <tt>ContactListTreeModel</tt>.
*/
public ContactListTreeModel(MetaContactGroup rootMetaGroup)
public ContactListTreeModel()
{
super(null);
rootGroupNode = new GroupNode(this, rootMetaGroup);
RootUIGroup rootDescriptor = new RootUIGroup();
rootGroupNode = new GroupNode(this, rootDescriptor);
rootDescriptor.setGroupNode(rootGroupNode);
this.setRoot(rootGroupNode);
}
/**
* Creates the <tt>ContactListTreeModel</tt> by specifying the
* root node.
* @param root the root node
*/
public ContactListTreeModel(TreeNode root)
{
super(root);
}
/**
* Returns the root group node.
* @return the root group node
*/
@Override
public GroupNode getRoot()
{
return rootGroupNode;
}
/**
* Returns the <tt>GroupNode</tt> corresponding to the given
* <tt>metaGroup</tt>. This method will look in deep.
* @param metaGroup the <tt>MetaContactGroup</tt>, which corresponding node
* we're looking for
* @return the <tt>GroupNode</tt> corresponding to the given
* <tt>metaGroup</tt>
* Returns the first found child <tt>ContactNode</tt>.
* @return the first found child <tt>ContactNode</tt> or <tt>null</tt>
* if there is no ContactNode.
*/
public GroupNode findGroupNodeByMetaGroup(MetaContactGroup metaGroup)
public ContactNode findFirstContactNode()
{
if (metaGroup.equals(rootGroupNode.getMetaContactGroup()))
return rootGroupNode;
else
return rootGroupNode.findGroupNode(metaGroup);
return findFirstContactNode(rootGroupNode);
}
/**
* Returns the <tt>ContactNode</tt> corresponding to the given
* <tt>metaContact</tt>. This method will look in deep.
* @param metaContact the <tt>MetaContact</tt>, which corresponding node
* we're looking for
* @return the <tt>ContactNode</tt> corresponding to the given
* <tt>metaContact</tt>
* Clears all dependencies in the abstraction path (i.e. GroupNode - UIGroup
* - MetaContactGroup or ContactNode - UIContact - SourceContact).
*/
public ContactNode findContactNodeByMetaContact(MetaContact metaContact)
public void clearDependencies()
{
MetaContactGroup parentGroup = metaContact.getParentMetaContactGroup();
if (parentGroup == null)
return null;
GroupNode parentGroupNode = findGroupNodeByMetaGroup(parentGroup);
if (parentGroupNode != null)
return parentGroupNode.findContactNode(metaContact);
return null;
clearDependencies(rootGroupNode);
}
/**
* Returns the first found child <tt>ContactNode</tt>.
* @return the first found child <tt>ContactNode</tt> or <tt>null</tt>
* if there is no ContactNode.
* Clears all dependencies for all children in the given <tt>groupNode</tt>
* (i.e. GroupNode - UIGroup - MetaContactGroup or ContactNode - UIContact
* - SourceContact).
* @param groupNode the <tt>TreeNode</tt> in which we clear dependencies
*/
public ContactNode findFirstContactNode()
private void clearDependencies(TreeNode groupNode)
{
return findFirstContactNode(rootGroupNode);
for (int i = 0; i < groupNode.getChildCount(); i ++)
{
TreeNode treeNode = groupNode.getChildAt(i);
if (treeNode instanceof ContactNode)
{
((ContactNode) treeNode).getContactDescriptor()
.setContactNode(null);
}
else if (treeNode instanceof GroupNode)
{
((GroupNode) treeNode).getGroupDescriptor()
.setGroupNode(null);
clearDependencies(treeNode);
}
}
}
/**
@ -113,14 +99,128 @@ public ContactNode findFirstContactNode()
private ContactNode findFirstContactNode(GroupNode parentNode)
{
// If the parent node has no children we have nothing to do here.
if (parentNode.getChildCount() ==0)
if (parentNode.getChildCount() == 0)
return null;
TreeNode treeNode = parentNode.getFirstChild();
if (treeNode instanceof GroupNode)
return findFirstContactNode((GroupNode)treeNode);
return findFirstContactNode((GroupNode) treeNode);
else
return (ContactNode)treeNode;
}
/**
* The <tt>RootUIGroup</tt> is the root group in this contact list model.
*/
private class RootUIGroup
implements UIGroup
{
/**
* The corresponding group node.
*/
private GroupNode groupNode;
/**
* Returns null to indicate that this group has no parent.
* @return null
*/
public UIGroup getParentGroup()
{
return null;
}
/**
* This group is not attached to a contact source, so we return the
* first index.
* @return 0
*/
public int getSourceIndex()
{
return 0;
}
/**
* This group should never be collapsed.
* @return false
*/
public boolean isGroupCollapsed()
{
return false;
}
/**
* Returns null to indicate that this group has no display name.
* @return null
*/
public String getDisplayName()
{
return null;
}
/**
* As this group is not attached to a contact source it has no child
* contacts.
* @return 0
*/
public int countChildContacts()
{
return 0;
}
/**
* As this group is not attached to a contact source it has no child
* contacts.
* @return 0
*/
public int countOnlineChildContacts()
{
return 0;
}
/**
* Returns the descriptor of this group, just a string.
* @return the descriptor of this group
*/
public Object getDescriptor()
{
return "RootGroup";
}
/**
* Returns null to indicate that this group has no identifier.
* @return null
*/
public String getId()
{
return null;
}
/**
* Returns the corresponding <tt>GroupNode</tt>.
* @return the corresponding <tt>GroupNode</tt>
*/
public GroupNode getGroupNode()
{
return groupNode;
}
/**
* Sets the corresponding <tt>GroupNode</tt>.
* @param groupNode the <tt>GroupNode</tt> to set
*/
public void setGroupNode(GroupNode groupNode)
{
this.groupNode = groupNode;
}
/**
* This group is not visible to the user.
* @return null
*/
public JPopupMenu getRightButtonMenu()
{
return null;
}
}
}

@ -8,11 +8,9 @@
import javax.swing.tree.*;
import net.java.sip.communicator.service.contactlist.*;
/**
* The <tt>ContactNode</tt> is a <tt>ContactListNode</tt> corresponding to a
* given <tt>MetaContact</tt>.
* given <tt>UIContact</tt>.
*
* @author Yana Stamcheva
*/
@ -21,9 +19,9 @@ public class ContactNode
implements ContactListNode
{
/**
* The <tt>MetaContact</tt> corresponding to this contact node.
* The <tt>UIContact</tt> corresponding to this contact node.
*/
private MetaContact metaContact;
private UIContact contact;
/**
* Indicates if this node is currently active. Has unread messages waiting.
@ -32,32 +30,31 @@ public class ContactNode
/**
* Creates a <tt>ContactNode</tt> by specifying the corresponding
* <tt>metaContact</tt>.
* @param metaContact the <tt>MetaContact</tt> corresponding to this node
* <tt>contact</tt>.
* @param contact the <tt>UIContact</tt> corresponding to this node
*/
public ContactNode(MetaContact metaContact)
public ContactNode(UIContact contact)
{
super(metaContact);
this.metaContact = metaContact;
super(contact);
this.contact = contact;
}
/**
* Returns the corresponding <tt>MetaContact</tt>.
* @return the corresponding <tt>MetaContact</tt>
* Returns the corresponding <tt>UIContact</tt>.
* @return the corresponding <tt>UIContact</tt>
*/
public MetaContact getMetaContact()
public UIContact getContactDescriptor()
{
return (MetaContact) getUserObject();
return (UIContact) getUserObject();
}
/**
* Returns the index of this contact node in its parent group in
* the <tt>MetaContactListService</tt>.
* @return the index in the <tt>MetaContactListService</tt>
* Returns the index of this contact node in its parent group.
* @return the index of this contact node in its parent group
*/
public int getMetaContactListIndex()
public int getSourceIndex()
{
return metaContact.getParentMetaContactGroup().indexOf(metaContact);
return contact.getSourceIndex();
}
/**

@ -8,7 +8,6 @@
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
@ -16,9 +15,9 @@
import net.java.sip.communicator.impl.gui.lookandfeel.*;
import net.java.sip.communicator.impl.gui.main.chat.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
/**
* DeafultContactlist used to display <code>JList</code>s with contacts.
@ -73,40 +72,28 @@ public void mouseReleased(MouseEvent e)
* Dummy method used and overridden from classes extending this
* functionality such as ContactList.
*
* @param metaContact the <tt>MetaContact</tt> to verify
* @param contact the <tt>MetaContact</tt> to verify
* @return TRUE if the given <tt>MetaContact</tt> is active, FALSE -
* otherwise
*/
public boolean isContactActive(MetaContact metaContact)
public boolean isContactActive(UIContact contact)
{
return false;
}
/**
* Returns the general status of the given MetaContact. Detects the status
* using the priority status table. The priority is defined on the
* "availability" factor and here the most "available" status is returned.
* Checks if the given contact is currently active.
* Dummy method used and overridden from classes extending this
* functionality such as ContactList.
*
* @param metaContact The metaContact for which the status is asked.
* @return PresenceStatus The most "available" status from all subcontact
* statuses.
* @param metaContact the <tt>MetaContact</tt> to verify
* @return TRUE if the given <tt>MetaContact</tt> is active, FALSE -
* otherwise
*/
public PresenceStatus getMetaContactStatus(MetaContact metaContact)
public boolean isContactActive(MetaContact metaContact)
{
PresenceStatus status = null;
Iterator<Contact> i = metaContact.getContacts();
while (i.hasNext()) {
Contact protoContact = i.next();
PresenceStatus contactStatus = protoContact.getPresenceStatus();
if (status == null) {
status = contactStatus;
} else {
status = (contactStatus.compareTo(status) > 0) ? contactStatus
: status;
}
}
return status;
return isContactActive(
MetaContactListSource.getUIContact(metaContact));
}
/**
@ -125,51 +112,26 @@ public JToolTip createToolTip()
Object element = path.getLastPathComponent();
ExtendedTooltip tip = new ExtendedTooltip(true);
ExtendedTooltip tip = null;
if (element instanceof ContactNode)
{
MetaContact metaContact = ((ContactNode) element).getMetaContact();
byte[] avatarImage = metaContact.getAvatar();
if (avatarImage != null && avatarImage.length > 0)
tip.setImage(new ImageIcon(avatarImage));
tip.setTitle(metaContact.getDisplayName());
Iterator<Contact> i = metaContact.getContacts();
UIContact contact
= ((ContactNode) element).getContactDescriptor();
String statusMessage = null;
Contact protocolContact;
while (i.hasNext())
tip = contact.getToolTip();
if (tip == null)
{
protocolContact = i.next();
ImageIcon protocolStatusIcon
= new ImageIcon(
protocolContact.getPresenceStatus().getStatusIcon());
String contactAddress = protocolContact.getAddress();
//String statusMessage = protocolContact.getStatusMessage();
tip.addLine(protocolStatusIcon, contactAddress);
// Set the first found status message.
if (statusMessage == null
&& protocolContact.getStatusMessage() != null
&& protocolContact.getStatusMessage().length() > 0)
statusMessage = protocolContact.getStatusMessage();
tip = new ExtendedTooltip(true);
tip.setTitle(contact.getDisplayName());
}
if (statusMessage != null)
tip.setBottomText(statusMessage);
}
else if (element instanceof GroupNode)
{
MetaContactGroup metaGroup
= ((GroupNode) element).getMetaContactGroup();
UIGroup group
= ((GroupNode) element).getGroupDescriptor();
tip.setTitle(metaGroup.getGroupName());
tip = new ExtendedTooltip(true);
tip.setTitle(group.getDisplayName());
}
else if (element instanceof ChatContact)
{
@ -177,6 +139,7 @@ else if (element instanceof ChatContact)
ImageIcon avatarImage = chatContact.getAvatar();
tip = new ExtendedTooltip(true);
if (avatarImage != null)
tip.setImage(avatarImage);

@ -10,12 +10,9 @@
import javax.swing.tree.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
/**
* The <tt>GroupNode</tt> is a <tt>ContactListNode</tt> corresponding to a
* given <tt>MetaContactGroup</tt>.
* given <tt>UIGroup</tt>.
*
* @author Yana Stamcheva
*/
@ -24,9 +21,14 @@ public class GroupNode
implements ContactListNode
{
/**
* The corresponding <tt>MetaContactGroup</tt>.
* The parent contact list model.
*/
private final MetaContactGroup metaGroup;
private final ContactListTreeModel treeModel;
/**
* The corresponding <tt>UIGroup</tt>.
*/
private final UIGroup group;
/**
* A node comparator used to sort the list of children.
@ -40,50 +42,50 @@ public class GroupNode
/**
* Creates a <tt>GroupNode</tt> by specifying the parent <tt>treeModel</tt>
* and the corresponding <tt>metaGroup</tt> in the
* <tt>MetaContactListService</tt>.
* and the corresponding <tt>uiGroup</tt>.
*
* @param treeModel the parent tree model containing this group
* @param metaGroup the corresponding <tt>MetaContactGroup</tt>
* @param uiGroup the corresponding <tt>UIGroup</tt>
*/
public GroupNode(ContactListTreeModel treeModel, MetaContactGroup metaGroup)
public GroupNode( ContactListTreeModel treeModel,
UIGroup uiGroup)
{
super(metaGroup, true);
super(uiGroup, true);
this.metaGroup = metaGroup;
this.treeModel = treeModel;
this.group = uiGroup;
isCollapsed = ConfigurationManager
.isContactListGroupCollapsed(metaGroup.getMetaUID());
isCollapsed = group.isGroupCollapsed();
}
/**
* Creates a <tt>ContactNode</tt> for the given <tt>metaContact</tt> and
* adds it to this group.
* @param metaContact the <tt>MetaContact</tt> to add
* Creates a <tt>ContactNode</tt> for the given <tt>uiContact</tt>
* and adds it to this group.
* @param uiContact the <tt>UIContact</tt> to add
* @return the created <tt>ContactNode</tt>
*/
public ContactNode addMetaContact(MetaContact metaContact)
public ContactNode addContact(UIContact uiContact)
{
ContactNode contactNode = new ContactNode(metaContact);
ContactNode contactNode = new ContactNode(uiContact);
uiContact.setContactNode(contactNode);
this.add(contactNode);
return contactNode;
}
/**
* Creates a <tt>ContactNode</tt> for the given <tt>metaContact</tt>,
* Creates a <tt>ContactNode</tt> for the given <tt>uiContact</tt>,
* adds it to this group and performs a sort at the end.
* @param treeModel the <tt>ContactListTreeModel</tt> to which the given
* <tt>metaContact</tt> is added
* @param metaContact the <tt>MetaContact</tt> to add
* @param uiContact the <tt>UIContact</tt> to add
* @param isRefreshView indicates if the view should be refreshed
* @return the created <tt>ContactNode</tt>
*/
@SuppressWarnings("unchecked")
public ContactNode sortedAddMetaContact(ContactListTreeModel treeModel,
MetaContact metaContact,
boolean isRefreshView)
public ContactNode sortedAddContact(UIContact uiContact,
boolean isRefreshView)
{
ContactNode contactNode = new ContactNode(metaContact);
ContactNode contactNode = new ContactNode(uiContact);
uiContact.setContactNode(contactNode);
this.add(contactNode);
@ -91,22 +93,19 @@ public ContactNode sortedAddMetaContact(ContactListTreeModel treeModel,
Collections.sort(children, nodeComparator);
if (isRefreshView)
this.fireNodeInserted(treeModel, getIndex(contactNode));
this.fireNodeInserted(getIndex(contactNode));
return contactNode;
}
/**
* Removes the node corresponding to the given <tt>MetaContact</tt> from
* this group.
* @param treeModel the <tt>ContactListTreeModel</tt> from which the
* given <tt>metaContact</tt> is removed
* @param metaContact the <tt>MetaContact</tt> to remove
* Removes the node corresponding to the given <tt>uiContact</tt> from this
* group.
* @param uiContact the <tt>UIContact</tt> to remove
*/
public void removeMetaContact( ContactListTreeModel treeModel,
MetaContact metaContact)
public void removeContact(UIContact uiContact)
{
ContactNode contactNode = findContactNode(metaContact);
ContactNode contactNode = findContactNode(uiContact);
if (contactNode != null)
{
@ -115,38 +114,35 @@ public void removeMetaContact( ContactListTreeModel treeModel,
// checks verifying if the node belongs to this parent.
children.removeElementAt(index);
contactNode.setParent(null);
uiContact.setContactNode(null);
fireNodeRemoved(treeModel, contactNode, index);
fireNodeRemoved(contactNode, index);
}
}
/**
* Creates a <tt>GroupNode</tt> for the given <tt>metaGroup</tt> and adds it
* to this group.
* @param treeModel the <tt>ContactListTreeModel</tt> to which the given
* <tt>metaGroup</tt> is added
* @param metaGroup the <tt>MetaContactGroup</tt> to add
* Creates a <tt>GroupNode</tt> for the given <tt>uiGroup</tt> and
* adds it to this group.
* @param uiGroup the <tt>UIGroup</tt> to add
* @return the created <tt>GroupNode</tt>
*/
public GroupNode addMetaContactGroup( ContactListTreeModel treeModel,
MetaContactGroup metaGroup)
public GroupNode addContactGroup(UIGroup uiGroup)
{
GroupNode groupNode = new GroupNode(treeModel, metaGroup);
GroupNode groupNode = new GroupNode(treeModel, uiGroup);
uiGroup.setGroupNode(groupNode);
this.add(groupNode);
return groupNode;
}
/**
* Removes the node corresponding to the given <tt>metaGroup</tt> from this
* Removes the node corresponding to the given <tt>uiGroup</tt> from this
* group node.
* @param treeModel the <tt>ContactListTreeModel</tt> from which the given
* <tt>metaGroup</tt> is removed
* @param metaGroup the <tt>MetaContactGroup</tt> to remove
* @param uiGroup the <tt>UIGroup</tt> to remove
*/
public void removeMetaContactGroup( ContactListTreeModel treeModel,
MetaContactGroup metaGroup)
public void removeContactGroup(UIGroup uiGroup)
{
GroupNode groupNode = findGroupNode(metaGroup);
GroupNode groupNode = uiGroup.getGroupNode();
if (groupNode != null)
{
@ -155,26 +151,47 @@ public void removeMetaContactGroup( ContactListTreeModel treeModel,
// checks verifying if the node belongs to this parent.
children.removeElementAt(index);
groupNode.setParent(null);
uiGroup.setGroupNode(null);
fireNodeRemoved(groupNode, index);
}
}
/**
* Removes all of this node's children, setting their parents to null.
* If this node has no children, this method does nothing.
*/
public void removeAllChildren()
{
for (int i = getChildCount()-1; i >= 0; i--)
{
TreeNode treeNode = getChildAt(i);
if (treeNode instanceof ContactNode)
((ContactNode) treeNode).getContactDescriptor()
.setContactNode(null);
else if (treeNode instanceof GroupNode)
((GroupNode) treeNode).getGroupDescriptor()
.setGroupNode(null);
fireNodeRemoved(treeModel, groupNode, index);
children.removeElementAt(i);
((DefaultMutableTreeNode) treeNode).setParent(null);
}
}
/**
* Creates a <tt>GroupNode</tt> for the given <tt>metaGroup</tt>, adds it
* to this group node and performs a sort at the end.
* @param treeModel the <tt>ContactListTreeModel</tt> to which the given
* <tt>metaGroup</tt> is added
* @param metaGroup the <tt>MetaContactGroup</tt> to add
* Creates a <tt>GroupNode</tt> for the given <tt>uiGroup</tt>,
* adds it to this group node and performs a sort at the end.
* @param uiGroup the <tt>UIGroup</tt> to add
* @param isRefreshView indicates if the view should be refreshed
* @return the created <tt>GroupNode</tt>
*/
@SuppressWarnings("unchecked")
public GroupNode sortedAddMetaContactGroup( ContactListTreeModel treeModel,
MetaContactGroup metaGroup,
boolean isRefreshView)
public GroupNode sortedAddContactGroup( UIGroup uiGroup,
boolean isRefreshView)
{
GroupNode groupNode = new GroupNode(treeModel, metaGroup);
GroupNode groupNode = new GroupNode(treeModel, uiGroup);
uiGroup.setGroupNode(groupNode);
this.add(groupNode);
@ -182,77 +199,51 @@ public GroupNode sortedAddMetaContactGroup( ContactListTreeModel treeModel,
Collections.sort(children, nodeComparator);
if (isRefreshView)
this.fireNodeInserted(treeModel, getIndex(groupNode));
this.fireNodeInserted(getIndex(groupNode));
return groupNode;
}
/**
* Returns the <tt>MetaContactGroup</tt> corresponding to this
* <tt>GroupNode</tt>.
* @return the <tt>MetaContactGroup</tt> corresponding to this
* <tt>GroupNode</tt>
* Returns the <tt>UIGroup</tt> corresponding to this <tt>GroupNode</tt>.
* @return the <tt>UIGroup</tt> corresponding to this <tt>GroupNode</tt>
*/
public MetaContactGroup getMetaContactGroup()
public UIGroup getGroupDescriptor()
{
return (MetaContactGroup) getUserObject();
}
/**
* Finds the <tt>GroupNode</tt> corresponding to the given
* <tt>metaGroup</tt> in the children of this node.
* @param metaGroup the <tt>MetaContactGroup</tt>, which node we're looking
* for
* @return the corresponding <tt>GroupNode</tt> or null if no group node
* was found
*/
@SuppressWarnings("unchecked")
public GroupNode findGroupNode(MetaContactGroup metaGroup)
{
Enumeration<TreeNode> children = children();
while(children.hasMoreElements())
{
TreeNode treeNode = children.nextElement();
if (treeNode instanceof GroupNode
&& ((GroupNode)treeNode).getMetaContactGroup().equals(metaGroup))
return (GroupNode) treeNode;
}
return null;
return (UIGroup) getUserObject();
}
/**
* Finds the <tt>ContactNode</tt> corresponding to the given
* <tt>metaContact</tt> in the children of this node.
* @param metaContact the <tt>MetaContact</tt>, which node we're looking for
* @return the corresponding <tt>ContactNode</tt> or null if no contact node
* was found
* <tt>uiContact</tt> in the children of this node.
* @param uiContact the <tt>UIContact</tt>, which node we're looking for
* @return the corresponding <tt>ContactNode</tt> or null if no contact
* node was found
*/
@SuppressWarnings("unchecked")
public ContactNode findContactNode(MetaContact metaContact)
public ContactNode findContactNode(UIContact uiContact)
{
Enumeration<TreeNode> children = children();
while(children.hasMoreElements())
{
TreeNode treeNode = children.nextElement();
if (treeNode instanceof ContactNode
&& ((ContactNode)treeNode).getMetaContact().equals(metaContact))
&& ((ContactNode) treeNode).getContactDescriptor()
.equals(uiContact))
{
return (ContactNode) treeNode;
}
}
return null;
}
/**
* Returns the index of this node in its parent group in the
* <tt>MetaContactListService</tt>.
* @return the index of this node in its parent group in the
* <tt>MetaContactListService</tt>
* Returns the index of this node in its parent group.
* @return the index of this node in its parent group
*/
public int getMetaContactListIndex()
public int getSourceIndex()
{
MetaContactGroup parentGroup = metaGroup.getParentMetaContactGroup();
if (parentGroup != null)
return parentGroup.indexOf(metaGroup);
else return 0; //this is the root group
return group.getSourceIndex();
}
/**
@ -267,7 +258,7 @@ public void sort(ContactListTreeModel treeModel)
{
Collections.sort(children, nodeComparator);
fireNodesChanged(treeModel);
fireNodesChanged();
}
}
@ -285,11 +276,9 @@ public boolean isCollapsed()
/**
* Notifies all interested listeners that a node has been inserted at the
* given <tt>index</tt>.
* @param treeModel the <tt>ContactListTreeModel</tt> which should be
* notified
* @param index the index of the newly inserted node
*/
private void fireNodeInserted(ContactListTreeModel treeModel, int index)
private void fireNodeInserted(int index)
{
int[] newIndexs = new int[1];
newIndexs[0] = index;
@ -299,13 +288,10 @@ private void fireNodeInserted(ContactListTreeModel treeModel, int index)
/**
* Notifies all interested listeners that <tt>node</tt> has been removed
* from the given <tt>index</tt>.
* @param treeModel the <tt>ContactListTreeModel</tt> which should
* be notified
* @param node the node that has been removed
* @param index the index of the removed node
*/
private void fireNodeRemoved(
ContactListTreeModel treeModel, ContactListNode node, int index)
private void fireNodeRemoved(ContactListNode node, int index)
{
int[] removedIndexs = new int[1];
removedIndexs[0] = index;
@ -314,10 +300,8 @@ private void fireNodeRemoved(
/**
* Notifies all interested listeners that all nodes has changed.
* @param treeModel the <tt>ContactListTreeModel</tt> which should
* be notified
*/
private void fireNodesChanged(ContactListTreeModel treeModel)
private void fireNodesChanged()
{
int childCount = getChildCount();
int[] changedIndexs = new int[childCount];
@ -328,16 +312,25 @@ private void fireNodesChanged(ContactListTreeModel treeModel)
}
/**
*
* Note: this comparator imposes orderings that are inconsistent with
* equals.
*/
private class NodeComparator implements Comparator<ContactListNode>
{
/**
* Compares its two arguments for order. Returns a negative integer,
* zero, or a positive integer as the first argument is less than, equal
* to, or greater than the second.
* @param node1 the first <tt>ContactListNode</tt> to compare
* @param node2 the second <tt>ContactListNode</tt> to compare
* @return -1 if the first node should be positioned before the second
* one, 1 if the first argument should be positioned after the second
* one, 0 if there's no matter
*/
public int compare(ContactListNode node1, ContactListNode node2)
{
int index1 = node1.getMetaContactListIndex();
int index2 = node2.getMetaContactListIndex();
int index1 = node1.getSourceIndex();
int index2 = node2.getSourceIndex();
// Child groups are shown after child contacts.
if (node1 instanceof GroupNode && node2 instanceof ContactNode)
@ -346,6 +339,14 @@ public int compare(ContactListNode node1, ContactListNode node2)
if (node1 instanceof ContactNode && node2 instanceof GroupNode)
return -1;
// If the first index is unknown then we position it to the end.
if (index1 < 0)
return 1;
// If the second index is unknown then we position it to the end.
if (index2 < 0)
return -1;
if (index1 > index2) return 1;
else if (index1 < index2) return -1;
else return 0;

@ -19,6 +19,7 @@
import net.java.sip.communicator.impl.gui.main.*;
import net.java.sip.communicator.impl.gui.main.call.*;
import net.java.sip.communicator.impl.gui.main.chat.history.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.gui.*;
@ -35,7 +36,7 @@
*
* @author Yana Stamcheva
*/
public class ContactRightButtonMenu
public class MetaContactRightButtonMenu
extends JPopupMenu
implements ActionListener,
PluginComponentListener,
@ -46,7 +47,8 @@ public class ContactRightButtonMenu
*/
private static final long serialVersionUID = 3033031652970285857L;
private final Logger logger = Logger.getLogger(ContactRightButtonMenu.class);
private final Logger logger
= Logger.getLogger(MetaContactRightButtonMenu.class);
private static final String allContactsString
= GuiActivator.getResources().getI18NString("service.gui.ALL_CONTACTS");
@ -54,8 +56,9 @@ public class ContactRightButtonMenu
private static final String moveToString = GuiActivator.getResources()
.getI18NString("service.gui.MOVE_TO_GROUP");
private static final String moveSubcontactString = GuiActivator.getResources()
.getI18NString("service.gui.MOVE_SUBCONTACT");
private static final String moveSubcontactString
= GuiActivator.getResources()
.getI18NString("service.gui.MOVE_SUBCONTACT");
private static final String removeContactString
= GuiActivator.getResources()
@ -71,7 +74,8 @@ public class ContactRightButtonMenu
= GuiActivator.getResources().getI18NString("service.gui.SEND_FILE");
private static final String renameContactString
= GuiActivator.getResources().getI18NString("service.gui.RENAME_CONTACT");
= GuiActivator.getResources()
.getI18NString("service.gui.RENAME_CONTACT");
private static final String viewHistoryString
= GuiActivator.getResources().getI18NString("service.gui.VIEW_HISTORY");
@ -97,7 +101,8 @@ public class ContactRightButtonMenu
private final JMenuItem sendMessageItem = new JMenuItem(
sendMessageString,
new ImageIcon(ImageLoader.getImage(ImageLoader.SEND_MESSAGE_16x16_ICON)));
new ImageIcon(ImageLoader
.getImage(ImageLoader.SEND_MESSAGE_16x16_ICON)));
private final JMenuItem sendFileItem = new JMenuItem(
sendFileString,
@ -105,7 +110,8 @@ public class ContactRightButtonMenu
private final JMenuItem sendSmsItem = new JMenuItem(
sendSmsString,
new ImageIcon(ImageLoader.getImage(ImageLoader.SEND_MESSAGE_16x16_ICON)));
new ImageIcon(ImageLoader
.getImage(ImageLoader.SEND_MESSAGE_16x16_ICON)));
private final JMenuItem renameContactItem = new JMenuItem(
renameContactString,
@ -137,16 +143,14 @@ public class ContactRightButtonMenu
/**
* Creates an instance of ContactRightButtonMenu.
* @param contactItem The MetaContact for which the menu is opened.
* @param contactList The contact list over which this menu is shown.
* @param contactItem The MetaContact for which the menu is opened
*/
public ContactRightButtonMenu( MetaContact contactItem,
TreeContactList contactList)
public MetaContactRightButtonMenu( MetaContact contactItem)
{
super();
this.mainFrame = GuiActivator.getUIService().getMainFrame();
this.contactList = contactList;
this.contactList = GuiActivator.getContactList();
this.contactItem = contactItem;
@ -621,11 +625,19 @@ private Contact getContactFromMetaContact(String itemID)
* the selected contact to the selected group.
* @param evt the <tt>ContactListEvent</tt> has
*/
public void groupSelected(ContactListEvent evt)
public void groupClicked(ContactListEvent evt)
{
this.moveDialog.dispose();
MetaContactGroup sourceGroup = evt.getSourceGroup();
UIGroup sourceGroup = evt.getSourceGroup();
// TODO: may be show a warning message to tell the user that she should
// select another group.
if (!(sourceGroup instanceof MetaUIGroup))
return;
MetaContactGroup metaGroup
= (MetaContactGroup) sourceGroup.getDescriptor();
contactList.removeContactListListener(this);
@ -636,12 +648,12 @@ public void groupSelected(ContactListEvent evt)
if(moveAllContacts)
{
MetaContactListManager
.moveMetaContactToGroup(contactItem, sourceGroup);
.moveMetaContactToGroup(contactItem, metaGroup);
}
else if(contactToMove != null)
{
MetaContactListManager
.moveContactToGroup(contactToMove, sourceGroup);
.moveContactToGroup(contactToMove, metaGroup);
}
contactList.setGroupClickConsumed(false);
@ -654,17 +666,12 @@ else if(contactToMove != null)
*/
public void contactClicked(ContactListEvent evt)
{
this.moveContact(evt.getSourceContact());
}
UIContact descriptor = evt.getSourceContact();
// We're only interested in MetaContacts here.
if (!(descriptor instanceof MetaUIContact))
return;
/**
* Implements ContactListListener.contactSelected method in order
* to move the chosen sub-contact when a meta contact is selected.
* @param evt the <tt>ContactListEvent</tt> that notified us
*/
public void protocolContactClicked(ContactListEvent evt)
{
this.moveContact(evt.getSourceContact());
this.moveContact((MetaContact) descriptor.getDescriptor());
}
/**
@ -766,48 +773,4 @@ public Image createContactStatusImage(Contact protoContact)
protoContact.getPresenceStatus().getStatusIcon()),
protoContact.getProtocolProvider());
}
/**
* A menu item that performs an action related to a specific protocol
* provider.
*
*/
private static class ProviderAwareMenuItem extends JMenuItem
{
/**
* An eclipse generated serialVersionUID.
*/
private static final long serialVersionUID = 6343418726839985645L;
private ProtocolProviderService provider = null;
/**
* Initializes the menu item and stores a reference to the specified
* provider.
*
* @param provider the provider that we are related to
* @param text the text string for this menu
* @param icon the icon to display when showing this menu
*/
public ProviderAwareMenuItem(ProtocolProviderService provider,
String text,
Icon icon)
{
super(text, icon);
this.provider = provider;
}
/**
* Returns a reference to the <tt>ProtocolProviderService</tt> that
* this item is related to.
*
* @return a reference to the <tt>ProtocolProviderService</tt> that
* this item is related to.
*/
public ProtocolProviderService getProvider()
{
return provider;
}
}
}

@ -6,8 +6,16 @@
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
import java.util.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
/**
* The <tt>PresenceFilter</tt> is used to filter offline contacts from the
@ -16,10 +24,78 @@
* @author Yana Stamcheva
*/
public class PresenceFilter
implements ContactListFilter
implements ContactListFilter,
ContactPresenceStatusListener
{
/**
* Indicates if this presence filter shows or hides the offline contacts.
*/
private boolean isShowOffline;
/**
* Indicates if there's a presence filtering going on.
*/
private boolean isFiltering = false;
private final Object lockedFiltering = new Object();
/**
* Creates an instance of <tt>PresenceFilter</tt>.
*/
public PresenceFilter()
{
this.setShowOffline(ConfigurationManager.isShowOffline());
}
/**
* Applies this filter. This filter is applied over the
* <tt>MetaContactListService</tt>.
* @param treeModel the model to which we should add the results from the
* filtering operation
*/
public void applyFilter(ContactListTreeModel treeModel)
{
isFiltering = true;
synchronized (lockedFiltering)
{
addMatching(GuiActivator.getContactListService().getRoot(),
treeModel);
}
isFiltering = false;
}
/**
* Indicates if the given <tt>uiContact</tt> is matching this filter.
* @param uiContact the <tt>UIContact</tt> to check
* @return <tt>true</tt> if the given <tt>uiContact</tt> is matching
* this filter, otherwise returns <tt>false</tt>
*/
public boolean isMatching(UIContact uiContact)
{
Object descriptor = uiContact.getDescriptor();
if (descriptor instanceof MetaContact)
return isMatching((MetaContact) descriptor);
return false;
}
/**
* Indicates if the given <tt>uiGroup</tt> is matching this filter.
* @param uiGroup the <tt>UIGroup</tt> to check
* @return <tt>true</tt> if the given <tt>uiGroup</tt> is matching
* this filter, otherwise returns <tt>false</tt>
*/
public boolean isMatching(UIGroup uiGroup)
{
Object descriptor = uiGroup.getDescriptor();
if (descriptor instanceof MetaContactGroup)
return isMatching((MetaContactGroup) descriptor);
return false;
}
/**
* Sets the show offline property.
* @param isShowOffline indicates if offline contacts are shown
@ -47,7 +123,7 @@ public boolean isShowOffline()
* @return <tt>true</tt> if the given <tt>MetaContact</tt> is matching this
* filter
*/
public boolean isMatching(MetaContact metaContact)
private boolean isMatching(MetaContact metaContact)
{
return isShowOffline || isContactOnline(metaContact);
}
@ -59,7 +135,7 @@ public boolean isMatching(MetaContact metaContact)
* @return <tt>true</tt> if the given <tt>MetaContactGroup</tt> is matching
* this filter
*/
public boolean isMatching(MetaContactGroup metaGroup)
private boolean isMatching(MetaContactGroup metaGroup)
{
return (isShowOffline || metaGroup.countOnlineChildContacts() > 0)
? true
@ -85,4 +161,106 @@ private boolean isContactOnline(MetaContact contact)
return defaultContact.getPresenceStatus().getStatus()
>= PresenceStatus.ONLINE_THRESHOLD;
}
/**
* Adds all contacts contained in the given <tt>MetaContactGroup</tt>
* matching the current filter and not contained in the contact list.
* @param metaGroup the <tt>MetaContactGroup</tt>, which matching contacts
* to add
* @param resultTreeModel the <tt>ContactListTreeModel</tt>, where results
* should be added
*/
private void addMatching( MetaContactGroup metaGroup,
ContactListTreeModel resultTreeModel)
{
Iterator<MetaContact> childContacts = metaGroup.getChildContacts();
while(childContacts.hasNext() && isFiltering)
{
MetaContact metaContact = childContacts.next();
if(isMatching(metaContact))
GuiActivator.getContactList().addContact(
resultTreeModel,
MetaContactListSource.createUIContact(metaContact),
true,
false);
}
Iterator<MetaContactGroup> subgroups = metaGroup.getSubgroups();
while(subgroups.hasNext() && isFiltering)
{
MetaContactGroup subgroup = subgroups.next();
if (subgroup.countChildContacts() == 0
&& subgroup.countSubgroups() == 0
&& isMatching(subgroup))
GuiActivator.getContactList().addGroup(
resultTreeModel,
MetaContactListSource.createUIGroup(subgroup),
false);
else
addMatching(subgroup, resultTreeModel);
}
}
/**
* Indicates that a contact has changed its status.
*
* @param evt the presence event containing information about the
* contact status change
*/
public void contactPresenceStatusChanged(
final ContactPresenceStatusChangeEvent evt)
{
final Contact sourceContact = evt.getSourceContact();
final MetaContact metaContact = GuiActivator.getContactListService()
.findMetaContactByContact(sourceContact);
if (metaContact == null
|| (evt.getOldStatus() == evt.getNewStatus()))
return;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
UIContact cDescriptor
= MetaContactListSource.getUIContact(metaContact);
if (cDescriptor == null)
cDescriptor = MetaContactListSource
.createUIContact(metaContact);
synchronized(lockedFiltering)
{
if (GuiActivator.getContactList().getCurrentFilter()
.equals(PresenceFilter.this)
&& isMatching(metaContact))
{
if (cDescriptor.getContactNode() == null)
GuiActivator.getContactList()
.addContact(cDescriptor);
else
GuiActivator.getContactList()
.nodeChanged(cDescriptor.getContactNode());
}
else
{
GuiActivator.getContactList()
.removeContact(cDescriptor);
}
}
}
});
}
/**
* Stops this filter current queries.
*/
public void stopFilter()
{
isFiltering = false;
}
}

@ -10,6 +10,7 @@
import net.java.sip.communicator.impl.gui.main.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.swing.*;
import net.java.sip.communicator.util.swing.event.*;
import net.java.sip.communicator.util.swing.plaf.*;
/**
@ -19,7 +20,7 @@
*/
public class SearchField
extends SIPCommTextField
implements DocumentListener
implements TextFieldChangeListener
{
private final Logger logger = Logger.getLogger(SearchField.class);
@ -52,7 +53,7 @@ public SearchField(MainFrame frame)
this.setPreferredSize(new Dimension(100, 22));
this.setDragEnabled(true);
this.getDocument().addDocumentListener(this);
this.addTextChangeListener(this);
InputMap imap = getInputMap(JComponent.WHEN_FOCUSED);
imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "escape");
@ -70,9 +71,8 @@ public void actionPerformed(ActionEvent e)
/**
* Handles the change when a char has been inserted in the field.
* @param e the <tt>DocumentEvent</tt> that notified us
*/
public void insertUpdate(DocumentEvent e)
public void textInserted()
{
// Should explicitly check if there's a text, because the default text
// triggers also an insertUpdate event.
@ -85,9 +85,8 @@ public void insertUpdate(DocumentEvent e)
/**
* Handles the change when a char has been removed from the field.
* @param e the <tt>DocumentEvent</tt> that notified us
*/
public void removeUpdate(DocumentEvent e)
public void textRemoved()
{
scheduleUpdate();
}
@ -171,10 +170,10 @@ public void run()
*/
public void updateContactListView(String filterString)
{
TreeContactList contactList = GuiActivator.getContactList();
if (filterString != null && filterString.length() > 0)
{
TreeContactList contactList = GuiActivator.getContactList();
boolean hasMatching
= contactList.applyFilter(TreeContactList.searchFilter);
@ -198,9 +197,7 @@ public void updateContactListView(String filterString)
}
else
{
TreeContactList contactList = GuiActivator.getContactList();
contactList.applyFilter(TreeContactList.presenceFilter);
contactList.applyDefaultFilter();
enableUnknownContactView(false);
}

@ -9,8 +9,9 @@
import java.util.*;
import java.util.regex.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.service.contactsource.*;
/**
* The <tt>SearchFilter</tt> is a <tt>ContactListFilter</tt> that filters the
@ -19,19 +20,137 @@
* @author Yana Stamcheva
*/
public class SearchFilter
implements ContactListFilter
implements ContactListFilter,
ContactQueryListener
{
/**
* The default contact source search type.
*/
public static final int DEFAULT_SOURCE = 0;
/**
* The history contact source search type.
*/
public static final int HISTORY_SOURCE = 1;
/**
* The string, which we're searching.
*/
private String filterString;
/**
* The pattern to filter.
*/
private Pattern filterPattern;
/**
* The <tt>ContactListTreeModel</tt>, where results from the search
* are added.
*/
private ContactListTreeModel resultTreeModel;
/**
* The <tt>MetaContactListSource</tt> to search in.
*/
private final MetaContactListSource mclSource;
/**
* The list of external contact sources to search in.
*/
private Collection<ExternalContactSource> contactSources;
/**
* The current operating query.
*/
private ContactQuery currentQuery;
/**
* The type of the search source. One of the above defined DEFAUT_SOURCE or
* HISTORY_SOURCE.
*/
private int searchSourceType;
/**
* Creates an instance of <tt>SearchFilter</tt>.
*/
public SearchFilter()
{
this.mclSource = new MetaContactListSource();
}
/**
* Applies this filter and stores the result in the given <tt>treeModel</tt>.
* @param treeModel the <tt>ContactListTreeModel</tt>, in which we store
* results
*/
public void applyFilter(ContactListTreeModel treeModel)
{
resultTreeModel = treeModel;
if (contactSources == null)
contactSources = TreeContactList.getContactSources();
if (searchSourceType == DEFAULT_SOURCE)
// First add the MetaContactListSource
mclSource.filter(filterPattern, treeModel);
for (ExternalContactSource contactSource : contactSources)
{
ContactSourceService sourceService
= contactSource.getContactSourceService();
if (sourceService instanceof ExtendedContactSourceService)
currentQuery
= ((ExtendedContactSourceService) sourceService)
.queryContactSource(filterPattern);
else
currentQuery = sourceService.queryContactSource(filterString);
// Add first available results.
this.addMatching(currentQuery.getQueryResults());
currentQuery.addContactQueryListener(this);
}
}
/**
* Indicates if the given <tt>uiGroup</tt> matches this filter.
* @param uiGroup the <tt>UIGroup</tt> to check
* @return <tt>true</tt> if the given <tt>uiGroup</tt> matches the current
* filter, <tt>false</tt> - otherwise
*/
public boolean isMatching(UIContact uiGroup)
{
Iterator<String> searchStrings = uiGroup.getSearchStrings();
while (searchStrings != null && searchStrings.hasNext())
{
if (isMatching(searchStrings.next()))
return true;
}
return false;
}
/**
* For all groups we return false. If some of the child contacts of this
* group matches this filter the group would be automatically added when
* the contact is added in the list.
* @param uiGroup the <tt>UIGroup</tt> to check
* @return false
*/
public boolean isMatching(UIGroup uiGroup)
{
return false;
}
/**
* Creates the <tt>SearchFilter</tt> by specifying the string used for
* filtering.
* @param filterString the String used for filtering
* @param filter the String used for filtering
*/
public void setFilterString(String filterString)
public void setFilterString(String filter)
{
// First escape all special characters from the given filter string.
filterString = Pattern.quote(filterString);
this.filterString = Pattern.quote(filter);
// Then create the pattern.
// By default, case-insensitive matching assumes that only characters
@ -45,60 +164,138 @@ public void setFilterString(String filterString)
}
/**
* Checks if the given <tt>metaContact</tt> is matching the current filter.
* A <tt>MetaContact</tt> would be matching the filter if one of the
* following is true:<br>
* - its display name contains the filter string
* - at least one of its child protocol contacts has a display name or an
* address that contains the filter string.
* @param metaContact the <tt>MetaContact</tt> to check
* @return <tt>true</tt> to indicate that the given <tt>metaContact</tt> is
* Stops the current query.
*/
public void stopFilter()
{
if (currentQuery != null)
currentQuery.cancel();
}
/**
* Checks if the given <tt>contact</tt> is matching the current filter.
* A <tt>SourceContact</tt> would be matching the filter if its display
* name is matching the search string.
* @param contact the <tt>ContactListContactDescriptor</tt> to check
* @return <tt>true</tt> to indicate that the given <tt>contact</tt> is
* matching the current filter, otherwise returns <tt>false</tt>
*/
public boolean isMatching(MetaContact metaContact)
private boolean isMatching(SourceContact contact)
{
Matcher matcher = filterPattern.matcher(metaContact.getDisplayName());
return isMatching(contact.getDisplayName());
}
/**
* Indicates if the given string matches this filter.
* @param text the text to check
* @return <tt>true</tt> to indicate that the given <tt>text</tt> matches
* this filter, <tt>false</tt> - otherwise
*/
private boolean isMatching(String text)
{
Matcher matcher = filterPattern.matcher(text);
if(matcher.find())
return true;
Iterator<Contact> contacts = metaContact.getContacts();
while (contacts.hasNext())
return false;
}
/**
* Indicates that a contact has been received for a query.
* @param event the <tt>ContactReceivedEvent</tt> that notified us
*/
public void contactReceived(ContactReceivedEvent event)
{
synchronized (resultTreeModel)
{
Contact contact = contacts.next();
addSourceContact(event.getContact());
}
}
matcher = filterPattern.matcher(contact.getDisplayName());
/**
* Indicates that the status of a query has changed.
* @param event the <tt>ContactQueryStatusEvent</tt> that notified us
*/
public void queryStatusChanged(ContactQueryStatusEvent event)
{
int eventType = event.getEventType();
if (matcher.find())
return true;
// Remove the current query when it's stopped for some reason.
// QUERY_COMPLETED, QUERY_COMPLETED, QUERY_ERROR
currentQuery = null;
matcher = filterPattern.matcher(contact.getAddress());
if (eventType == ContactQueryStatusEvent.QUERY_ERROR)
{
//TODO: Show the error to the user??
}
if (matcher.find())
return true;
event.getQuerySource().removeContactQueryListener(this);
}
/**
* Adds the list of <tt>sourceContacts</tt> in the current result tree model.
* @param sourceContacts the list of <tt>SourceContact</tt>s to add
*/
private void addMatching(List<SourceContact> sourceContacts)
{
Iterator<SourceContact> contactsIter = sourceContacts.iterator();
while (contactsIter.hasNext())
{
addSourceContact(contactsIter.next());
}
return false;
}
/**
* Checks if the given <tt>metaGroup</tt> is matching the current filter. A
* group is matching the current filter only if it contains at least one
* child <tt>MetaContact</tt>, which is matching the current filter.
* @param metaGroup the <tt>MetaContactGroup</tt> to check
* @return <tt>true</tt> to indicate that the given <tt>metaGroup</tt> is
* matching the current filter, otherwise returns <tt>false</tt>
* Adds the given <tt>sourceContact</tt> to the result tree model.
* @param sourceContact the <tt>SourceContact</tt> to add
*/
private void addSourceContact(SourceContact sourceContact)
{
ContactSourceService contactSource
= sourceContact.getContactSource();
ExternalContactSource sourceUI
= TreeContactList.getContactSource(contactSource);
if (sourceUI != null
// ExtendedContactSourceService has already matched the
// SourceContact over the pattern
&& (contactSource instanceof ExtendedContactSourceService)
|| isMatching(sourceContact))
GuiActivator.getContactList().addContact(
resultTreeModel,
sourceUI.getUIContact(sourceContact),
true,
false);
}
/**
* Sets the search source type: DEFAULT_SOURCE or HISTORY_SOURCE.
* @param searchSourceType the type of the search source to set
*/
public boolean isMatching(MetaContactGroup metaGroup)
public void setSearchSourceType(int searchSourceType)
{
Iterator<MetaContact> contacts = metaGroup.getChildContacts();
this.searchSourceType = searchSourceType;
while (contacts.hasNext())
switch(searchSourceType)
{
MetaContact metaContact = contacts.next();
case DEFAULT_SOURCE:
contactSources = TreeContactList.getContactSources();
break;
case HISTORY_SOURCE:
{
ExternalContactSource historySource
= TreeContactList.getContactSource(
ContactSourceService.CALL_HISTORY);
if (isMatching(metaContact))
return true;
Collection<ExternalContactSource> historySources
= new LinkedList<ExternalContactSource>();
historySources.add(historySource);
contactSources = historySources;
break;
}
}
return false;
}
}

@ -0,0 +1,136 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.call.*;
import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.swing.*;
/**
* The right button menu for external contact sources.
* @see ExternalContactSource
*/
public class SourceContactRightButtonMenu
extends JPopupMenu
{
private final SourceContact sourceContact;
/**
* Creates an instance of <tt>SourceContactRightButtonMenu</tt> by
* specifying the <tt>SourceContact</tt>, for which this menu is created.
* @param sourceContact the <tt>SourceContact</tt>, for which this menu is
* created
*/
public SourceContactRightButtonMenu(SourceContact sourceContact)
{
this.sourceContact = sourceContact;
this.initItems();
}
/**
* Initializes menu items.
*/
private void initItems()
{
ContactDetail cDetail = sourceContact
.getPreferredContactDetail(OperationSetBasicTelephony.class);
if (cDetail != null)
add(initCallMenu());
}
/**
* Initializes the call menu.
* @return the call menu
*/
private Component initCallMenu()
{
SIPCommMenu callContactMenu = new SIPCommMenu(
GuiActivator.getResources().getI18NString("service.gui.CALL"));
callContactMenu.setIcon(new ImageIcon(ImageLoader
.getImage(ImageLoader.CALL_16x16_ICON)));
Iterator<ContactDetail> details
= sourceContact.getContactDetails(OperationSetBasicTelephony.class)
.iterator();
while (details.hasNext())
{
final ContactDetail detail = details.next();
// add all the contacts that support telephony to the call menu
JMenuItem callContactItem = new JMenuItem();
callContactItem.setName(detail.getContactAddress());
callContactItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
ProtocolProviderService protocolProvider
= detail.getPreferredProtocolProvider(
OperationSetBasicTelephony.class);
if (protocolProvider != null)
CallManager.createCall( protocolProvider,
detail.getContactAddress());
else
CallManager.createCall(detail.getContactAddress());
}
});
callContactMenu.add(callContactItem);
}
return callContactMenu;
}
// private Component initIMMenu()
// {
// SIPCommMenu callContactMenu = new SIPCommMenu(
// GuiActivator.getResources().getI18NString(
// "service.gui.SEND_MESSAGE"));
// callContactMenu.setIcon(new ImageIcon(ImageLoader
// .getImage(ImageLoader.SEND_MESSAGE_16x16_ICON)));
//
// Iterator<ContactDetail> details
// = sourceContact.getContactDetails(
// OperationSetBasicInstantMessaging.class).iterator();
//
// while (details.hasNext())
// {
// final ContactDetail detail = details.next();
// // add all the contacts that support telephony to the call menu
// JMenuItem callContactItem = new JMenuItem();
// callContactItem.setName(detail.getContactAddress());
// callContactItem.addActionListener(new ActionListener()
// {
// public void actionPerformed(ActionEvent e)
// {
// ProtocolProviderService protocolProvider
// = detail.getPreferredProtocolProvider(
// OperationSetBasicInstantMessaging.class);
//
// if (protocolProvider != null)
// CallManager.createCall( protocolProvider,
// detail.getContactAddress());
// else
// GuiActivator.getUIService().getChatWindowManager()
// .startChat(contactItem);
// }
// });
// callContactMenu.add(callContactItem);
// }
// return callContactMenu;
// }
}

@ -0,0 +1,128 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
import java.util.*;
import java.util.List;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.protocol.*;
/**
* The <tt>UIContact</tt> represents the user interface contact contained in the
* contact list component.
*
* @author Yana Stamcheva
*/
public interface UIContact
{
/**
* Returns the descriptor of this contact.
* @return the descriptor of this contact
*/
public Object getDescriptor();
/**
* Returns the display name of this contact.
* @return the display name of this contact
*/
public String getDisplayName();
/**
* Returns the display details of this contact. These would be shown
* whenever the contact is selected.
* @return the display details of this contact
*/
public String getDisplayDetails();
/**
* Returns the index of this contact in its source.
* @return the source index
*/
public int getSourceIndex();
/**
* Returns the avatar of this contact.
* @param isSelected indicates if the contact is selected
* @param width the width of the avatar
* @param height the height of the avatar
* @return the avatar of this contact
*/
public ImageIcon getAvatar(boolean isSelected, int width, int height);
/**
* Returns the status icon of this contact or null if no status is
* available.
* @return the status icon of this contact or null if no status is
* available
*/
public ImageIcon getStatusIcon();
/**
* Creates a tool tip for this contact. If such tooltip is
* provided it would be shown on mouse over over this <tt>UIContact</tt>.
* @return the tool tip for this contact descriptor
*/
public ExtendedTooltip getToolTip();
/**
* Returns the right button menu component.
* @return the right button menu component
*/
public JPopupMenu getRightButtonMenu();
/**
* Returns the parent group.
* @return the parent group
*/
public UIGroup getParentGroup();
/**
* Returns an <tt>Iterator</tt> over a list of the search strings of this
* contact.
* @return an <tt>Iterator</tt> over a list of the search strings of this
* contact
*/
public Iterator<String> getSearchStrings();
/**
* Returns the corresponding <tt>ContactNode</tt>. The <tt>ContactNode</tt>
* is the real node that is stored in the contact list component data model.
* @return the corresponding <tt>ContactNode</tt>
*/
public ContactNode getContactNode();
/**
* Sets the given <tt>contactNode</tt>. The <tt>ContactNode</tt>
* is the real node that is stored in the contact list component data model.
* @param contactNode the <tt>ContactNode</tt> that corresponds to this
* <tt>UIGroup</tt>
*/
public void setContactNode(ContactNode contactNode);
/**
* Returns the default <tt>ContactDetail</tt> to use for any operations
* depending to the given <tt>OperationSet</tt> class.
* @param opSetClass the <tt>OperationSet</tt> class we're interested in
* @return the default <tt>ContactDetail</tt> to use for any operations
* depending to the given <tt>OperationSet</tt> class
*/
public UIContactDetail getDefaultContactDetail(
Class<? extends OperationSet> opSetClass);
/**
* Returns a list of all <tt>UIContactDetail</tt>s corresponding to the
* given <tt>OperationSet</tt> class.
* @param opSetClass the <tt>OperationSet</tt> class we're looking for
* @return a list of all <tt>UIContactDetail</tt>s corresponding to the
* given <tt>OperationSet</tt> class
*/
public List<UIContactDetail> getContactDetailsForOperationSet(
Class<? extends OperationSet> opSetClass);
}

@ -0,0 +1,90 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
import net.java.sip.communicator.service.protocol.*;
/**
* The <tt>UIContactDetail</tt> corresponds to a particular contact detail,
* phone number, IM identifier, email, etc. which has it's preferred mode of
* transport <tt>ProtocolProviderService</tt>.
*
* @author Yana Stamcheva
*/
public abstract class UIContactDetail
{
/**
* The address of this detail.
*/
private final String address;
/**
* The display name of this detail.
*/
private final String displayName;
/**
* The <tt>ProtocolProviderService</tt> corresponding to this detail.
*/
private final ProtocolProviderService protocolProvider;
/**
* Creates a <tt>UIContactDetail</tt> by specifying the contact
* <tt>address</tt>, the <tt>displayName</tt> and <tt>preferredProvider</tt>.
* @param address the contact address
* @param displayName the contact display name
* @param preferredProvider the preferred protocol provider
*/
public UIContactDetail(
String address,
String displayName,
ProtocolProviderService preferredProvider)
{
this.address = address;
this.displayName = displayName;
this.protocolProvider = preferredProvider;
}
/**
* Returns the display name of this detail.
* @return the display name of this detail
*/
public String getDisplayName()
{
return displayName;
}
/**
* Returns the address of this detail.
* @return the address of this detail
*/
public String getAddress()
{
return address;
}
/**
* Returns the protocol provider preferred for contacting this detail for
* the given <tt>OperationSet</tt> class.
* @param opSetClass the <tt>OperationSet</tt> class for which we're looking
* for provider
* @return the protocol provider preferred for contacting this detail
*/
public ProtocolProviderService getPreferredProtocolProvider(
Class<? extends OperationSet> opSetClass)
{
return protocolProvider;
}
/**
* Returns the <tt>PresenceStatus</tt> of this <tt>ContactDetail</tt> or
* null if the detail doesn't support presence.
* @return the <tt>PresenceStatus</tt> of this <tt>ContactDetail</tt> or
* null if the detail doesn't support presence
*/
public abstract PresenceStatus getPresenceStatus();
}

@ -0,0 +1,89 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist;
import javax.swing.*;
/**
* The <tt>UIGroup</tt> represents the user interface contact list group.
*
* @author Yana Stamcheva
*/
public interface UIGroup
{
/**
* Returns the descriptor of the group. This would be the underlying object
* that should provide all other necessary information for the group.
* @return the descriptor of the group
*/
public Object getDescriptor();
/**
* The display name of the group. The display name is the name to be shown
* in the contact list group row.
* @return the display name of the group
*/
public String getDisplayName();
/**
* Returns the index of this group in its source. In other words this is
* the descriptor index.
* @return the index of this group in its source
*/
public int getSourceIndex();
/**
* Returns the parent group.
* @return the parent group
*/
public UIGroup getParentGroup();
/**
* Indicates if the group is collapsed or expanded.
* @return <tt>true</tt> to indicate that the group is collapsed,
* <tt>false</tt> to indicate that it's expanded
*/
public boolean isGroupCollapsed();
/**
* Returns the count of online child contacts.
* @return the count of online child contacts
*/
public int countOnlineChildContacts();
/**
* Returns the child contacts count.
* @return child contacts count
*/
public int countChildContacts();
/**
* Returns the identifier of this group.
* @return the identifier of this group
*/
public String getId();
/**
* Returns the <tt>GroupNode</tt> corresponding to this <tt>UIGroup</tt>.
* The is the actual node used in the contact list component data model.
* @return the <tt>GroupNode</tt> corresponding to this <tt>UIGroup</tt>
*/
public GroupNode getGroupNode();
/**
* Sets the <tt>GroupNode</tt> corresponding to this <tt>UIGroup</tt>.
* @param groupNode the <tt>GroupNode</tt> to set. The is the actual
* node used in the contact list component data model.
*/
public void setGroupNode(GroupNode groupNode);
/**
* Returns the right button menu for this group.
* @return the right button menu component for this group
*/
public JPopupMenu getRightButtonMenu();
}

@ -85,7 +85,7 @@ public void actionPerformed(ActionEvent e)
if (searchText == null)
return;
Vector<ProtocolProviderService> telephonyProviders
List<ProtocolProviderService> telephonyProviders
= CallManager.getTelephonyProviders();
if (telephonyProviders.size() == 1)

@ -0,0 +1,201 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.service.contactsource.*;
/**
* The <tt>ExternalContactSource</tt> is the UI abstraction of the
* <tt>ContactSourceService</tt>.
*
* @author Yana Stamcheva
*/
public class ExternalContactSource
{
/**
* The <tt>SourceUIGroup</tt> containing all contacts from this source.
*/
private final SourceUIGroup sourceUIGroup;
private final ContactSourceService contactSource;
/**
* Creates an <tt>ExternalContactSource</tt> based on the given
* <tt>ContactSourceService</tt>.
* @param contactSource the <tt>ContactSourceService</tt>, on which this
* <tt>ExternalContactSource</tt> is based
*/
public ExternalContactSource(ContactSourceService contactSource)
{
this.contactSource = contactSource;
sourceUIGroup
= new SourceUIGroup(contactSource.getDisplayName());
}
/**
* Returns the corresponding <tt>ContactSourceService</tt>.
* @return the corresponding <tt>ContactSourceService</tt>
*/
public ContactSourceService getContactSourceService()
{
return contactSource;
}
/**
* Returns the UI group for this contact source. There's only one group
* descriptor per external source.
* @return the group descriptor
*/
public UIGroup getUIGroup()
{
return sourceUIGroup;
}
/**
* Returns the <tt>UIContact</tt> corresponding to the given
* <tt>sourceContact</tt>.
* @param sourceContact the <tt>SourceContact</tt>, for which we search a
* corresponding <tt>UIContact</tt>
* @return the <tt>UIContact</tt> corresponding to the given
* <tt>sourceContact</tt>
*/
public UIContact getUIContact(SourceContact sourceContact)
{
return new SourceUIContact(sourceContact, sourceUIGroup);
}
/**
* The <tt>SourceUIGroup</tt> is the implementation of the UIGroup for the
* <tt>ExternalContactSource</tt>. It takes the name of the source and
* sets it as a group name.
*/
private class SourceUIGroup
implements UIGroup
{
/**
* The display name of the group.
*/
private final String displayName;
/**
* The corresponding group node.
*/
private GroupNode groupNode;
/**
* Creates an instance of <tt>SourceUIGroup</tt>.
* @param name the name of the group
*/
public SourceUIGroup(String name)
{
this.displayName = name;
}
/**
* Returns null to indicate that this group doesn't have a parent group
* and can be added directly to the root group.
* @return null
*/
public UIGroup getParentGroup()
{
return null;
}
/**
* Returns -1 to indicate that this group doesn't have a source index.
* @return -1
*/
public int getSourceIndex()
{
return -1;
}
/**
* Returns <tt>false</tt> to indicate that this group is always opened.
* @return false
*/
public boolean isGroupCollapsed()
{
return false;
}
/**
* Returns the display name of this group.
* @return the display name of this group
*/
public String getDisplayName()
{
return displayName;
}
/**
* Returns -1 to indicate that the child count is unknown.
* @return -1
*/
public int countChildContacts()
{
return -1;
}
/**
* Returns -1 to indicate that the child count is unknown.
* @return -1
*/
public int countOnlineChildContacts()
{
return -1;
}
/**
* Returns the display name of the group.
* @return the display name of the group
*/
public Object getDescriptor()
{
return displayName;
}
/**
* Returns null to indicate that this group doesn't have an identifier.
* @return null
*/
public String getId()
{
return null;
}
/**
* Returns the corresponding <tt>GroupNode</tt>.
* @return the corresponding <tt>GroupNode</tt>
*/
public GroupNode getGroupNode()
{
return groupNode;
}
/**
* Sets the given <tt>groupNode</tt>.
* @param groupNode the <tt>GroupNode</tt> to set
*/
public void setGroupNode(GroupNode groupNode)
{
this.groupNode = groupNode;
}
/**
* Returns the right button menu for this group.
* @return null
*/
public JPopupMenu getRightButtonMenu()
{
return null;
}
}
}

@ -0,0 +1,244 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
import java.util.*;
import java.util.regex.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
/**
* The <tt>MetaContactListSource</tt> is an abstraction of the
* <tt>MetaContactListService</tt>, which makes the correspondence between a
* <tt>MetaContact</tt> and an <tt>UIContact</tt> and between a
* <tt>MetaContactGroup</tt> and an <tt>UIGroup</tt>. It is also responsible
* for filtering of the <tt>MetaContactListService</tt> through a given pattern.
*
* @author Yana Stamcheva
*/
public class MetaContactListSource
{
/**
* The data key of the MetaContactDescriptor object used to store a
* reference to this object in its corresponding MetaContact.
*/
public static final String UI_CONTACT_DATA_KEY
= MetaUIContact.class.getName() + ".uiContactDescriptor";
/**
* The data key of the MetaGroupDescriptor object used to store a
* reference to this object in its corresponding MetaContactGroup.
*/
public static final String UI_GROUP_DATA_KEY
= MetaUIGroup.class.getName() + ".uiGroupDescriptor";
/**
* Returns the <tt>UIContact</tt> corresponding to the given
* <tt>MetaContact</tt>.
* @param metaContact the <tt>MetaContact</tt>, which corresponding UI
* contact we're looking for
* @return the <tt>UIContact</tt> corresponding to the given
* <tt>MetaContact</tt>
*/
public static UIContact getUIContact(MetaContact metaContact)
{
return (UIContact) metaContact.getData(UI_CONTACT_DATA_KEY);
}
/**
* Returns the <tt>UIGroup</tt> corresponding to the given
* <tt>MetaContactGroup</tt>.
* @param metaGroup the <tt>MetaContactGroup</tt>, which UI group we're
* looking for
* @return the <tt>UIGroup</tt> corresponding to the given
* <tt>MetaContactGroup</tt>
*/
public static UIGroup getUIGroup(MetaContactGroup metaGroup)
{
return (UIGroup) metaGroup.getData(UI_GROUP_DATA_KEY);
}
/**
* Creates a <tt>UIContact</tt> for the given <tt>metaContact</tt>.
* @param metaContact the <tt>MetaContact</tt> for which we would like to
* create an <tt>UIContact</tt>
* @return an <tt>UIContact</tt> for the given <tt>metaContact</tt>
*/
public static UIContact createUIContact(MetaContact metaContact)
{
UIGroup uiGroup = null;
MetaContactGroup parentMetaGroup
= metaContact.getParentMetaContactGroup();
if (parentMetaGroup != null
&& !parentMetaGroup.equals(
GuiActivator.getContactListService().getRoot()))
{
uiGroup = MetaContactListSource.getUIGroup(parentMetaGroup);
if (uiGroup == null)
uiGroup = MetaContactListSource.createUIGroup(parentMetaGroup);
}
MetaUIContact descriptor
= new MetaUIContact(metaContact, uiGroup);
metaContact.setData(UI_CONTACT_DATA_KEY, descriptor);
return descriptor;
}
/**
* Removes the <tt>UIContact</tt> from the given <tt>metaContact</tt>.
* @param metaContact the <tt>MetaContact</tt>, which corresponding UI
* contact we would like to remove
*/
public static void removeUIContact(MetaContact metaContact)
{
metaContact.setData(UI_CONTACT_DATA_KEY, null);
}
/**
* Creates a <tt>UIGroupDescriptor</tt> for the given <tt>metaGroup</tt>.
* @param metaGroup the <tt>MetaContactGroup</tt> for which we would like to
* create an <tt>UIContact</tt>
* @return a <tt>UIGroup</tt> for the given <tt>metaGroup</tt>
*/
public static UIGroup createUIGroup(MetaContactGroup metaGroup)
{
MetaUIGroup descriptor
= new MetaUIGroup(metaGroup);
metaGroup.setData(UI_GROUP_DATA_KEY, descriptor);
return descriptor;
}
/**
* Removes the descriptor from the given <tt>metaGroup</tt>.
* @param metaGroup the <tt>MetaContactGroup</tt>, which descriptor we
* would like to remove
*/
public static void removeUIGroup(
MetaContactGroup metaGroup)
{
metaGroup.setData(UI_GROUP_DATA_KEY, null);
}
/**
* Filters the <tt>MetaContactListService</tt> to match the given
* <tt>filterPattern</tt> and stores the result in the given
* <tt>treeModel</tt>.
* @param filterPattern the pattern to filter through
* @param treeModel the <tt>ContactListTreeModel</tt>, in which we store
* the results
*/
public void filter(Pattern filterPattern, ContactListTreeModel treeModel)
{
filter(filterPattern, treeModel,
GuiActivator.getContactListService().getRoot());
}
/**
* Filters the children in the given <tt>MetaContactGroup</tt> to match the
* given <tt>filterPattern</tt> and stores the result in the given
* <tt>treeModel</tt>.
* @param filterPattern the pattern to filter through
* @param treeModel the <tt>ContactListTreeModel</tt>, in which we store
* the results
* @param parentGroup the <tt>MetaContactGroup</tt> to filter
*/
private void filter(Pattern filterPattern,
ContactListTreeModel treeModel,
MetaContactGroup parentGroup)
{
Iterator<MetaContact> childContacts = parentGroup.getChildContacts();
while (childContacts.hasNext())
{
MetaContact metaContact = childContacts.next();
if (isMatching(filterPattern, metaContact))
{
GuiActivator.getContactList().addContact(
treeModel,
MetaContactListSource.createUIContact(metaContact),
true,
false);
}
}
Iterator<MetaContactGroup> subgroups = parentGroup.getSubgroups();
while (subgroups.hasNext())
{
MetaContactGroup subgroup = subgroups.next();
filter(filterPattern, treeModel, subgroup);
}
}
/**
* Checks if the given <tt>metaContact</tt> is matching the given
* <tt>filterPattern</tt>.
* A <tt>MetaContact</tt> would be matching the filter if one of the
* following is true:<br>
* - its display name contains the filter string
* - at least one of its child protocol contacts has a display name or an
* address that contains the filter string.
* @param filterPattern the filter pattern to check for matches
* @param metaContact the <tt>MetaContact</tt> to check
* @return <tt>true</tt> to indicate that the given <tt>metaContact</tt> is
* matching the current filter, otherwise returns <tt>false</tt>
*/
private boolean isMatching(Pattern filterPattern, MetaContact metaContact)
{
Matcher matcher = filterPattern.matcher(metaContact.getDisplayName());
if(matcher.find())
return true;
Iterator<Contact> contacts = metaContact.getContacts();
while (contacts.hasNext())
{
Contact contact = contacts.next();
matcher = filterPattern.matcher(contact.getDisplayName());
if (matcher.find())
return true;
matcher = filterPattern.matcher(contact.getAddress());
if (matcher.find())
return true;
}
return false;
}
/**
* Checks if the given <tt>metaGroup</tt> is matching the current filter. A
* group is matching the current filter only if it contains at least one
* child <tt>MetaContact</tt>, which is matching the current filter.
* @param filterPattern the filter pattern to check for matches
* @param metaGroup the <tt>MetaContactGroup</tt> to check
* @return <tt>true</tt> to indicate that the given <tt>metaGroup</tt> is
* matching the current filter, otherwise returns <tt>false</tt>
*/
public boolean isMatching(Pattern filterPattern, MetaContactGroup metaGroup)
{
Iterator<MetaContact> contacts = metaGroup.getChildContacts();
while (contacts.hasNext())
{
MetaContact metaContact = contacts.next();
if (isMatching(filterPattern, metaContact))
return true;
}
return false;
}
}

@ -0,0 +1,408 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
import java.util.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
/**
* The <tt>MetaUIContact</tt> is the implementation of the UIContact interface
* for the <tt>MetaContactListService</tt>. This implementation is based on the
* <tt>MetaContact</tt>.
*
* @author Yana Stamcheva
*/
public class MetaUIContact
implements UIContact
{
/**
* The key of the user data in <tt>MetaContact</tt> which specifies
* the avatar cached from previous invocations.
*/
private static final String AVATAR_DATA_KEY
= MetaUIContact.class.getName() + ".avatar";
/**
* A list of all search strings available for the underlying
* <tt>MetaContact</tt>.
*/
private final List<String> searchStrings = new LinkedList<String>();
/**
* The <tt>MetaContact</tt>, on which this implementation is based.
*/
private MetaContact metaContact;
/**
* The corresponding <tt>ContactNode</tt> in the contact list component
* data model.
*/
private ContactNode contactNode;
/**
* The parent <tt>UIGroup</tt> of this contact.
*/
private UIGroup parentUIGroup;
/**
* Creates an instance of <tt>MetaUIContact</tt> by specifying the
* underlying <tt>MetaContact</tt>, on which it's based.
* @param metaContact the <tt>MetaContact</tt>, on which this implementation
* is based
* @param group the parent <tt>UIGroup</tt>
*/
public MetaUIContact(MetaContact metaContact, UIGroup group)
{
this.metaContact = metaContact;
this.parentUIGroup = group;
initSearchStrings();
}
/**
* Returns the underlying <tt>MetaContact</tt>.
* @return the underlying <tt>MetaContact</tt>
*/
public Object getDescriptor()
{
return metaContact;
}
/**
* Returns the display name of this <tt>MetaUIContact</tt>.
* @return the display name of this <tt>MetaUIContact</tt>
*/
public String getDisplayName()
{
return metaContact.getDisplayName();
}
/**
* Returns the index of the underlying <tt>MetaContact</tt> in its
* <tt>MetaContactListService</tt> parent group.
* @return the source index of the underlying <tt>MetaContact</tt>
*/
public int getSourceIndex()
{
return metaContact.getParentMetaContactGroup().indexOf(metaContact);
}
/**
* Returns an <tt>Iterator</tt> over a list of strings, which can be used
* to find this contact.
* @return an <tt>Iterator</tt> over a list of search strings
*/
public Iterator<String> getSearchStrings()
{
return searchStrings.iterator();
}
/**
* Returns the general status icon of the given MetaContact. Detects the
* status using the priority status table. The priority is defined on
* the "availability" factor and here the most "available" status is
* returned.
*
* @return PresenceStatus The most "available" status from all
* sub-contact statuses.
*/
public ImageIcon getStatusIcon()
{
PresenceStatus status = null;
Iterator<Contact> i = metaContact.getContacts();
while (i.hasNext()) {
Contact protoContact = i.next();
PresenceStatus contactStatus = protoContact.getPresenceStatus();
if (status == null)
status = contactStatus;
else
status = (contactStatus.compareTo(status) > 0)
? contactStatus
: status;
}
if (status != null)
return new ImageIcon(Constants.getStatusIcon(status));
return null;
}
/**
* Returns the parent <tt>UIGroup</tt>.
* @return the parent <tt>UIGroup</tt>
*/
public UIGroup getParentGroup()
{
return parentUIGroup;
}
/**
* Returns the default <tt>ContactDetail</tt> to use for any operations
* depending to the given <tt>OperationSet</tt> class.
* @param opSetClass the <tt>OperationSet</tt> class we're interested in
* @return the default <tt>ContactDetail</tt> to use for any operations
* depending to the given <tt>OperationSet</tt> class
*/
public UIContactDetail getDefaultContactDetail(
Class<? extends OperationSet> opSetClass)
{
List<UIContactDetail> details
= getContactDetailsForOperationSet(opSetClass);
if (details != null && !details.isEmpty())
return details.get(0);
return null;
}
/**
* Returns a list of <tt>UIContactDetail</tt>s supporting the given
* <tt>OperationSet</tt> class.
* @param opSetClass the <tt>OperationSet</tt> class we're interested in
* @return a list of <tt>UIContactDetail</tt>s supporting the given
* <tt>OperationSet</tt> class
*/
public List<UIContactDetail> getContactDetailsForOperationSet(
Class<? extends OperationSet> opSetClass)
{
List<UIContactDetail> resultList
= new LinkedList<UIContactDetail>();
Iterator<Contact> contacts
= metaContact.getContactsForOperationSet(opSetClass).iterator();
while (contacts.hasNext())
{
resultList.add(new MetaContactDetail(contacts.next()));
}
return resultList;
}
/**
* Gets the avatar of a specific <tt>MetaContact</tt> in the form of an
* <tt>ImageIcon</tt> value.
*
* @param isSelected indicates if the contact is selected
* @param width the desired icon width
* @param height the desired icon height
* @return an <tt>ImageIcon</tt> which represents the avatar of the
* specified <tt>MetaContact</tt>
*/
public ImageIcon getAvatar(
boolean isSelected, int width, int height)
{
byte[] avatarBytes = metaContact.getAvatar(true);
ImageIcon avatar = null;
// If there'rs no avatar we have nothing more to do here.
if((avatarBytes == null) || (avatarBytes.length <= 0))
return null;
// If the cell is selected we return a zoomed version of the avatar
// image.
if (isSelected)
return ImageUtils.getScaledRoundedIcon(
avatarBytes,
width,
height);
// In any other case try to get the avatar from the cache.
Object[] avatarCache
= (Object[]) metaContact.getData(AVATAR_DATA_KEY);
if ((avatarCache != null) && (avatarCache[0] == avatarBytes))
avatar = (ImageIcon) avatarCache[1];
// Just
int avatarWidth = width;
int avatarHeight = height;
// If the avatar isn't available or it's not up-to-date, create it.
if (avatar == null)
avatar = ImageUtils.getScaledRoundedIcon(
avatarBytes,
avatarWidth,
avatarHeight);
// Cache the avatar in case it has changed.
if (avatarCache == null)
{
if (avatar != null)
metaContact.setData(
AVATAR_DATA_KEY,
new Object[] { avatarBytes, avatar });
}
else
{
avatarCache[0] = avatarBytes;
avatarCache[1] = avatar;
}
return avatar;
}
/**
* Returns the display details for the underlying <tt>MetaContact</tt>.
* @return the display details for the underlying <tt>MetaContact</tt>
*/
public String getDisplayDetails()
{
String statusMessage = null;
Iterator<Contact> protoContacts = metaContact.getContacts();
while (protoContacts.hasNext())
{
Contact protoContact = protoContacts.next();
statusMessage = protoContact.getStatusMessage();
if (statusMessage != null && statusMessage.length() > 0)
break;
}
return statusMessage;
}
/**
* Returns the tool tip opened on mouse over.
* @return the tool tip opened on mouse over
*/
public ExtendedTooltip getToolTip()
{
ExtendedTooltip tip = new ExtendedTooltip(true);
byte[] avatarImage = metaContact.getAvatar();
if (avatarImage != null && avatarImage.length > 0)
tip.setImage(new ImageIcon(avatarImage));
tip.setTitle(metaContact.getDisplayName());
Iterator<Contact> i = metaContact.getContacts();
String statusMessage = null;
Contact protocolContact;
while (i.hasNext())
{
protocolContact = i.next();
ImageIcon protocolStatusIcon
= new ImageIcon(
protocolContact.getPresenceStatus().getStatusIcon());
String contactAddress = protocolContact.getAddress();
//String statusMessage = protocolContact.getStatusMessage();
tip.addLine(protocolStatusIcon, contactAddress);
// Set the first found status message.
if (statusMessage == null
&& protocolContact.getStatusMessage() != null
&& protocolContact.getStatusMessage().length() > 0)
statusMessage = protocolContact.getStatusMessage();
}
if (statusMessage != null)
tip.setBottomText(statusMessage);
return tip;
}
/**
* Returns the corresponding <tt>ContactNode</tt> in the contact list
* component data model.
* @return the corresponding <tt>ContactNode</tt>
*/
public ContactNode getContactNode()
{
return contactNode;
}
/**
* Sets the corresponding <tt>ContactNode</tt>.
* @param contactNode the corresponding <tt>ContactNode</tt> in the contact
* list component data model
*/
public void setContactNode(ContactNode contactNode)
{
this.contactNode = contactNode;
if (contactNode == null)
MetaContactListSource.removeUIContact(metaContact);
}
/**
* Initializes all search strings for this <tt>MetaUIGroup</tt>.
*/
private void initSearchStrings()
{
searchStrings.add(metaContact.getDisplayName());
Iterator<Contact> contacts = metaContact.getContacts();
while (contacts.hasNext())
{
Contact contact = contacts.next();
searchStrings.add(contact.getDisplayName());
searchStrings.add(contact.getAddress());
}
}
/**
* The implementation of the <tt>UIContactDetail</tt> interface for the
* <tt>MetaContactListService</tt>.
*/
private class MetaContactDetail extends UIContactDetail
{
/**
* The underlying protocol contact.
*/
private Contact contact;
/**
* Creates an instance of <tt>MetaContactDetail</tt> by specifying the
* underlying protocol <tt>Contact</tt>.
* @param contact the protocol contact, on which this implementation
* is based
*/
public MetaContactDetail(Contact contact)
{
super( contact.getAddress(),
contact.getDisplayName(),
contact.getProtocolProvider());
this.contact = contact;
}
/**
* Returns the presence status of the underlying protocol
* <tt>Contact</tt>.
* @return the presence status of the underlying protocol
* <tt>Contact</tt>
*/
public PresenceStatus getPresenceStatus()
{
return contact.getPresenceStatus();
}
}
/**
* Returns the right button menu component.
* @return the right button menu component
*/
public JPopupMenu getRightButtonMenu()
{
return new MetaContactRightButtonMenu(metaContact);
}
}

@ -0,0 +1,164 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
/**
* The <tt>MetaUIGroup</tt> is the implementation of the UIGroup for the
* <tt>MetaContactListService</tt>. This implementation is based on the
* <tt>MetaContactGroup</tt>.
*
* @author Yana Stamcheva
*/
public class MetaUIGroup
implements UIGroup
{
/**
* The <tt>MetaContactGroup</tt>, on which this UI group is based.
*/
private final MetaContactGroup metaGroup;
/**
* The corresponding <tt>GroupNode</tt> in the contact list component data
* model.
*/
private GroupNode groupNode;
/**
* Creates an instance of <tt>MetaUIGroup</tt> by specifying the underlying
* <tt>MetaContactGroup</tt>.
* @param metaGroup the <tt>MetaContactGroup</tt>, on which this UI group
* is based
*/
public MetaUIGroup(MetaContactGroup metaGroup)
{
this.metaGroup = metaGroup;
}
/**
* Returns the underlying <tt>MetaContactGroup</tt>.
* @return the underlying <tt>MetaContactGroup</tt>
*/
public Object getDescriptor()
{
return metaGroup;
}
/**
* Returns the index of the underlying <tt>MetaContactGroup</tt> in its
* <tt>MetaContactListService</tt> parent group.
* @return the source index of the underlying <tt>MetaContactGroup</tt>
*/
public int getSourceIndex()
{
return metaGroup.getParentMetaContactGroup().indexOf(metaGroup);
}
/**
* Returns the parent <tt>UIGroup</tt>.
* @return the parent <tt>UIGroup</tt>
*/
public UIGroup getParentGroup()
{
MetaContactGroup parentGroup = metaGroup.getParentMetaContactGroup();
if (parentGroup != null
&& !parentGroup.equals(
GuiActivator.getContactListService().getRoot()))
return new MetaUIGroup(parentGroup);
return null;
}
/**
* Indicates if this group was collapsed.
* @return <tt>true</tt> to indicate that this group has been collapsed,
* <tt>false</tt> - otherwise
*/
public boolean isGroupCollapsed()
{
return ConfigurationManager
.isContactListGroupCollapsed(metaGroup.getMetaUID());
}
/**
* Returns the display name of the underlying <tt>MetaContactGroup</tt>.
* @return the display name of the underlying <tt>MetaContactGroup</tt>
*/
public String getDisplayName()
{
return metaGroup.getGroupName();
}
/**
* Returns the count of child contacts of the underlying
* <tt>MetaContactGroup</tt>.
* @return the count of child contacts
*/
public int countChildContacts()
{
return metaGroup.countChildContacts();
}
/**
* Returns the count of online child contacts of the underlying
* <tt>MetaContactGroup</tt>.
* @return the count of online child contacts
*/
public int countOnlineChildContacts()
{
return metaGroup.countOnlineChildContacts();
}
/**
* Returns the identifier of the underlying <tt>MetaContactGroup</tt>.
* @return the identifier of the underlying <tt>MetaContactGroup</tt>
*/
public String getId()
{
return metaGroup.getMetaUID();
}
/**
* Returns the corresponding <tt>GroupNode</tt>.
* @return the corresponding <tt>GroupNode</tt>
*/
public GroupNode getGroupNode()
{
return groupNode;
}
/**
* Sets the corresponding <tt>GroupNode</tt>.
* @param groupNode the corresponding <tt>GroupNoe</tt> in the contact list
* component data model
*/
public void setGroupNode(GroupNode groupNode)
{
this.groupNode = groupNode;
if (groupNode == null)
MetaContactListSource.removeUIGroup(metaGroup);
}
/**
* Returns the <tt>JPopupMenu</tt> opened on a right button click over this
* group in the contact list.
* @return the <tt>JPopupMenu</tt> opened on a right button click over this
* group in the contact list
*/
public JPopupMenu getRightButtonMenu()
{
return new GroupRightButtonMenu(
GuiActivator.getUIService().getMainFrame(), metaGroup);
}
}

@ -0,0 +1,282 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.gui.main.contactlist.contactsource;
import java.util.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.main.contactlist.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.util.*;
/**
* The <tt>SourceUIContact</tt> is the implementation of the UIContact for the
* <tt>ExternalContactSource</tt>.
*
* @author Yana Stamcheva
*/
public class SourceUIContact
implements UIContact
{
/**
* The corresponding <tt>SourceContact</tt>, on which this abstraction is
* based.
*/
private final SourceContact sourceContact;
/**
* The corresponding <tt>ContactNode</tt> in the contact list component.
*/
private ContactNode contactNode;
/**
* The parent <tt>UIGroup</tt>.
*/
private UIGroup uiGroup;
/**
* Creates an instance of <tt>SourceUIContact</tt> by specifying the
* <tt>SourceContact</tt>, on which this abstraction is based and the
* parent <tt>UIGroup</tt>.
*
* @param contact the <tt>SourceContact</tt>, on which this abstraction
* is based
* @param parentGroup the parent <tt>UIGroup</tt>
*/
public SourceUIContact( SourceContact contact,
UIGroup parentGroup)
{
this.sourceContact = contact;
this.uiGroup = parentGroup;
}
/**
* Returns the display name of the underlying <tt>SourceContact</tt>.
* @return the display name
*/
public String getDisplayName()
{
return sourceContact.getDisplayName();
}
/**
* Returns the parent <tt>UIGroup</tt>.
* @return the parent <tt>UIGroup</tt>
*/
public UIGroup getParentGroup()
{
return uiGroup;
}
/**
* Returns -1 to indicate that the source index of the underlying
* <tt>SourceContact</tt> is unknown.
* @return -1
*/
public int getSourceIndex()
{
return -1;
}
/**
* Returns null to indicate unknown status of the underlying
* <tt>SourceContact</tt>.
* @return null
*/
public ImageIcon getStatusIcon()
{
return null;
}
/**
* Returns the image corresponding to the underlying <tt>SourceContact</tt>.
* @param isSelected indicates if the contact is currently selected in the
* contact list component
* @param width the desired image width
* @param height the desired image height
* @return the image
*/
public ImageIcon getAvatar(boolean isSelected, int width, int height)
{
ImageIcon icon = new ImageIcon(sourceContact.getImage());
if (icon.getIconWidth() > width
|| icon.getIconHeight() > height)
{
icon = ImageUtils
.getScaledRoundedIcon(icon.getImage(), width, height);
}
return icon;
}
/**
* Returns the default <tt>ContactDetail</tt> to use for any operations
* depending to the given <tt>OperationSet</tt> class.
* @param opSetClass the <tt>OperationSet</tt> class we're interested in
* @return the default <tt>ContactDetail</tt> to use for any operations
* depending to the given <tt>OperationSet</tt> class
*/
public UIContactDetail getDefaultContactDetail(
Class<? extends OperationSet> opSetClass)
{
List<UIContactDetail> details
= getContactDetailsForOperationSet(opSetClass);
if (details != null && !details.isEmpty())
return details.get(0);
return null;
}
/**
* Returns the underlying <tt>SourceContact</tt> this abstraction is about.
* @return the underlying <tt>SourceContact</tt>
*/
public Object getDescriptor()
{
return sourceContact;
}
/**
* Returns the display details for the underlying <tt>SourceContact</tt>.
* @return the display details for the underlying <tt>SourceContact</tt>
*/
public String getDisplayDetails()
{
return sourceContact.getDisplayDetails();
}
/**
* Returns a list of <tt>UIContactDetail</tt>s supporting the given
* <tt>OperationSet</tt> class.
* @param opSetClass the <tt>OperationSet</tt> class we're interested in
* @return a list of <tt>UIContactDetail</tt>s supporting the given
* <tt>OperationSet</tt> class
*/
public List<UIContactDetail> getContactDetailsForOperationSet(
Class<? extends OperationSet> opSetClass)
{
List<UIContactDetail> resultList
= new LinkedList<UIContactDetail>();
Iterator<ContactDetail> details
= sourceContact.getContactDetails().iterator();
while (details.hasNext())
{
ContactDetail detail = details.next();
if (detail.getSupportedOperationSets().contains(opSetClass))
resultList.add(new SourceContactDetail(detail, opSetClass));
}
return resultList;
}
public Iterator<String> getSearchStrings()
{
return null;
}
/**
* Returns the corresponding <tt>ContactNode</tt> from the contact list
* component.
* @return the corresponding <tt>ContactNode</tt>
*/
public ContactNode getContactNode()
{
return contactNode;
}
/**
* Sets the corresponding <tt>ContactNode</tt>.
* @param contactNode the corresponding <tt>ContactNode</tt>
*/
public void setContactNode(ContactNode contactNode)
{
this.contactNode = contactNode;
}
/**
* The implementation of the <tt>UIContactDetail</tt> interface for the
* external source <tt>ContactDetail</tt>s.
*/
private class SourceContactDetail extends UIContactDetail
{
/**
* Creates an instance of <tt>SourceContactDetail</tt> by specifying
* the underlying <tt>detail</tt> and the <tt>OperationSet</tt> class
* for it.
* @param detail the underlying <tt>ContactDetail</tt>
* @param opSetClass the <tt>OperationSet</tt> class for the
* preferred protocol provider
*/
public SourceContactDetail( ContactDetail detail,
Class<? extends OperationSet> opSetClass)
{
super( detail.getContactAddress(),
detail.getContactAddress(),
detail.getPreferredProtocolProvider(opSetClass));
}
/**
* Returns null to indicate that this detail doesn't support presence.
* @return null
*/
public PresenceStatus getPresenceStatus()
{
return null;
}
}
/**
* Returns the <tt>JPopupMenu</tt> opened on a right button click over this
* <tt>SourceUIContact</tt>.
* @return the <tt>JPopupMenu</tt> opened on a right button click over this
* <tt>SourceUIContact</tt>
*/
public JPopupMenu getRightButtonMenu()
{
return new SourceContactRightButtonMenu(sourceContact);
}
/**
* Returns the tool tip opened on mouse over.
* @return the tool tip opened on mouse over
*/
public ExtendedTooltip getToolTip()
{
ExtendedTooltip tip = new ExtendedTooltip(true);
byte[] avatarImage = sourceContact.getImage();
if (avatarImage != null && avatarImage.length > 0)
tip.setImage(new ImageIcon(avatarImage));
tip.setTitle(sourceContact.getDisplayName());
Iterator<ContactDetail> details
= sourceContact.getContactDetails().iterator();
ContactDetail contactDetail;
while (details.hasNext())
{
contactDetail = details.next();
String contactAddress = contactDetail.getContactAddress();
//String statusMessage = protocolContact.getStatusMessage();
tip.addLine(null, contactAddress);
}
tip.setBottomText(getDisplayDetails());
return tip;
}
}

@ -119,8 +119,14 @@ else if (itemName.equals("showHideOffline"))
TreeContactList.presenceFilter.setShowOffline(!isShowOffline);
GuiActivator.getContactList()
.applyFilter(TreeContactList.presenceFilter);
new Thread()
{
public void run()
{
GuiActivator.getContactList()
.applyFilter(TreeContactList.presenceFilter);
}
}.start();
ConfigurationManager.setShowOffline(!isShowOffline);

@ -32,6 +32,7 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.systray,
net.java.sip.communicator.service.neomedia,
net.java.sip.communicator.service.neomedia.device,
net.java.sip.communicator.service.contactsource,
net.java.sip.communicator.util,
net.java.sip.communicator.util.swing,
net.java.sip.communicator.util.swing.border,

@ -277,6 +277,18 @@ public class ImageLoader
public static final ImageID CHAT_CALL
= new ImageID("service.gui.buttons.CHAT_CALL");
/**
* The call history button image.
*/
public static final ImageID CALL_HISTORY_BUTTON
= new ImageID("service.gui.buttons.CALL_HISTORY_BUTTON");
/**
* The call history pressed button image.
*/
public static final ImageID CALL_HISTORY_BUTTON_PRESSED
= new ImageID("service.gui.buttons.CALL_HISTORY_BUTTON_PRESSED");
/**
* The chat button small pressed image.
*/

@ -10,6 +10,8 @@
import org.w3c.dom.*;
import sun.nio.cs.ext.*;
import net.java.sip.communicator.service.history.*;
import net.java.sip.communicator.service.history.event.*;
import net.java.sip.communicator.service.history.records.*;
@ -17,6 +19,7 @@
/**
* @author Alexander Pelov
* @author Damian Minkov
* @author Yana Stamcheva
*/
public class HistoryReaderImpl
implements HistoryReader
@ -30,6 +33,15 @@ public class HistoryReaderImpl
private static String REGEXP_SENSITIVE_START = "(?s)^.*";
private static String REGEXP_INSENSITIVE_START = "(?si)^.*";
/**
* Indicates if the current find should be canceled.
*/
private boolean isFindCanceled = false;
/**
* Creates an instance of <tt>HistoryReaderImpl</tt>.
* @param historyImpl the parent History implementation
*/
protected HistoryReaderImpl(HistoryImpl historyImpl)
{
this.historyImpl = historyImpl;
@ -45,7 +57,8 @@ protected HistoryReaderImpl(HistoryImpl historyImpl)
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public synchronized QueryResultSet<HistoryRecord> findByStartDate(Date startDate)
public synchronized QueryResultSet<HistoryRecord> findByStartDate(
Date startDate)
throws RuntimeException
{
return find(startDate, null, null, null, false);
@ -498,22 +511,26 @@ private QueryResultSet<HistoryRecord> find(
TreeSet<HistoryRecord> result
= new TreeSet<HistoryRecord>(new HistoryRecordComparator());
Vector<String> filelist =
filterFilesByDate(this.historyImpl.getFileList(), startDate, endDate);
Vector<String> filelist
= filterFilesByDate(this.historyImpl.getFileList(),
startDate, endDate);
double currentProgress = HistorySearchProgressListener.PROGRESS_MINIMUM_VALUE;
double fileProgressStep = HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE;
double currentProgress
= HistorySearchProgressListener.PROGRESS_MINIMUM_VALUE;
double fileProgressStep
= HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE;
if(filelist.size() != 0)
fileProgressStep =
HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE / filelist.size();
fileProgressStep
= HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE
/ filelist.size();
// start progress - minimum value
fireProgressStateChanged(startDate, endDate,
keywords, HistorySearchProgressListener.PROGRESS_MINIMUM_VALUE);
Iterator<String> fileIterator = filelist.iterator();
while (fileIterator.hasNext())
while (fileIterator.hasNext() && !isFindCanceled)
{
String filename = fileIterator.next();
@ -530,7 +547,7 @@ private QueryResultSet<HistoryRecord> find(
nodesProgressStep = fileProgressStep / nodes.getLength();
Node node;
for (int i = 0; i < nodes.getLength(); i++)
for (int i = 0; i < nodes.getLength() && !isFindCanceled; i++)
{
node = nodes.item(i);
@ -560,13 +577,17 @@ private QueryResultSet<HistoryRecord> find(
}
// if maximum value is not reached fire an event
if((int)currentProgress < HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
if((int)currentProgress
< HistorySearchProgressListener.PROGRESS_MAXIMUM_VALUE)
{
fireProgressStateChanged(startDate, endDate, keywords,
HistorySearchProgressListener.
PROGRESS_MAXIMUM_VALUE);
}
// Before returning we want to reset the isFindCanceled to false.
isFindCanceled = false;
return new OrderedQueryResultSet<HistoryRecord>(result);
}
@ -902,4 +923,12 @@ public int compare(HistoryRecord h1, HistoryRecord h2)
compareTo(h2.getTimestamp());
}
}
/**
* Cancels the current find. If there's no find going on, then does nothing.
*/
public void cancelCurrentFind()
{
isFindCanceled = true;
}
}

@ -0,0 +1,53 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.callhistory;
import java.util.*;
import net.java.sip.communicator.service.callhistory.event.*;
/**
* The <tt>CallHistoryQuery</tt> corresponds to a query made to the
* <tt>CallHistoryService</tt>. It allows to be canceled, to listen for changes
* in the results and to obtain initial results if available.
*
* @author Yana Stamcheva
*/
public interface CallHistoryQuery
{
/**
* Cancels this query.
*/
public void cancel();
/**
* Returns a collection of the initial results for this query. It's up to
* the implementation to determine, which and how many the initial results
* would be.
* <p>
* This method is meant to be used in order to return first fast initial
* results and then notify interested parties of additional results through
* the <tt>CallHistoryQueryListener</tt>, which should improve user
* experience when waiting for results.
* @return a collection of the initial results for this query
*/
public Collection<CallRecord> getCallRecords();
/**
* Adds the given <tt>CallHistoryQueryListener</tt> to the list of
* listeners interested in query result changes.
* @param l the <tt>CallHistoryQueryListener</tt> to add
*/
public void addCallRecordsListener(CallHistoryQueryListener l);
/**
* Removes the given <tt>CallHistoryQueryListener</tt> from the list of
* listeners interested in query result changes.
* @param l the <tt>CallHistoryQueryListener</tt> to remove
*/
public void removeCallRecordsListener(CallHistoryQueryListener l);
}

@ -21,7 +21,7 @@ public interface CallHistoryService
{
/**
* Returns all the calls made by all the contacts
* in the supplied metacontact after the given date
* in the supplied <tt>contact</tt> after the given date.
*
* @param contact MetaContact which contacts participate in
* the returned calls
@ -29,12 +29,13 @@ public interface CallHistoryService
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
public Collection<CallRecord> findByStartDate(MetaContact contact, Date startDate)
public Collection<CallRecord> findByStartDate( MetaContact contact,
Date startDate)
throws RuntimeException;
/**
* Returns all the calls made by all the contacts
* in the supplied metacontact before the given date
* in the supplied <tt>contact</tt> before the given date.
*
* @param contact MetaContact which contacts participate in
* the returned calls
@ -42,12 +43,13 @@ public Collection<CallRecord> findByStartDate(MetaContact contact, Date startDat
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
public Collection<CallRecord> findByEndDate(MetaContact contact, Date endDate)
public Collection<CallRecord> findByEndDate(MetaContact contact,
Date endDate)
throws RuntimeException;
/**
* Returns all the calls made by all the contacts
* in the supplied metacontact between the given dates
* in the supplied <tt>contact</tt> between the given dates.
*
* @param contact MetaContact which contacts participate in
* the returned calls
@ -56,12 +58,14 @@ public Collection<CallRecord> findByEndDate(MetaContact contact, Date endDate)
* @return Collection of CallRecords with CallPeerRecord
* @throws RuntimeException
*/
public Collection<CallRecord> findByPeriod(MetaContact contact, Date startDate, Date endDate)
public Collection<CallRecord> findByPeriod( MetaContact contact,
Date startDate,
Date endDate)
throws RuntimeException;
/**
* Returns all the calls made after the given date
* Returns all the calls made after the given date.
*
* @param startDate Date the start date of the calls
* @return Collection of CallRecords with CallPeerRecord
@ -71,7 +75,7 @@ public Collection<CallRecord> findByStartDate(Date startDate)
throws RuntimeException;
/**
* Returns all the calls made before the given date
* Returns all the calls made before the given date.
*
* @param endDate Date the end date of the calls
* @return Collection of CallRecords with CallPeerRecord
@ -81,7 +85,7 @@ public Collection<CallRecord> findByEndDate(Date endDate)
throws RuntimeException;
/**
* Returns all the calls made between the given dates
* Returns all the calls made between the given dates.
*
* @param startDate Date the start date of the calls
* @param endDate Date the end date of the calls
@ -93,7 +97,7 @@ public Collection<CallRecord> findByPeriod(Date startDate, Date endDate)
/**
* Returns the supplied number of recent calls made by all the contacts
* in the supplied metacontact
* in the supplied <tt>contact</tt>.
*
* @param contact MetaContact which contacts participate in
* the returned calls
@ -105,8 +109,7 @@ public Collection<CallRecord> findLast(MetaContact contact, int count)
throws RuntimeException;
/**
* Returns the supplied number of recent calls made by all the contacts
* in the supplied metacontact
* Returns the supplied number of recent calls.
*
* @param count calls count
* @return Collection of CallRecords with CallPeerRecord
@ -129,12 +132,19 @@ public Collection<CallRecord> findByPeer(String address)
*
* @param listener HistorySearchProgressListener
*/
public void addSearchProgressListener(CallHistorySearchProgressListener listener);
public void addSearchProgressListener(
CallHistorySearchProgressListener listener);
/**
* Removing progress listener
*
* @param listener HistorySearchProgressListener
*/
public void removeSearchProgressListener(CallHistorySearchProgressListener listener);
public void removeSearchProgressListener(
CallHistorySearchProgressListener listener);
/**
* Cancels the current find. If there's no find going on, then does nothing.
*/
public void cancelCurrentFind();
}

@ -1,30 +1,61 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.callhistory;
import java.util.*;
import net.java.sip.communicator.service.protocol.*;
/**
* Structure used for encapsulating data when writing or reading
* Call History Data. Also These records are uesd for returning data
* from the Call History Service
* Call History Data. Also these records are used for returning data
* from the Call History Service.
*
* @author Damian Minkov
* @author Yana Stamcheva
*/
public class CallRecord
{
/**
* Possible directions of the call
* The outgoing call direction.
*/
public final static String OUT = "out";
/**
* The incoming call direction.
*/
public final static String IN = "in";
/**
* Indicates the direction of the call - IN or OUT.
*/
protected String direction = null;
/**
* A list of all peer records corresponding to this call record.
*/
protected final List<CallPeerRecord> peerRecords =
new Vector<CallPeerRecord>();
/**
* The start call date.
*/
protected Date startTime = null;
/**
* The end call date.
*/
protected Date endTime = null;
/**
* The protocol provider through which the call was made.
*/
protected ProtocolProviderService protocolProvider;
/**
* Creates CallRecord
*/
@ -101,4 +132,14 @@ public Date getStartTime()
{
return startTime;
}
/**
* Returns the protocol provider used for the call. Could be null if the
* record has not saved the provider.
* @return the protocol provider used for the call
*/
public ProtocolProviderService getProtocolProvider()
{
return protocolProvider;
}
}

@ -0,0 +1,26 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.callhistory.event;
/**
* The <tt>CallHistoryQueryListener</tt> listens for changes in the result of
* a given <tt>CallHistoryQuery</tt>. When a query to the call history is
* started, this listener would be notified every time new results are available
* for this query.
*
* @author Yana Stamcheva
*/
public interface CallHistoryQueryListener
{
/**
* Indicates that new <tt>CallRecord</tt>s are received as a result of the
* query.
* @param event the <tt>CallRecordsEvent</tt> containing information about
* the query results.
*/
public void callRecordsReceived(CallRecordsEvent event);
}

@ -0,0 +1,61 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.service.callhistory.event;
import java.util.*;
import net.java.sip.communicator.service.callhistory.*;
/**
* The <tt>CallRecordsEvent</tt> indicates that one or more
* <tt>CallRecord</tt>s has been received as a result of a
* <tt>CallHistoryQuery</tt>.
*
* @author Yana Stamcheva
*/
public class CallRecordsEvent
extends EventObject
{
/**
* A collection of call records received as a result of a given
* <tt>query</tt>.
*/
private final Collection<CallRecord> callRecords;
/**
* Creates a <tt>ContactReceivedEvent</tt> by specifying the contact search
* source and the received <tt>searchContact</tt>.
* @param query the source that triggered this event
* @param callRecords the call records received as a result from the given
* <tt>query</tt>
*/
public CallRecordsEvent(CallHistoryQuery query,
Collection<CallRecord> callRecords)
{
super(query);
this.callRecords = callRecords;
}
/**
* Returns the <tt>ContactQuery</tt> that triggered this event.
* @return the <tt>ContactQuery</tt> that triggered this event
*/
public CallHistoryQuery getQuerySource()
{
return (CallHistoryQuery) source;
}
/**
* Returns the collection of <tt>CallRecord</tt>s this event is about.
* @return the collection of <tt>CallRecord</tt>s this event is about
*/
public Collection<CallRecord> getCallRecords()
{
return callRecords;
}
}

@ -291,4 +291,40 @@ public MetaContactGroup getMetaContactSubgroup(int index)
* @return a String uniquely identifying this meta contact.
*/
public String getMetaUID();
/**
* Gets the user data associated with this instance and a specific key.
*
* @param key
* the key of the user data associated with this instance to be
* retrieved
* @return an <code>Object</code> which represents the value associated with
* this instance and the specified <code>key</code>; <tt>null</tt>
* if no association with the specified <code>key</code> exists in
* this instance
*/
public Object getData(Object key);
/**
* Sets a user-specific association in this instance in the form of a
* key-value pair. If the specified <code>key</code> is already associated
* in this instance with a value, the existing value is overwritten with the
* specified <code>value</code>.
* <p>
* The user-defined association created by this method and stored in this
* instance is not serialized by this instance and is thus only meant for
* runtime use.
* </p>
* <p>
* The storage of the user data is implementation-specific and is thus not
* guaranteed to be optimized for execution time and memory use.
* </p>
*
* @param key
* the key to associate in this instance with the specified value
* @param value
* the value to be associated in this instance with the specified
* <code>key</code>
*/
public void setData(Object key, Object value);
}

@ -67,17 +67,14 @@ public ContactDetail(String contactAddress)
* Creates a <tt>ContactDetail</tt> by specifying the corresponding contact
* address and a mapping of preferred <tt>ProtocolProviderServices</tt> for
* a specific <tt>OperationSet</tt>.
* @param contactAddress the contact address corresponding to this detail
* @param preferredProviders a mapping of preferred
* <tt>ProtocolProviderService</tt>s for specific <tt>OperationSet</tt>
* classes
*/
public ContactDetail(String contactAddress,
public void setPreferredProviders(
Map<Class<? extends OperationSet>, ProtocolProviderService>
preferredProviders)
{
this(contactAddress);
this.preferredProviders = preferredProviders;
}
@ -86,16 +83,13 @@ public ContactDetail(String contactAddress,
* address and a list of all <tt>supportedOpSets</tt>, indicating what are
* the supporting actions with this contact detail (e.g. sending a message,
* making a call, etc.)
* @param contactAddress the address of the contact
* @param supportedOpSets a list of all <tt>supportedOpSets</tt>, indicating
* what are the supporting actions with this contact detail (e.g. sending a
* message, making a call, etc.)
*/
public ContactDetail( String contactAddress,
public void setSupportedOpSets(
List<Class<? extends OperationSet>> supportedOpSets)
{
this(contactAddress);
this.supportedOpSets = supportedOpSets;
}
@ -119,7 +113,10 @@ public String getContactAddress()
public ProtocolProviderService getPreferredProtocolProvider(
Class<? extends OperationSet> opSetClass)
{
return preferredProviders.get(opSetClass);
if (preferredProviders != null && preferredProviders.size() > 0)
return preferredProviders.get(opSetClass);
return null;
}
/**
@ -132,30 +129,4 @@ public List<Class<? extends OperationSet>> getSupportedOperationSets()
{
return supportedOpSets;
}
/**
* Sets the list of supported <tt>OperationSet</tt> classes. These are meant
* to indicate what are the supported actions (sending an IM message,
* making a call, etc.).
* @param opSets the list of supported <tt>OperationSet</tt> classes
*/
public void setSupportedOperationSets(
List<Class<? extends OperationSet>> opSets)
{
this.supportedOpSets = opSets;
}
/**
* Sets a mapping of preferred <tt>ProtocolProviderServices</tt> for
* <tt>OperationSet</tt> classes.
* @param preferredProviders a mapping of preferred
* <tt>ProtocolProviderService</tt>s for specific <tt>OperationSet</tt>
* classes
*/
public void setPreferredProtocolProviders(
Map<Class<? extends OperationSet>, ProtocolProviderService>
preferredProviders)
{
this.preferredProviders = preferredProviders;
}
}

@ -6,6 +6,8 @@
*/
package net.java.sip.communicator.service.contactsource;
import java.util.*;
/**
* The <tt>ContactQuery</tt> corresponds to a particular query made through the
* <tt>ContactSourceService</tt>. Each query once started could be
@ -16,6 +18,20 @@
*/
public interface ContactQuery
{
/**
* Returns the <tt>ContactSourceService</tt>, where this query was first
* initiated.
* @return the <tt>ContactSourceService</tt>, where this query was first
* initiated
*/
public ContactSourceService getContactSource();
/**
* Returns the list of <tt>SourceContact</tt>s returned by this query.
* @return the list of <tt>SourceContact</tt>s returned by this query
*/
public List<SourceContact> getQueryResults();
/**
* Cancels this query.
*/

@ -6,8 +6,6 @@
*/
package net.java.sip.communicator.service.contactsource;
import net.java.sip.communicator.service.protocol.*;
/**
* The <tt>ContactSourceService</tt> interface is meant to be implemented
* by modules supporting large lists of contacts and wanting to enable searching
@ -17,6 +15,16 @@
*/
public interface ContactSourceService
{
public static final String CALL_HISTORY = "CallHistory";
/**
* Returns the identifier of this contact source. Some of the common
* identifiers are defined here (For example the CALL_HISTORY identifier
* should be returned by all call history implementations of this interface)
* @return the identifier of this contact source
*/
public String getIdentifier();
/**
* Returns a user-friendly string that identifies this contact source.
* @return the display name of this contact source
@ -29,11 +37,4 @@ public interface ContactSourceService
* @return the created query
*/
public ContactQuery queryContactSource(String queryString);
/**
* Returns the telephony provider preferred for calling items from this
* source.
* @return the preferred telephony provider
*/
public ProtocolProviderService getPreferredTelephonyProvider();
}

@ -50,6 +50,16 @@ public interface SourceContact
*/
public List<ContactDetail> getContactDetails();
/**
* Returns a list of all <tt>ContactDetail</tt>s supporting the given
* <tt>OperationSet</tt> class.
* @param operationSet the <tt>OperationSet</tt> class we're looking for
* @return a list of all <tt>ContactDetail</tt>s supporting the given
* <tt>OperationSet</tt> class
*/
public List<ContactDetail> getContactDetails(
Class<? extends OperationSet> operationSet);
/**
* Returns the preferred <tt>ContactDetail</tt> for a given
* <tt>OperationSet</tt> class.

@ -3,5 +3,6 @@ Bundle-Description: ContactSource Service.
Bundle-Vendor: sip-communicator.org
Bundle-Version: 0.0.1
System-Bundle: yes
Import-Package: org.osgi.framework
Import-Package: org.osgi.framework,
net.java.sip.communicator.service.protocol
Export-Package: net.java.sip.communicator.service.contactsource

@ -28,7 +28,8 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public QueryResultSet<HistoryRecord> findByStartDate(Date startDate) throws RuntimeException;
public QueryResultSet<HistoryRecord> findByStartDate(Date startDate)
throws RuntimeException;
/**
* Searches the history for all records with timestamp before
@ -40,7 +41,8 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public QueryResultSet<HistoryRecord> findByEndDate(Date endDate) throws RuntimeException;
public QueryResultSet<HistoryRecord> findByEndDate(Date endDate)
throws RuntimeException;
/**
* Searches the history for all records with timestamp between
@ -53,7 +55,8 @@ public interface HistoryReader {
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate)
public QueryResultSet<HistoryRecord> findByPeriod( Date startDate,
Date endDate)
throws RuntimeException;
/**
@ -66,7 +69,9 @@ public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate)
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public QueryResultSet<HistoryRecord> findByKeyword(String keyword, String field) throws RuntimeException;
public QueryResultSet<HistoryRecord> findByKeyword( String keyword,
String field)
throws RuntimeException;
/**
* Searches the history for all records containing the <tt>keyword</tt>.
@ -79,7 +84,9 @@ public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate)
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public QueryResultSet<HistoryRecord> findByKeyword(String keyword, String field, boolean caseSensitive)
public QueryResultSet<HistoryRecord> findByKeyword( String keyword,
String field,
boolean caseSensitive)
throws RuntimeException;
/**
@ -92,7 +99,9 @@ public QueryResultSet<HistoryRecord> findByKeyword(String keyword, String field,
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public QueryResultSet<HistoryRecord> findByKeywords(String[] keywords, String field) throws RuntimeException;
public QueryResultSet<HistoryRecord> findByKeywords(String[] keywords,
String field)
throws RuntimeException;
/**
* Searches the history for all records containing all <tt>keywords</tt>.
@ -105,7 +114,9 @@ public QueryResultSet<HistoryRecord> findByKeyword(String keyword, String field,
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public QueryResultSet<HistoryRecord> findByKeywords(String[] keywords, String field, boolean caseSensitive)
public QueryResultSet<HistoryRecord> findByKeywords(String[] keywords,
String field,
boolean caseSensitive)
throws RuntimeException;
/**
@ -121,8 +132,10 @@ public QueryResultSet<HistoryRecord> findByKeywords(String[] keywords, String fi
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate,
String[] keywords, String field)
public QueryResultSet<HistoryRecord> findByPeriod( Date startDate,
Date endDate,
String[] keywords,
String field)
throws UnsupportedOperationException;
/**
@ -139,8 +152,11 @@ public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate,
* Thrown if an exception occurs during the execution of the
* query, such as internal IO error.
*/
public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate,
String[] keywords, String field, boolean caseSensitive)
public QueryResultSet<HistoryRecord> findByPeriod( Date startDate,
Date endDate,
String[] keywords,
String field,
boolean caseSensitive)
throws UnsupportedOperationException;
/**
@ -160,7 +176,9 @@ public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate,
* @return QueryResultSet the found records
* @throws RuntimeException
*/
public QueryResultSet<HistoryRecord> findFirstRecordsAfter(Date date, int count) throws RuntimeException;
public QueryResultSet<HistoryRecord> findFirstRecordsAfter( Date date,
int count)
throws RuntimeException;
/**
* Returns the supplied number of recent messages before the given date
@ -170,22 +188,26 @@ public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate,
* @return QueryResultSet the found records
* @throws RuntimeException
*/
public QueryResultSet<HistoryRecord> findLastRecordsBefore(Date date, int count) throws RuntimeException;
public QueryResultSet<HistoryRecord> findLastRecordsBefore( Date date,
int count)
throws RuntimeException;
/**
* Adding progress listener for monitoring progress of search process
*
* @param listener HistorySearchProgressListener
*/
public void addSearchProgressListener(HistorySearchProgressListener listener);
public void addSearchProgressListener(
HistorySearchProgressListener listener);
/**
* Removing progress listener
*
* @param listener HistorySearchProgressListener
*/
public void removeSearchProgressListener(HistorySearchProgressListener listener);
public void removeSearchProgressListener(
HistorySearchProgressListener listener);
/**
* Total count of records that current history reader will read through
*
@ -196,4 +218,9 @@ public QueryResultSet<HistoryRecord> findByPeriod(Date startDate, Date endDate,
*/
public int countRecords()
throws UnsupportedOperationException;
/**
* Cancels the current find. If there's no find going on, then does nothing.
*/
public void cancelCurrentFind();
}

@ -17,14 +17,13 @@
import javax.swing.*;
import javax.swing.text.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.util.*;
/**
* A TransferHandler that we use to handle copying, pasting and DnD operations.
* The string handler is heavily inspired by Sun's
* <tt>DefaultTransferHandler</tt> with the main difference being that
* we only accept pasting of plain text. We do this in order to avoid html
* we only accept pasting of plain text. We do this in order to avoid HTML
* support problems that appear when pasting formatted text into our editable
* area.
*
@ -34,12 +33,6 @@
public class ExtendedTransferHandler
extends TransferHandler
{
/**
* The data flavor used when transferring <tt>MetaContact</tt>s.
*/
protected static final DataFlavor metaContactDataFlavor
= new DataFlavor(MetaContact.class, "MetaContact");
/**
* Returns the type of transfer actions supported by the source;
* any bitwise-OR combination of <tt>COPY</tt>, <tt>MOVE</tt>

@ -27,12 +27,14 @@ public class SIPCommTextButton extends JButton
private final float[] borderColor
= Color.DARK_GRAY.getRGBComponents(null);
private Image bgImage;
/**
* Creates a <tt>SIPCommTextButton</tt>.
*/
public SIPCommTextButton()
{
this(null);
this("", null);
}
/**
@ -40,9 +42,16 @@ public SIPCommTextButton()
* @param text the text of the button
*/
public SIPCommTextButton(String text)
{
this(text, null);
}
public SIPCommTextButton(String text, Image bgImage)
{
super(text);
this.bgImage = bgImage;
MouseRolloverHandler mouseHandler = new MouseRolloverHandler();
this.addMouseListener(mouseHandler);
@ -55,11 +64,15 @@ public SIPCommTextButton(String text)
* Explicitly remove all borders that may be set from the current look
* and feel.
*/
this.setBorder(BorderFactory.createEmptyBorder(4, 10, 4, 10));
this.setContentAreaFilled(false);
this.setUI(new BasicButtonUI());
}
public void setBgImage(Image image)
{
this.bgImage = image;
}
/**
* Overrides the <code>paintComponent</code> method of <tt>JButton</tt> to
* paint the button background and icon, and all additional effects of this
@ -101,19 +114,37 @@ private void internalPaintComponent(Graphics2D g)
visibility /= 2;
g.setColor(getBackground());
g.fillRoundRect(1, 1,
this.getWidth() - 2, this.getHeight() - 2,
20, 20);
if (visibility != 0.0f)
{
g.setColor(new Color(borderColor[0], borderColor[1],
borderColor[2], visibility));
g.drawRoundRect(0, 0,
this.getWidth() - 1, this.getHeight() - 1,
if (bgImage != null)
g.fillRoundRect((this.getWidth() - bgImage.getWidth(null))/2,
(this.getHeight() - bgImage.getHeight(null))/2,
bgImage.getWidth(null) - 1,
bgImage.getHeight(null) - 1,
20, 20);
else
g.fillRoundRect(0, 0,
this.getWidth() - 1, this.getHeight() - 1,
20, 20);
}
if (bgImage != null)
{
g.drawImage(bgImage,
(this.getWidth() - bgImage.getWidth(null))/2,
(this.getHeight() - bgImage.getHeight(null))/2, null);
}
else
{
g.setColor(getBackground());
g.fillRoundRect(1, 1,
this.getWidth() - 2, this.getHeight() - 2,
20, 20);
}
}
/**

@ -8,8 +8,12 @@
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import net.java.sip.communicator.util.swing.event.*;
/**
* The <tt>SIPCommTextField</tt> is a <tt>JTextField</tt> that offers the
@ -21,9 +25,24 @@ public class SIPCommTextField
extends JTextField
implements MouseListener,
FocusListener,
KeyListener
KeyListener,
DocumentListener
{
private final String defaultText;
/**
* The default text.
*/
private String defaultText;
/**
* A list of all listeners registered for text field change events.
*/
private Collection<TextFieldChangeListener> changeListeners
= new LinkedList<TextFieldChangeListener>();
/**
* Indicates if the default text is currently visible.
*/
private boolean isDefaultTextVisible;
/**
* Creates an instance of <tt>SIPCommTextField</tt> by specifying the text
@ -34,7 +53,11 @@ public SIPCommTextField(String text)
{
super(text);
this.defaultText = text;
if (text != null && text.length() > 0)
{
this.defaultText = text;
isDefaultTextVisible = true;
}
this.setFont(getFont().deriveFont(10f));
this.setForeground(Color.GRAY);
@ -43,6 +66,7 @@ public SIPCommTextField(String text)
this.addFocusListener(this);
this.addKeyListener(this);
this.getDocument().addDocumentListener(this);
}
/**
@ -154,4 +178,71 @@ public void keyTyped(KeyEvent e)
}
public void keyReleased(KeyEvent e){}
/**
* Adds the given <tt>TextFieldChangeListener</tt> to the list of listeners
* notified on changes of the text contained in this field.
* @param l the <tt>TextFieldChangeListener</tt> to add
*/
public void addTextChangeListener(TextFieldChangeListener l)
{
synchronized (changeListeners)
{
changeListeners.add(l);
}
}
/**
* Removes the given <tt>TextFieldChangeListener</tt> from the list of
* listeners notified on changes of the text contained in this field.
* @param l the <tt>TextFieldChangeListener</tt> to add
*/
public void removeTextChangeListener(TextFieldChangeListener l)
{
synchronized (changeListeners)
{
changeListeners.remove(l);
}
}
public void changedUpdate(DocumentEvent e) {}
/**
* Handles the change when a char has been inserted in the field.
* @param e the <tt>DocumentEvent</tt> that notified us
*/
public void insertUpdate(DocumentEvent e)
{
if(!super.getText().equals(defaultText))
fireTextFieldChangeListener(0);
else
isDefaultTextVisible = true;
}
/**
* Handles the change when a char has been removed from the field.
* @param e the <tt>DocumentEvent</tt> that notified us
*/
public void removeUpdate(DocumentEvent e)
{
if (!isDefaultTextVisible)
fireTextFieldChangeListener(1);
else
isDefaultTextVisible = false;
}
/**
* Notifies all registered <tt>TextFieldChangeListener</tt>s that a change
* has occurred in the text contained in this field.
* @param eventType the type of the event to transfer
*/
private void fireTextFieldChangeListener(int eventType)
{
for (TextFieldChangeListener l : changeListeners)
switch (eventType)
{
case 0: l.textInserted(); break;
case 1: l.textRemoved(); break;
}
}
}

@ -0,0 +1,27 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.util.swing.event;
/**
* The <tt>TextFieldChangeListener</tt> listens for any changes in the text
* contained in a <tt>SIPCommTextField</tt>. It is notified every time a char
* is inserted or removed from the field.
*
* @author Yana Stamcheva
*/
public interface TextFieldChangeListener
{
/**
* Indicates that a text has been removed from the text field.
*/
public void textRemoved();
/**
* Indicates that a text has been inserted to the text field.
*/
public void textInserted();
}
Loading…
Cancel
Save