diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallComboBox.java b/src/net/java/sip/communicator/impl/gui/main/call/CallComboBox.java
new file mode 100644
index 000000000..d0bb1c47f
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallComboBox.java
@@ -0,0 +1,276 @@
+/*
+ * 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 java.beans.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.AbstractListModel;
+import javax.swing.ComboBoxEditor;
+import javax.swing.MutableComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+import net.java.sip.communicator.impl.gui.main.contactlist.*;
+import net.java.sip.communicator.service.contactlist.*;
+
+/**
+ * The CallComboBox is a history editable combo box that is positioned
+ * above call and hangup buttons and is used when writing a number or a contact
+ * name in order to be called.
+ *
+ * @author Yana Stamcheva
+ */
+public class CallComboBox
+ extends JComboBox
+ implements ActionListener,
+ FocusListener
+{
+
+ private ArrayList historyList = new ArrayList();
+
+ private CallManager callManager;
+
+ public CallComboBox(CallManager callManager) {
+
+ this.callManager = callManager;
+
+ historyList.add("gigi@ekyga.net");
+ historyList.add("sc3@voipgw.u-strasbg.fr");
+
+ setModel(new FilterableComboBoxModel(historyList));
+ setEditor(new CallComboEditor());
+ setEditable(true);
+ setFocusable(true);
+
+ this.addActionListener(this);
+ this.getEditor().getEditorComponent().addFocusListener(this);
+ }
+
+ /**
+ * Checks if this combo box editor field is empty. This will be the case if
+ * the user hasn't selected an item from the combobox and hasn't written
+ * anything the field.
+ *
+ * @return TRUE if the combobox editor field is empty, otherwise FALSE
+ */
+ public boolean isComboFieldEmpty()
+ {
+ String item = ((CallComboEditor)this.getEditor()).getItem().toString();
+
+ if(item.length() > 0)
+ return false;
+ else
+ return true;
+ }
+
+ /**
+ * Handles events triggered by user selection. Enables the call button
+ * when user selects something the combo box.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if(!isComboFieldEmpty()) {
+
+ callManager.setCallMetaContact(false);
+ callManager.getCallButton().setEnabled(true);
+ }
+ }
+
+ public void focusGained(FocusEvent e)
+ {
+ this.callManager.setCallMetaContact(false);
+
+ ContactList clist = this.callManager.getMainFrame()
+ .getContactListPanel().getContactList();
+ clist.removeSelectionInterval(
+ clist.getSelectedIndex(), clist.getSelectedIndex());
+ }
+
+ public void focusLost(FocusEvent e)
+ {}
+
+ public class FilterableComboBoxModel extends AbstractListModel
+ implements MutableComboBoxModel {
+
+ private List items;
+ private Filter filter;
+ private List filteredItems;
+ private Object selectedItem;
+
+ public FilterableComboBoxModel(List items) {
+
+ this.items = new ArrayList(items);
+ filteredItems = new ArrayList(items.size());
+ updateFilteredItems();
+ }
+
+ public void addElement( Object obj ) {
+
+ items.add(obj);
+ updateFilteredItems();
+ }
+
+ public void removeElement( Object obj ) {
+
+ items.remove(obj);
+ updateFilteredItems();
+ }
+
+ public void removeElementAt(int index) {
+
+ items.remove(index);
+ updateFilteredItems();
+ }
+
+ public void insertElementAt( Object obj, int index ) {}
+
+ public void setFilter(Filter filter) {
+
+ this.filter = filter;
+ updateFilteredItems();
+ }
+
+ protected void updateFilteredItems() {
+
+ fireIntervalRemoved(this, 0, filteredItems.size());
+ filteredItems.clear();
+
+ if (filter == null)
+ filteredItems.addAll(items);
+ else {
+ for (Iterator iterator = items.iterator(); iterator.hasNext();) {
+
+ Object item = iterator.next();
+
+ if (filter.accept(item))
+ filteredItems.add(item);
+ }
+ }
+ fireIntervalAdded(this, 0, filteredItems.size());
+ }
+
+ public int getSize() {
+ return filteredItems.size();
+ }
+
+ public Object getElementAt(int index) {
+ return filteredItems.get(index);
+ }
+
+ public Object getSelectedItem() {
+ return selectedItem;
+ }
+
+ public void setSelectedItem(Object val) {
+
+ if ((selectedItem == null) && (val == null))
+ return;
+
+ if ((selectedItem != null) && selectedItem.equals(val))
+ return;
+
+ if ((val != null) && val.equals(selectedItem))
+ return;
+
+ selectedItem = val;
+ fireContentsChanged(this, -1, -1);
+ }
+ }
+
+ public static interface Filter {
+ public boolean accept(Object obj);
+ }
+
+ class StartsWithFilter implements Filter {
+
+ private String prefix;
+ public StartsWithFilter(String prefix) { this.prefix = prefix; }
+ public boolean accept(Object o) { return o.toString().startsWith(prefix); }
+ }
+
+ public class CallComboEditor
+ implements ComboBoxEditor, DocumentListener
+ {
+ private JTextField text;
+ private volatile boolean filtering = false;
+ private volatile boolean setting = false;
+
+ public CallComboEditor() {
+
+ text = new JTextField(15);
+ text.getDocument().addDocumentListener(this);
+ }
+
+ public Component getEditorComponent() { return text; }
+
+ public void setItem(Object item) {
+
+ if(filtering)
+ return;
+
+ setting = true;
+ String newText = (item == null) ? "" : item.toString();
+ text.setText(newText);
+ setting = false;
+ }
+
+ public Object getItem() {
+ return text.getText();
+ }
+
+ public void selectAll() { text.selectAll(); }
+
+ public void addActionListener(ActionListener l) {
+ text.addActionListener(l);
+ }
+
+ public void removeActionListener(ActionListener l) {
+ text.removeActionListener(l);
+ }
+
+ public void insertUpdate(DocumentEvent e) { handleChange(); }
+ public void removeUpdate(DocumentEvent e) { handleChange(); }
+ public void changedUpdate(DocumentEvent e) { }
+
+ protected void handleChange() {
+ if (setting)
+ return;
+
+ filtering = true;
+
+ Filter filter = null;
+ if (text.getText().length() > 0) {
+ filter = new StartsWithFilter(text.getText());
+
+ callManager.setCallMetaContact(false);
+
+ callManager.getCallButton().setEnabled(true);
+ }
+ else {
+ Object o = callManager.getMainFrame().getContactListPanel()
+ .getContactList().getSelectedValue();
+
+ if(o == null || !(o instanceof MetaContact))
+ callManager.getCallButton().setEnabled(false);
+ }
+
+ ((FilterableComboBoxModel) getModel()).setFilter(filter);
+ // A bit nasty but it seems to get the popup validated properly
+ setPopupVisible(false);
+ setPopupVisible(true);
+ filtering = false;
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java
index 7c5f34d63..4e6d460fd 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java
@@ -12,28 +12,35 @@
import java.util.*;
import javax.swing.*;
+import javax.swing.Timer;
+import javax.swing.event.*;
import net.java.sip.communicator.impl.gui.customcontrols.*;
+import net.java.sip.communicator.impl.gui.i18n.*;
import net.java.sip.communicator.impl.gui.main.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.contactlist.*;
import net.java.sip.communicator.service.protocol.*;
-import java.text.*;
-
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
/**
- * The CallPanel is the panel that contains the "Call" and "Hangup"
- * buttons, as well as the field, where user could enter the phone number or
- * the contact name of the person, to which he would like to call.
+ * The CallManager is the one that handles calls. It contains also the
+ * "Call" and "Hangup" buttons panel. Here are handles incoming and outgoing
+ * calls from and to the call operation set.
*
* @author Yana Stamcheva
*/
public class CallManager
extends JPanel
- implements ActionListener
+ implements ActionListener,
+ CallListener,
+ ListSelectionListener,
+ ChangeListener
{
+ private Logger logger = Logger.getLogger(CallManager.class.getName());
- private JComboBox phoneNumberCombo = new JComboBox();
+ private CallComboBox phoneNumberCombo;
private JPanel comboPanel = new JPanel(new BorderLayout());
@@ -64,9 +71,11 @@ public class CallManager
private Hashtable activeCalls = new Hashtable();
private boolean isShown;
+
+ private boolean isCallMetaContact;
/**
- * Creates an instance of CallPanel.
+ * Creates an instance of CallManager.
* @param mainFrame The main application window.
*/
public CallManager(MainFrame mainFrame)
@@ -75,6 +84,8 @@ public CallManager(MainFrame mainFrame)
this.mainFrame = mainFrame;
+ phoneNumberCombo = new CallComboBox(this);
+
this.buttonsPanel
.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
@@ -89,7 +100,7 @@ public CallManager(MainFrame mainFrame)
private void init()
{
this.phoneNumberCombo.setEditable(true);
-
+
this.comboPanel.add(phoneNumberCombo, BorderLayout.CENTER);
// this.add(comboPanel, BorderLayout.NORTH);
@@ -105,41 +116,60 @@ private void init()
this.buttonsPanel.add(callButton);
this.buttonsPanel.add(hangupButton);
+
+ this.callButton.setEnabled(false);
+
+ this.hangupButton.setEnabled(false);
this.add(minimizeButtonPanel, BorderLayout.SOUTH);
}
- /**
- * Returns the combo box, where user enters the phone number to call to.
- * @return the combo box, where user enters the phone number to call to.
- */
- public JComboBox getPhoneNumberCombo()
- {
- return phoneNumberCombo;
- }
-
+
/**
* Handles the ActionEvent generated when user presses one of the
* buttons in this panel.
*/
- public void actionPerformed(ActionEvent e)
+ public void actionPerformed(ActionEvent evt)
{
- JButton button = (JButton) e.getSource();
+ JButton button = (JButton) evt.getSource();
String buttonName = button.getName();
- if (buttonName.equalsIgnoreCase("call")) {
+ if (buttonName.equals("call")) {
OperationSetBasicTelephony telephony;
-
- Object o = mainFrame.getContactListPanel()
- .getContactList().getSelectedValue();
-
- if(o != null && o instanceof MetaContact) {
+
+ Object o = mainFrame.getContactListPanel().getContactList()
+ .getSelectedValue();
+
+ //call button is pressed over an already open call panel
+ if(mainFrame.getSelectedCallPanel() != null
+ && mainFrame.getSelectedCallPanel().getCallType()
+ == CallPanel.INCOMING_CALL
+ && mainFrame.getSelectedCallPanel().getCall().getCallState()
+ == CallState.CALL_INITIALIZATION) {
+
+ CallPanel callPanel = mainFrame.getSelectedCallPanel();
+
+ Call call = callPanel.getCall();
+
+ ProtocolProviderService pps
+ = call.getProtocolProvider();
+
+ Iterator participants = call.getCallParticipants();
+
+ while(participants.hasNext()) {
+ CallParticipant participant
+ = (CallParticipant)participants.next();
+ answerCall(pps, participant);
+ }
+ }
+ //call button is pressed when a meta contact is selected
+ else if(isCallMetaContact && o != null && o instanceof MetaContact) {
+
MetaContact metaContact
= (MetaContact)o;
- Contact contact
- = getTelephonyContact(metaContact);
-
+ Contact contact = getTelephonyContact(metaContact);
+
if(contact != null) {
telephony
= mainFrame.getTelephony(contact.getProtocolProvider());
@@ -149,24 +179,30 @@ public void actionPerformed(ActionEvent e)
createdCall = telephony.createCall(contact);
CallPanel callPanel = new CallPanel(
- createdCall, CallPanel.OUTGOING_CALL);
+ this, createdCall, CallPanel.OUTGOING_CALL);
+
mainFrame.addCallPanel(callPanel);
activeCalls.put(createdCall, callPanel);
}
- catch (OperationFailedException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
+ catch (OperationFailedException e) {
+ logger.error("The call could not be created: " + e);
}
}
else {
- //Message to user which says "This contact could not be called!"
+ JOptionPane.showMessageDialog(this.mainFrame,
+ Messages.getString("contactNotSupportingTelephony"),
+ Messages.getString("warning"),
+ JOptionPane.WARNING_MESSAGE);
}
}
- else if(phoneNumberCombo.getSelectedItem() != null) {
+ //if no contact is selected checks if the user has chosen or has
+ //writen something in the phone combo box
+ else if(!phoneNumberCombo.isComboFieldEmpty()) {
+
ProtocolProviderService pps
= getDefaultTelephonyProvider();
-
+
if(pps != null) {
telephony = mainFrame.getTelephony(pps);
@@ -176,27 +212,50 @@ else if(phoneNumberCombo.getSelectedItem() != null) {
phoneNumberCombo.getSelectedItem().toString());
CallPanel callPanel = new CallPanel(
- createdCall, CallPanel.OUTGOING_CALL);
+ this, createdCall, CallPanel.OUTGOING_CALL);
+
mainFrame.addCallPanel(callPanel);
activeCalls.put(createdCall, callPanel);
}
- catch (OperationFailedException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
+ catch (OperationFailedException e) {
+ logger.error("The call could not be created: " + e);
}
- catch (ParseException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
+ catch (ParseException e) {
+ logger.error("The call could not be created: " + e);
}
}
}
- else {
- //Message to user which says "You must select a contact to call or enter a phone number"
- }
}
else if (buttonName.equalsIgnoreCase("hangup")) {
-
+ CallPanel selectedCallPanel = this.mainFrame.getSelectedCallPanel();
+
+ if(selectedCallPanel != null) {
+ Call call = selectedCallPanel.getCall();
+
+ ProtocolProviderService pps
+ = call.getProtocolProvider();
+
+ OperationSetBasicTelephony telephony
+ = mainFrame.getTelephony(pps);
+
+ Iterator participants = call.getCallParticipants();
+
+ while(participants.hasNext()) {
+ try {
+ //now we hang up the first call participant in the call
+ telephony.hangupCallParticipant(
+ (CallParticipant)participants.next());
+ }
+ catch (OperationFailedException e) {
+ logger.error("Hang up was not successful: " + e);
+ }
+ }
+
+ activeCalls.remove(call);
+ mainFrame.removeCallPanel(selectedCallPanel);
+ updateButtonsStateAccordingToSelectedPanel();
+ }
}
else if (buttonName.equalsIgnoreCase("minimize")) {
@@ -225,34 +284,6 @@ else if (buttonName.equalsIgnoreCase("restore")) {
}
}
- /**
- * Returns TRUE if this panel is visible, FALSE otherwise.
- * @return TRUE if this panel is visible, FALSE otherwise
- */
- public boolean isShown()
- {
- return this.isShown;
- }
-
- /**
- * When TRUE shows this panel, when FALSE hides it.
- * @param isShown
- */
- public void setShown(boolean isShown)
- {
- this.isShown = isShown;
-
- if(isShown) {
- this.add(comboPanel, BorderLayout.NORTH);
- this.add(buttonsPanel, BorderLayout.CENTER);
-
- this.minimizeButtonPanel.add(minimizeButton);
- }
- else {
- this.minimizeButtonPanel.add(restoreButton);
- }
- }
-
/**
* For the given MetaContact returns the protocol contact that supports
* a basic telephony operation.
@@ -263,19 +294,15 @@ public void setShown(boolean isShown)
private Contact getTelephonyContact(
MetaContact metaContact)
{
- String telephonySet
- = OperationSetBasicTelephony.class.getName();
-
Iterator i = metaContact.getContacts();
while(i.hasNext()) {
Contact contact = (Contact)i.next();
- if(contact.getProtocolProvider()
- .getSupportedOperationSets()
- .containsKey(telephonySet)) {
-
+ OperationSetBasicTelephony telephony
+ = mainFrame.getTelephony(contact.getProtocolProvider());
+
+ if(telephony != null)
return contact;
- }
}
return null;
}
@@ -294,7 +321,7 @@ private ProtocolProviderService getDefaultTelephonyProvider() {
while(i.hasNext()) {
ProtocolProviderService pps
= (ProtocolProviderService) i.next();
-
+
if(pps.getSupportedOperationSets()
.containsKey(telephonySet)) {
return pps;
@@ -302,4 +329,212 @@ private ProtocolProviderService getDefaultTelephonyProvider() {
}
return null;
}
+
+ /**
+ * Implements CallListener.incomingCallReceived. When a call is received
+ * creates a call panel and adds it to the main tabbed pane and plays the
+ * ring phone sound to the user.
+ */
+ public void incomingCallReceived(CallEvent event)
+ {
+ Call sourceCall = event.getSourceCall();
+
+ CallPanel callPanel = new CallPanel(
+ this, sourceCall, CallPanel.INCOMING_CALL);
+
+ mainFrame.addCallPanel(callPanel);
+
+ this.callButton.setEnabled(true);
+ this.hangupButton.setEnabled(true);
+
+ SoundLoader.playInLoop(Constants.getDefaultIncomingCallAudio(), 2000);
+
+ activeCalls.put(sourceCall, callPanel);
+ }
+
+ /**
+ * Implements CallListener.callEnded. Stops sounds that are playing at the
+ * moment if there're any. Removes the call panel and disables the hangup
+ * button.
+ */
+ public void callEnded(CallEvent event)
+ {
+ SoundLoader.getSound(SoundLoader.BUSY).stop();
+ SoundLoader.stop(Constants.getDefaultIncomingCallAudio());
+
+ Call sourceCall = event.getSourceCall();
+
+ if(activeCalls.get(sourceCall) != null) {
+ CallPanel callPanel = (CallPanel)activeCalls.get(sourceCall);
+ this.removeCallPanel(callPanel);
+ }
+ }
+
+ public void outgoingCallCreated(CallEvent event)
+ {}
+
+ /**
+ * Removes the given call panel tab.
+ * @param callPanel the CallPanel to remove
+ */
+ public void removeCallPanel(CallPanel callPanel)
+ {
+ Timer timer = new Timer(5000, new RemoveCallPanelListener(callPanel));
+
+ timer.setRepeats(false);
+ timer.start();
+ }
+
+
+ /**
+ * Removes the given CallPanel from the main tabbed pane.
+ */
+ private class RemoveCallPanelListener implements ActionListener
+ {
+ private CallPanel callPanel;
+ public RemoveCallPanelListener(CallPanel callPanel)
+ {
+ this.callPanel = callPanel;
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ activeCalls.remove(callPanel.getCall());
+ mainFrame.removeCallPanel(callPanel);
+ updateButtonsStateAccordingToSelectedPanel();
+ }
+ }
+
+ /**
+ * Implements ListSelectionListener.valueChanged. Enables or disables
+ * call and hangup buttons depending on the selection in the contactlist.
+ */
+ public void valueChanged(ListSelectionEvent e)
+ {
+ Object o = mainFrame.getContactListPanel()
+ .getContactList().getSelectedValue();
+
+ if((e.getFirstIndex() != -1 || e.getLastIndex() != -1)
+ && (o instanceof MetaContact)) {
+ setCallMetaContact(true);
+ callButton.setEnabled(true);
+ }
+ else if(phoneNumberCombo.isComboFieldEmpty()) {
+ callButton.setEnabled(false);
+ }
+ }
+
+ /**
+ * Implements ChangeListener.stateChanged. Enables the hangup button if
+ * ones selects a tab in the main tabbed pane that contains a call panel.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ this.updateButtonsStateAccordingToSelectedPanel();
+ }
+
+ private void updateButtonsStateAccordingToSelectedPanel()
+ {
+ if(mainFrame.getSelectedCallPanel() != null) {
+ this.hangupButton.setEnabled(true);
+ }
+ else {
+ this.hangupButton.setEnabled(false);
+ }
+ }
+
+ /**
+ * Returns the call button.
+ * @return the call button
+ */
+ public SIPCommButton getCallButton()
+ {
+ return callButton;
+ }
+
+ /**
+ * Returns the hangup button.
+ * @return the hangup button
+ */
+ public SIPCommButton getHangupButton()
+ {
+ return hangupButton;
+ }
+
+ /**
+ * Returns the main application frame. Meant to be used from the contained
+ * components that do not have direct access to the MainFrame.
+ * @return the main application frame
+ */
+ public MainFrame getMainFrame()
+ {
+ return mainFrame;
+ }
+
+ /**
+ * Returns the combo box, where user enters the phone number to call to.
+ * @return the combo box, where user enters the phone number to call to.
+ */
+ public JComboBox getPhoneNumberCombo()
+ {
+ return phoneNumberCombo;
+ }
+
+ /**
+ * Returns TRUE if this panel is visible, FALSE otherwise.
+ * @return TRUE if this panel is visible, FALSE otherwise
+ */
+ public boolean isShown()
+ {
+ return this.isShown;
+ }
+
+ /**
+ * When TRUE shows this panel, when FALSE hides it.
+ * @param isShown
+ */
+ public void setShown(boolean isShown)
+ {
+ this.isShown = isShown;
+
+ if(isShown) {
+ this.add(comboPanel, BorderLayout.NORTH);
+ this.add(buttonsPanel, BorderLayout.CENTER);
+
+ this.minimizeButtonPanel.add(minimizeButton);
+ }
+ else {
+ this.minimizeButtonPanel.add(restoreButton);
+ }
+ }
+
+ /**
+ * Answers the given callParticipant.
+ * @param pps the protocol provider to use
+ * @param callParticipant the call participant to answer to
+ */
+ public void answerCall(ProtocolProviderService pps,
+ CallParticipant callParticipant)
+ {
+ OperationSetBasicTelephony telephony = mainFrame.getTelephony(pps);
+
+ try {
+ telephony.answerCallParticipant(callParticipant);
+ }
+ catch (OperationFailedException e) {
+ logger.error("Could not answer to : " + callParticipant
+ + " caused by the following exception: " + e);
+ }
+ }
+
+ public boolean isCallMetaContact()
+ {
+ return isCallMetaContact;
+ }
+
+ public void setCallMetaContact(boolean isCallMetaContact)
+ {
+ this.isCallMetaContact = isCallMetaContact;
+ }
+
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java
index 66e85516d..643490706 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallPanel.java
@@ -6,43 +6,283 @@
*/
package net.java.sip.communicator.impl.gui.main.call;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+
import javax.swing.*;
+import javax.swing.Timer;
+import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+/**
+ * The CallPanel is the panel containing call information. It's created
+ * and added to the main tabbed pane when user makes or receives calls. It
+ * shows information about call participants, call duration, etc.
+ *
+ * @author Yana Stamcheva
+ */
public class CallPanel
- extends JPanel
+ extends JScrollPane
+ implements CallChangeListener,
+ CallParticipantListener
{
public static final String INCOMING_CALL = "IncomingCall";
public static final String OUTGOING_CALL = "OutgoingCall";
+ private JPanel mainPanel = new JPanel();
+
+ private Hashtable participantsPanels = new Hashtable();
+
+ private CallManager callManager;
+
+ private String title;
+
private Call call;
- public CallPanel(Call call, String callType)
+ private String callType;
+
+ /**
+ * Creates an instance of CallPanel for the given call and call type.
+ * @param call the call
+ * @param callType INCOMING_CALL and OUTGOING_CALL
+ */
+ public CallPanel(CallManager callManager, Call call, String callType)
{
this.call = call;
- if(callType.equals(INCOMING_CALL)) {
- this.processIncomingCall();
- }
- else {
- this.processOutgoingCall();
- }
+ this.callType = callType;
+
+ this.callManager = callManager;
+ this.call.addCallChangeListener(this);
+
+ this.mainPanel.setBorder(
+ BorderFactory.createEmptyBorder(20, 10, 20, 10));
+
+ int participantsCount = call.getCallParticipantsCount();
+
+ if(participantsCount > 0) {
+ CallParticipant participant
+ = ((CallParticipant)call.getCallParticipants().next());
+
+ if(participant.getDisplayName() != null)
+ this.title = participant.getDisplayName();
+ else
+ this.title = participant.getAddress();
+
+ if(participantsCount < 2) {
+ this.mainPanel.setLayout(new BorderLayout());
+ }
+ else {
+ int rows = participantsCount/2 + participantsCount%2;
+ this.mainPanel.setLayout(new GridLayout(rows, 2));
+ }
+ }
+
+ this.getViewport().add(mainPanel);
+
+ this.init();
}
- private void processIncomingCall()
- {
+ /**
+ * Initializes all panels which will contain participants information.
+ */
+ public void init()
+ {
+ Iterator i = call.getCallParticipants();
+
+ while(i.hasNext()) {
+ CallParticipant participant = (CallParticipant)i.next();
+ this.addCallParticipant(participant);
+ }
}
- private void processOutgoingCall()
+ /**
+ * Cteates and adds a panel for a call participant.
+ *
+ * @param participant the call participant
+ */
+ private void addCallParticipant(CallParticipant participant)
{
+ if(participantsPanels.get(participant) != null)
+ return;
+
+ CallParticipantPanel participantPanel
+ = new CallParticipantPanel(participant);
+ this.mainPanel.add(participantPanel);
+ this.participantsPanels.put(participant, participantPanel);
+
+ participant.addCallParticipantListener(this);
}
+ /**
+ * Returns the title of this call panel. The title is now the name of the
+ * first participant in the list of the call participants. Should be
+ * improved in the future.
+ *
+ * @return the title of this call panel
+ */
public String getTitle()
{
- return null;
+ return title;
+ }
+
+ /**
+ * Implements the CallChangeListener.callParticipantAdded method.
+ * When a new participant is added to our call add it to the call panel.
+ */
+ public void callParticipantAdded(CallParticipantEvent evt)
+ {
+ if(evt.getSourceCall() == call) {
+ this.addCallParticipant(evt.getSourceCallParticipant());
+ this.revalidate();
+ this.repaint();
+ }
+ }
+
+ /**
+ * Implements the CallChangeListener.callParticipantRemoved method.
+ * When a call participant is removed from our call remove it from the
+ * call panel.
+ */
+ public void callParticipantRemoved(CallParticipantEvent evt)
+ {
+ if(evt.getSourceCall() == call) {
+ CallParticipant participant = evt.getSourceCallParticipant();
+
+ CallParticipantPanel participantPanel
+ = (CallParticipantPanel)participantsPanels.get(participant);
+
+ if(participantPanel != null) {
+
+ CallParticipantState state = participant.getState();
+
+ participantPanel.setState(state.getStateString());
+
+ participantPanel.stopCallTimer();
+
+ this.participantsPanels.remove(participant);
+
+ if(participantsPanels.size() == 0)
+ this.callManager.removeCallPanel(this);
+ else {
+ Timer timer = new Timer(5000,
+ new RemoveParticipantPanelListener(participantPanel));
+
+ timer.setRepeats(false);
+ timer.start();
+ }
+
+ this.revalidate();
+ this.repaint();
+ }
+ }
+ }
+
+ public void callStateChanged(CallChangeEvent evt)
+ {
+ }
+
+ /**
+ * Implements the CallParicipantChangeListener.participantStateChanged
+ * method.
+ */
+ public void participantStateChanged(CallParticipantChangeEvent evt)
+ {
+ CallParticipant sourceParticipant = evt.getSourceCallParticipant();
+
+ if(sourceParticipant.getCall() != call)
+ return;
+
+ CallParticipantPanel participantPanel
+ = (CallParticipantPanel)participantsPanels.get(sourceParticipant);
+
+ participantPanel.setState(
+ sourceParticipant.getState().getStateString());
+
+ if(evt.getNewValue() == CallParticipantState.ALERTING_REMOTE_SIDE) {
+ SoundLoader.playInLoop(
+ Constants.getDefaultOutgoingCallAudio(), 3000);
+ }
+ else if(evt.getNewValue() == CallParticipantState.BUSY) {
+ SoundLoader.stop(Constants.getDefaultOutgoingCallAudio());
+ SoundLoader.getSound(SoundLoader.BUSY).loop();
+ }
+ else if(evt.getNewValue() == CallParticipantState.CONNECTED) {
+ //start the timer that takes care of refreshing the time label
+ participantPanel.startCallTimer();
+ }
+ else if(evt.getNewValue() == CallParticipantState.CONNECTING) {
+ }
+ else if(evt.getNewValue() == CallParticipantState.DISCONNECTED) {
+ //The call participant should be already removed from the call
+ //see callParticipantRemoved
+ }
+ else if(evt.getNewValue() == CallParticipantState.FAILED) {
+ //The call participant should be already removed from the call
+ //see callParticipantRemoved
+ }
+ else if(evt.getNewValue() == CallParticipantState.INCOMING_CALL) {
+ }
+ else if(evt.getNewValue() == CallParticipantState.INITIATING_CALL) {
+ }
+ else if(evt.getNewValue() == CallParticipantState.ON_HOLD) {
+ }
+ else if(evt.getNewValue() == CallParticipantState.UNKNOWN) {
+ }
+ }
+
+ public void participantDisplayNameChanged(CallParticipantChangeEvent evt)
+ {
+ }
+
+ public void participantAddressChanged(CallParticipantChangeEvent evt)
+ {
}
+
+ public void participantImageChanged(CallParticipantChangeEvent evt)
+ {
+ }
+
+ /**
+ * Returns the call for this call panel.
+ * @return the call for this call panel
+ */
+ public Call getCall()
+ {
+ return call;
+ }
+
+ /**
+ * Removes the given CallParticipant panel from this CallPanel.
+ */
+ private class RemoveParticipantPanelListener implements ActionListener
+ {
+ private JPanel participantPanel;
+ public RemoveParticipantPanelListener(JPanel participantPanel)
+ {
+ this.participantPanel = participantPanel;
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ mainPanel.remove(participantPanel);
+ }
+ }
+
+ /**
+ * Returns this call type : INCOMING_CALL or OUTGOING_CALL
+ * @return Returns this call type : INCOMING_CALL or OUTGOING_CALL
+ */
+ public String getCallType()
+ {
+ return callType;
+ }
+
+
}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java
new file mode 100644
index 000000000..38bd14abd
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java
@@ -0,0 +1,137 @@
+/*
+ * 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.utils.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * The CallParticipantPanel is the panel containing data for a call
+ * participant in a given call. It contains information like call participant
+ * name, photo, call duration, etc.
+ *
+ * @author Yana Stamcheva
+ */
+public class CallParticipantPanel extends JPanel
+{
+ JPanel contactPanel = new JPanel(new BorderLayout());
+
+ JPanel namePanel = new JPanel(new GridLayout(0, 1));
+
+ JLabel nameLabel = new JLabel("", JLabel.CENTER);
+
+ JLabel timeLabel = new JLabel("00:00:00", JLabel.CENTER);
+
+ JLabel photoLabel = new JLabel(new ImageIcon(
+ ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO)));
+
+ Timer timer = new Timer(1000, new CallTimerListener());
+
+ JLabel stateLabel;
+
+ /**
+ * Creates a CallParticipantPanel for the given call participant.
+ *
+ * @param participant the call participant
+ */
+ public CallParticipantPanel(CallParticipant participant)
+ {
+ super(new BorderLayout());
+
+ stateLabel = new JLabel(participant.getState().getStateString(),
+ JLabel.CENTER);
+
+ timer.setRepeats(true);
+
+ if(participant.getDisplayName() != null)
+ nameLabel.setText(participant.getDisplayName());
+ else
+ nameLabel.setText(participant.getAddress());
+
+ namePanel.add(nameLabel);
+ namePanel.add(stateLabel);
+ namePanel.add(timeLabel);
+
+ contactPanel.add(photoLabel, BorderLayout.CENTER);
+ contactPanel.add(namePanel, BorderLayout.SOUTH);
+
+ this.add(contactPanel, BorderLayout.NORTH);
+ this.setPreferredSize(new Dimension(100, 200));
+ }
+
+ /**
+ * Sets the state of the contained call participant.
+ * @param state the state of the contained call participant
+ */
+ public void setState(String state)
+ {
+ this.stateLabel.setText(state);
+ }
+
+ /**
+ * Starts the timer that counts call duration.
+ */
+ public void startCallTimer()
+ {
+ timer.start();
+ }
+
+ /**
+ * Stops the timer that counts call duration.
+ */
+ public void stopCallTimer()
+ {
+ timer.stop();
+ }
+
+ /**
+ * Each second refreshes the time label to show to the user the exact
+ * duration of the call.
+ */
+ private class CallTimerListener implements ActionListener
+ {
+ private int timer = 0;
+
+ public void actionPerformed(ActionEvent e)
+ {
+ timer ++;
+ int s = timer%60;
+ int m = (timer - s)/60;
+ int h = m/60;
+
+ timeLabel.setText(processTime(h)
+ + ":" + processTime(m)
+ + ":" + processTime(s));
+ }
+ }
+
+ /**
+ * Adds a 0 in the beginning of one digit numbers.
+ *
+ * @param time The time parameter could be hours, minutes or seconds.
+ * @return The formatted minutes string.
+ */
+ private String processTime(int time)
+ {
+ String timeString = new Integer(time).toString();
+
+ String resultString = "";
+ if (timeString.length() < 2)
+ resultString = resultString.concat("0").concat(timeString);
+ else
+ resultString = timeString;
+
+ return resultString;
+ }
+
+
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallReceivePanel.java b/src/net/java/sip/communicator/impl/gui/main/call/CallReceivePanel.java
deleted file mode 100644
index 8b8d5d002..000000000
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallReceivePanel.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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 javax.swing.*;
-
-import net.java.sip.communicator.impl.gui.customcontrols.*;
-import net.java.sip.communicator.impl.gui.main.*;
-import net.java.sip.communicator.impl.gui.utils.*;
-
-/**
- *
- * @author Yana Stamcheva
- */
-public class CallReceivePanel extends JDialog {
-
- private Image callButtonBG = ImageLoader
- .getImage(ImageLoader.CALL_BUTTON_BG);
-
- private Image callButtonRolloverBG = ImageLoader
- .getImage(ImageLoader.CALL_ROLLOVER_BUTTON_BG);
-
- private Image hangupButtonBG = ImageLoader
- .getImage(ImageLoader.HANGUP_BUTTON_BG);
-
- private Image hangupButtonRolloverBG = ImageLoader
- .getImage(ImageLoader.HANGUP_ROLLOVER_BUTTON_BG);
-
- private JLabel personPhoto = new JLabel(new ImageIcon(ImageLoader
- .getImage(ImageLoader.DEFAULT_USER_PHOTO)));
-
- private JLabel personName = new JLabel("John Smith");
-
- private JLabel birthDate = new JLabel("11/11/1900");
-
- private JLabel emptyLabel = new JLabel(" ");
-
- private JLabel personInfo = new JLabel("additional info");
-
- private SIPCommButton callButton;
-
- private SIPCommButton hangupButton;
-
- private JPanel userInfoPanel = new JPanel();
-
- private JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER,
- 15, 5));
-
- public CallReceivePanel(MainFrame parent) {
-
- super(parent);
-
- this.setSize(300, 200);
-
- this.getContentPane().setLayout(new BorderLayout());
-
- callButton = new SIPCommButton(callButtonBG, callButtonRolloverBG);
-
- hangupButton = new SIPCommButton(
- hangupButtonBG, hangupButtonRolloverBG);
-
- this.init();
- }
-
- public void init() {
- this.personName.setFont(new Font("Sans Serif", Font.BOLD, 12));
-
- // this.buttonsPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0,
- // 0, Color.GRAY));
- this.buttonsPanel.add(callButton);
- this.buttonsPanel.add(hangupButton);
-
- this.userInfoPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 0,
- 0));
- this.userInfoPanel.setLayout(new BoxLayout(userInfoPanel,
- BoxLayout.Y_AXIS));
-
- this.userInfoPanel.add(personName);
- this.userInfoPanel.add(birthDate);
- this.userInfoPanel.add(emptyLabel);
- this.userInfoPanel.add(personInfo);
-
- this.getContentPane().add(personPhoto, BorderLayout.WEST);
- this.getContentPane().add(userInfoPanel, BorderLayout.CENTER);
- this.getContentPane().add(buttonsPanel, BorderLayout.SOUTH);
- }
-}