diff options
Diffstat (limited to 'src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java')
-rw-r--r-- | src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java | 2786 |
1 files changed, 1393 insertions, 1393 deletions
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java index 0dd6ef5..07c653d 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java @@ -1,4 +1,4 @@ -/*
+/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,1395 +15,1395 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.gui.main.call.conference;
-
-import java.awt.*;
-import java.util.*;
-import java.util.List;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.impl.gui.main.call.*;
-import net.java.sip.communicator.impl.gui.utils.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.plugin.desktoputil.TransparentPanel;
-
-import org.jitsi.util.swing.*;
-
-/**
- * Extends <tt>BasicConferenceCallPanel</tt> to implement a user interface
- * <tt>Component</tt> which depicts a <tt>CallConference</tt> with audio and
- * video and is contained in a <tt>CallPanel</tt>.
- *
- * @author Yana Stamcheva
- * @author Lyubomir Marinov
- */
-public class VideoConferenceCallPanel
- extends BasicConferenceCallPanel
-{
- /**
- * The <tt>Logger</tt> used by the <tt>VideoConferenceCallPanel</tt> class
- * and its instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(VideoConferenceCallPanel.class);
-
- /**
- * The compile-time flag which indicates whether each video displayed by
- * <tt>VideoConferenceCallPanel</tt> is to be depicted with an associated
- * tool bar showing information and controls related to the (local or
- * remote) peer sending the respective video.
- */
- private static final boolean SHOW_TOOLBARS = true;
-
- /**
- * The facility which aids this instance with the video-related information.
- */
- private final UIVideoHandler2 uiVideoHandler;
-
- /**
- * The <tt>Observer</tt> which listens to {@link #uiVideoHandler} about
- * changes in the video-related information.
- */
- private final Observer uiVideoHandlerObserver
- = new Observer()
- {
- public void update(Observable o, Object arg)
- {
- updateViewFromModel();
- }
- };
-
- /**
- * The <tt>VideoContainer</tt> which occupies this whole <tt>Component</tt>
- * and arranges the visual <tt>Component</tt>s displaying the video
- * streaming between the local peer/user and the remote peer(s).
- */
- private final VideoContainer videoContainer;
-
- /**
- * The set of visual <tt>Component</tt>s displaying video streaming between
- * the local peer/user and the remote peers which are depicted by this
- * instance.
- */
- private final Set<Component> videos = new HashSet<Component>();
-
- /**
- * The thumbnail container.
- */
- private final ThumbnailConferenceCallPanel thumbnailContainer;
-
- private final JPanel thumbnailPanel;
-
- /**
- * Initializes a new <tt>VideoConferenceCallPanel</tt> instance which is to
- * be used by a specific <tt>CallPanel</tt> to depict a specific
- * <tt>CallConference</tt>. The new instance will depict both the
- * audio-related and the video-related information.
- *
- * @param callPanel the <tt>CallPanel</tt> which will use the new instance
- * to depict the specified <tt>CallConference</tt>.
- * @param callConference the <tt>CallConference</tt> to be depicted by the
- * new instance
- * @param uiVideoHandler the utility which is to aid the new instance in
- * dealing with the video-related information
- */
- public VideoConferenceCallPanel(
- CallPanel callPanel,
- CallConference callConference,
- UIVideoHandler2 uiVideoHandler)
- {
- super(callPanel, callConference);
-
- this.uiVideoHandler = uiVideoHandler;
-
- thumbnailPanel = new JPanel(new BorderLayout());
- thumbnailContainer
- = new ThumbnailConferenceCallPanel( callPanel,
- callConference,
- uiVideoHandler);
-
- videoContainer = createVideoContainer();
-
- /*
- * Our user interface hierarchy has been initialized so we are ready to
- * begin receiving events warranting updates of this view from its
- * model.
- */
- uiVideoHandler.addObserver(uiVideoHandlerObserver);
-
- /*
- * Notify the super that this instance has completed its initialization
- * and the view that it implements is ready to be updated from the
- * model.
- */
- initializeComplete();
- }
-
- private void addConferenceMemberContainers(
- ConferenceParticipantContainer cpc)
- {
- List<ConferenceParticipantContainer> cmcs
- = cpc.conferenceMemberContainers;
-
- if ((cmcs != null) && !cmcs.isEmpty())
- {
- for (ConferenceParticipantContainer cmc : cmcs)
- {
- if (!cmc.toBeRemoved)
- {
- videoContainer.add(
- cmc.getComponent(),
- VideoLayout.CENTER_REMOTE);
- }
- }
- }
- }
-
- private Component createDefaultPhotoPanel(Call call)
- {
- OperationSetServerStoredAccountInfo accountInfo
- = call.getProtocolProvider().getOperationSet(
- OperationSetServerStoredAccountInfo.class);
- ImageIcon photoLabelIcon = null;
-
- if (accountInfo != null)
- {
- byte[] accountImage = AccountInfoUtils.getImage(accountInfo);
-
- // do not set empty images
- if ((accountImage != null) && (accountImage.length > 0))
- photoLabelIcon = new ImageIcon(accountImage);
- }
- if (photoLabelIcon == null)
- {
- photoLabelIcon
- = new ImageIcon(
- ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO));
- }
-
- return createDefaultPhotoPanel(photoLabelIcon);
- }
-
- private Component createDefaultPhotoPanel(CallPeer callPeer)
- {
- byte[] peerImage = CallManager.getPeerImage(callPeer);
- ImageIcon photoLabelIcon
- = (peerImage == null)
- ? new ImageIcon(
- ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO))
- : new ImageIcon(peerImage);
-
- return createDefaultPhotoPanel(photoLabelIcon);
- }
-
- private Component createDefaultPhotoPanel(ConferenceMember conferenceMember)
- {
- return
- createDefaultPhotoPanel(
- new ImageIcon(
- ImageLoader.getImage(
- ImageLoader.DEFAULT_USER_PHOTO)));
- }
-
- /**
- * Creates a new <tt>Component</tt> which is to display a specific
- * <tt>ImageIcon</tt> representing the photo of a participant in a call.
- *
- * @param photoLabelIcon the <tt>ImageIcon</tt> which represents the photo
- * of a participant in a call and which is to be displayed by the new
- * <tt>Component</tt>
- * @return a new <tt>Component</tt> which displays the specified
- * <tt>photoLabelIcon</tt>
- */
- private Component createDefaultPhotoPanel(ImageIcon photoLabelIcon)
- {
- JLabel photoLabel = new JLabel();
-
- photoLabel.setIcon(photoLabelIcon);
-
- @SuppressWarnings("serial")
- JPanel photoPanel
- = new TransparentPanel(new GridBagLayout())
- {
- /**
- * @{inheritDoc}
- */
- @Override
- public void paintComponent(Graphics g)
- {
- super.paintComponent(g);
-
- g = g.create();
- try
- {
- AntialiasingManager.activateAntialiasing(g);
-
- g.setColor(Color.GRAY);
- g.fillRoundRect(
- 0, 0, this.getWidth(), this.getHeight(),
- 6, 6);
- }
- finally
- {
- g.dispose();
- }
- }
- };
-
- photoPanel.setPreferredSize(new Dimension(320, 240));
-
- GridBagConstraints photoPanelConstraints = new GridBagConstraints();
-
- photoPanelConstraints.anchor = GridBagConstraints.CENTER;
- photoPanelConstraints.fill = GridBagConstraints.NONE;
- photoPanel.add(photoLabel, photoPanelConstraints);
-
- return photoPanel;
- }
-
- /**
- * Initializes a new <tt>VideoContainer</tt> instance which is to contain
- * the visual/video <tt>Component</tt>s of the telephony conference depicted
- * by this instance.
- */
- private VideoContainer createVideoContainer()
- {
- VideoContainer videoContainer = new VideoContainer(null, true);
-
- thumbnailPanel.setBackground(Color.DARK_GRAY);
- thumbnailPanel.add(thumbnailContainer, BorderLayout.NORTH);
-
- add(thumbnailPanel, BorderLayout.EAST);
- add(videoContainer, BorderLayout.CENTER);
-
- return videoContainer;
- }
-
- /**
- * Shows/hides the participants thumbnails list.
- *
- * @param show <tt>true</tt> to show the participants list, <tt>false</tt>
- * to hide it
- */
- public void showThumbnailsList(boolean show)
- {
- thumbnailPanel.setVisible(show);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void dispose()
- {
- try
- {
- uiVideoHandler.deleteObserver(uiVideoHandlerObserver);
- }
- finally
- {
- super.dispose();
- }
- }
-
- /**
- * Determines whether a specific <tt>ConferenceMember</tt> represents the
- * same conference participant as a specific <tt>CallPeer</tt>. If the
- * specified <tt>conferenceMember</tt> is <tt>null</tt>, returns
- * <tt>true</tt>. Otherwise, determines whether the addresses of the
- * specified <tt>conferenceMember</tt> and the specified <tt>callPeer</tt>
- * identify one and the same entity.
- *
- * @param conferenceMember the <tt>ConferenceMember</tt> to be checked
- * whether is represents the same conference participant as the specified
- * <tt>callPeer</tt>. If it is <tt>null</tt>, <tt>true</tt> is returned.
- * @param callPeer the <tt>CallPeer</tt> to be checked whether it represents
- * the same conference participant as the specified
- * <tt>conferenceMember</tt>
- * @return <tt>true</tt> if the specified <tt>conferenceMember</tt> and the
- * specified <tt>callPeer</tt> represent the same conference participant or
- * the specified <tt>conferenceMember</tt> is <tt>null</tt>; otherwise,
- * <tt>false</tt>
- */
- private boolean isConferenceMemberCallPeer(
- ConferenceMember conferenceMember,
- CallPeer callPeer)
- {
- return
- (conferenceMember == null)
- ? true
- : CallManager.addressesAreEqual(
- conferenceMember.getAddress(),
- callPeer.getAddress());
- }
-
- /**
- * Determines whether a specific <tt>ConferenceMember</tt> represents the
- * local peer/user. Since this instance depicts a whole telephony
- * conference, the local peer/user may be participating with multiple
- * <tt>Call</tt>s in it. The <tt>Call</tt>s may be through different
- * (local) accounts. That's why the implementation determines whether the
- * address of the specified <tt>conferenceMember</tt> identifies the address
- * of a (local) accounts involved in the telephony conference depicted by
- * this instance.
- *
- * @param conferenceMember the <tt>ConferenceMember</tt> to be checked
- * whether it represents the local peer/user
- * @return <tt>true</tt> if the specified <tt>conferenceMember</tt>
- * represents the local peer/user; otherwise, <tt>false</tt>
- */
- private boolean isConferenceMemberLocalUser(
- ConferenceMember conferenceMember)
- {
- String address = conferenceMember.getAddress();
-
- for (Call call : callConference.getCalls())
- {
- if (CallManager.addressesAreEqual(
- address,
- call.getProtocolProvider().getAccountID()
- .getAccountAddress()))
- {
- return true;
- }
- }
- return false;
- }
-
- private void removeConferenceMemberContainers(
- ConferenceParticipantContainer cpc,
- boolean all)
- {
- List<ConferenceParticipantContainer> cmcs
- = cpc.conferenceMemberContainers;
-
- if ((cmcs != null) && !cmcs.isEmpty())
- {
- Iterator<ConferenceParticipantContainer> i = cmcs.iterator();
-
- while (i.hasNext())
- {
- ConferenceParticipantContainer cmc = i.next();
-
- if (all || cmc.toBeRemoved)
- {
- i.remove();
-
- videoContainer.remove(cmc.getComponent());
- cmc.dispose();
- }
- }
- }
- }
-
- /**
- * Updates the <tt>ConferenceParticipantContainer</tt>s which depict the
- * <tt>ConferenceMember</tt>s of the <tt>CallPeer</tt> depicted by a
- * specific <tt>ConferenceParticipantContainer</tt>.
- *
- * @param cpc the <tt>ConferenceParticipantContainer</tt> which depicts the
- * <tt>CallPeer</tt> whose <tt>ConferenceMember</tt>s are to be depicted
- * @param videos the visual <tt>Component</tt>s displaying video streaming
- * from the remote peer (represented by <tt>cpc</tt>) to the local peer/user
- * @param videoTelephony the <tt>OperationSetVideoTelephony</tt> which
- * retrieved the specified <tt>videos</tt> from the <tt>CallPeer</tt>
- * depicted by <tt>cpc</tt>. While the <tt>CallPeer</tt> could be queried
- * for it, such a query would waste more resources at run time given that
- * the invoker has it already.
- */
- private void updateConferenceMemberContainers(
- ConferenceParticipantContainer cpc,
- List<Component> videos,
- OperationSetVideoTelephony videoTelephony)
- {
- CallPeer callPeer = (CallPeer) cpc.getParticipant();
- List<ConferenceParticipantContainer> cmcs
- = cpc.conferenceMemberContainers;
-
- /*
- * Invalidate all conferenceMemberContainers. Then validate which of
- * them are to remain and which of them are to really be removed
- * later on.
- */
- if (cmcs != null)
- {
- for (ConferenceParticipantContainer cmc : cmcs)
- cmc.toBeRemoved = true;
- }
-
- /*
- * Depict the remote videos. They may or may not be associated with
- * ConferenceMembers so the ConferenceMembers which have no
- * associated videos will be depicted afterwards.
- */
- if (videos != null)
- {
- Component video = cpc.getVideo();
-
- for (Component conferenceMemberVideo : videos)
- {
- /*
- * One of the remote videos is already used to depict the
- * callPeer.
- */
- if (conferenceMemberVideo == video)
- continue;
-
- boolean addNewConferenceParticipantContainer = true;
- ConferenceMember conferenceMember
- = videoTelephony.getConferenceMember(
- callPeer,
- conferenceMemberVideo);
-
- if (cmcs == null)
- {
- cmcs = new LinkedList<ConferenceParticipantContainer>();
- cpc.conferenceMemberContainers = cmcs;
- }
- else
- {
- for (ConferenceParticipantContainer cmc : cmcs)
- {
- Object cmcParticipant = cmc.getParticipant();
-
- if (conferenceMember == null)
- {
- if (cmcParticipant == callPeer)
- {
- Component cmcVideo = cmc.getVideo();
-
- if (cmcVideo == null)
- {
- cmc.setVideo(conferenceMemberVideo);
- cmc.toBeRemoved = false;
- addNewConferenceParticipantContainer
- = false;
- break;
- }
- else if (cmcVideo == conferenceMemberVideo)
- {
- cmc.toBeRemoved = false;
- addNewConferenceParticipantContainer
- = false;
- break;
- }
- }
- }
- else if (cmcParticipant == conferenceMember)
- {
- cmc.setVideo(conferenceMemberVideo);
- cmc.toBeRemoved = false;
- addNewConferenceParticipantContainer = false;
- break;
- }
- }
- }
-
- if (addNewConferenceParticipantContainer)
- {
- ConferenceParticipantContainer cmc
- = (conferenceMember == null)
- ? new ConferenceParticipantContainer(
- callPeer,
- conferenceMemberVideo)
- : new ConferenceParticipantContainer(
- conferenceMember,
- conferenceMemberVideo);
-
- cmcs.add(cmc);
- }
- }
- }
-
- /*
- * Depict the ConferenceMembers which have not been depicted yet.
- * They have no associated videos.
- */
- List<ConferenceMember> conferenceMembers
- = callPeer.getConferenceMembers();
-
- if (!conferenceMembers.isEmpty())
- {
- if (cmcs == null)
- {
- cmcs = new LinkedList<ConferenceParticipantContainer>();
- cpc.conferenceMemberContainers = cmcs;
- }
- for (ConferenceMember conferenceMember : conferenceMembers)
- {
- /*
- * If the callPeer reports itself as a ConferenceMember, then
- * we've already depicted it with cpc.
- */
- if (isConferenceMemberCallPeer(conferenceMember, callPeer))
- continue;
- /*
- * If the callPeer reports the local peer/user as a
- * ConferenceMember, then we've already depicted it.
- */
- if (isConferenceMemberLocalUser(conferenceMember))
- continue;
-
- boolean addNewConferenceParticipantContainer = true;
-
- for (ConferenceParticipantContainer cmc : cmcs)
- {
- if (cmc.getParticipant() == conferenceMember)
- {
- /*
- * It is possible to have a ConferenceMember who is
- * sending video but we just do not have the SSRC of
- * that video to associate the video with the
- * ConferenceMember. In such a case, we may be depicting
- * the ConferenceMember twice: once with video without a
- * ConferenceMember and once with a ConferenceMember
- * without video. This will surely be the case at the
- * time of this writing with non-focus participants in a
- * telephony conference hosted on a Jitsi Videobridge.
- * Such a display is undesirable. If the
- * conferenceMember is known to send video, we will not
- * display it until we associated it with a video. This
- * way, if a ConferenceMember is not sending video, we
- * will depict it and we can be sure that no video
- * without a ConferenceMember association will be
- * depicting it a second time.
- */
- if (cmc.toBeRemoved
- && !conferenceMember
- .getVideoStatus()
- .allowsSending())
- {
- cmc.setVideo(null);
- cmc.toBeRemoved = false;
- }
- addNewConferenceParticipantContainer = false;
- break;
- }
- }
-
- if (addNewConferenceParticipantContainer)
- {
- ConferenceParticipantContainer cmc
- = new ConferenceParticipantContainer(
- conferenceMember,
- null);
-
- cmcs.add(cmc);
- }
- }
- }
-
- if ((cmcs != null) && !cmcs.isEmpty())
- {
- removeConferenceMemberContainers(cpc, false);
- /*
- * If cpc is already added to the user interface hierarchy of this
- * instance, then it was there before the update procedure and it
- * was determined to be appropriate to continue to depict its model.
- * Consequently, its Component will be neither added to (because it
- * was already added) nor removed from the user interface hierarchy
- * of this instance. That's why we have make sure that the
- * Components of its conferenceMemberContainers are also added to
- * the user interface.
- */
- if (UIVideoHandler2.isAncestor(this, cpc.getComponent()))
- addConferenceMemberContainers(cpc);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected ConferenceCallPeerRenderer updateViewFromModel(
- ConferenceCallPeerRenderer callPeerPanel,
- CallPeer callPeer)
- {
- if (callPeer == null)
- {
- /*
- * The local peer/user will be represented by a Call which has a
- * CallPeer who provides local video. However, if the user has
- * selected to hide the local video, the local peer/user will not be
- * represented at all.
- */
- Component video = null;
-
- if (uiVideoHandler.isLocalVideoVisible())
- {
- for (Call aCall : callConference.getCalls())
- {
- Iterator<? extends CallPeer> callPeerIter
- = aCall.getCallPeers();
- OperationSetVideoTelephony videoTelephony
- = aCall.getProtocolProvider().getOperationSet(
- OperationSetVideoTelephony.class);
-
- while (callPeerIter.hasNext())
- {
- callPeer = callPeerIter.next();
-
- if (videoTelephony != null)
- {
- try
- {
- video
- = videoTelephony.getLocalVisualComponent(
- callPeer);
- }
- catch (OperationFailedException ofe)
- {
- logger.error(
- "Failed to retrieve the local video"
- + " for display",
- ofe);
- }
- if (video != null)
- break;
- }
- }
- if (video != null)
- break;
- }
- }
-
- if (callPeer == null)
- callPeerPanel = null;
- else
- {
- Call call = callPeer.getCall();
-
- if (callPeerPanel instanceof ConferenceParticipantContainer)
- {
- ConferenceParticipantContainer cpc
- = (ConferenceParticipantContainer) callPeerPanel;
-
- if (cpc.getParticipant() == call)
- cpc.setVideo(video);
- else
- callPeerPanel = null;
- }
- else
- callPeerPanel = null;
- if (callPeerPanel == null)
- {
- callPeerPanel
- = new ConferenceParticipantContainer(call, video);
- }
- }
- }
- else
- {
- /*
- * The specified callPeer will be represented by one of its remote
- * videos which is not associated with a ConferenceMember or is
- * associated with a ConferenceMember representing the callPeer
- * itself.
- */
- OperationSetVideoTelephony videoTelephony
- = callPeer.getProtocolProvider().getOperationSet(
- OperationSetVideoTelephony.class);
- List<Component> videos = null;
- Component video = null;
-
- if (videoTelephony != null)
- {
- videos = videoTelephony.getVisualComponents(callPeer);
- if ((videos != null) && !videos.isEmpty())
- {
- for (Component aVideo : videos)
- {
- ConferenceMember conferenceMember
- = videoTelephony.getConferenceMember(
- callPeer,
- aVideo);
-
- if (isConferenceMemberCallPeer(
- conferenceMember,
- callPeer))
- {
- video = aVideo;
- break;
- }
- }
- }
- }
-
- ConferenceParticipantContainer cpc = null;
-
- if (callPeerPanel instanceof ConferenceParticipantContainer)
- {
- cpc = (ConferenceParticipantContainer) callPeerPanel;
- if (cpc.getParticipant() == callPeer)
- cpc.setVideo(video);
- else
- cpc = null;
- }
- if (cpc == null)
- cpc = new ConferenceParticipantContainer(callPeer, video);
- callPeerPanel = cpc;
-
- // Update the conferenceMemberContainers of the cpc.
- updateConferenceMemberContainers(cpc, videos, videoTelephony);
- }
- return callPeerPanel;
- }
-
- /**
- * {@inheritDoc}
- *
- * If {@link #SHOW_TOOLBARS} is <tt>false</tt>, disables the use of
- * <tt>ConferenceParticipantContainer</tt>. A reason for such a value of
- * <tt>SHOW_TOOLBARS</tt> may be that the functionality implemented in the
- * model may not fully support mapping of visual <tt>Component</tt>s
- * displaying video to telephony conference participants (e.g. in telephony
- * conferences utilizing the Jitsi Videobridge server-side technology). In
- * such a case displays the videos only, does not map videos to participants
- * and does not display participants who do not have videos.
- */
- @Override
- protected void updateViewFromModelInEventDispatchThread()
- {
- if (SHOW_TOOLBARS)
- {
- super.updateViewFromModelInEventDispatchThread();
- return;
- }
-
- /*
- * Determine the set of visual Components displaying video streaming
- * between the local peer/user and the remote peers which are to be
- * depicted by this instance.
- */
- Component localVideo = null;
- Set<Component> videos = new HashSet<Component>();
-
- for (Call call : callConference.getCalls())
- {
- OperationSetVideoTelephony videoTelephony
- = call.getProtocolProvider().getOperationSet(
- OperationSetVideoTelephony.class);
-
- if (videoTelephony == null)
- continue;
-
- Iterator<? extends CallPeer> callPeerIter = call.getCallPeers();
-
- while (callPeerIter.hasNext())
- {
- CallPeer callPeer = callPeerIter.next();
-
- /*
- * TODO VideoConferenceCallPanel respects
- * UIVideoHandler2.isLocalVideoVisible() in order to react to
- * the associated button at the bottom of the CallPanel.
- * However, it does not add a close button on top of the local
- * video in contrast to OneToOneCallPeerPanel. Overall, the
- * result is questionable.
- */
- if (uiVideoHandler.isLocalVideoVisible()
- && (localVideo == null))
- {
- try
- {
- localVideo
- = videoTelephony.getLocalVisualComponent(callPeer);
- }
- catch (OperationFailedException ofe)
- {
- /*
- * We'll just try to get the local video through another
- * CallPeer then.
- */
- }
- if (localVideo != null)
- videos.add(localVideo);
- }
-
- List<Component> callPeerRemoteVideos
- = videoTelephony.getVisualComponents(callPeer);
-
- videos.addAll(callPeerRemoteVideos);
- }
- }
-
- /*
- * Remove the Components of this view which are no longer present in the
- * model.
- */
- Iterator<Component> thisVideoIter = this.videos.iterator();
-
- while (thisVideoIter.hasNext())
- {
- Component thisVideo = thisVideoIter.next();
-
- if (!videos.contains(thisVideo))
- {
- thisVideoIter.remove();
- videoContainer.remove(thisVideo);
- }
-
- /*
- * If a video is known to be depicted by this view and is still
- * present in the model, then we could remove it from the set of
- * videos present in the model in order to prevent going through the
- * procedure of adding it to this view. However, we choose to play
- * on the safe side.
- */
- }
-
- /*
- * Add the Components of the model which are not depicted by this view.
- */
- for (Component video : videos)
- {
- if (!UIVideoHandler2.isAncestor(videoContainer, video))
- {
- this.videos.add(video);
- videoContainer.add(
- video,
- (video == localVideo)
- ? VideoLayout.LOCAL
- : VideoLayout.CENTER_REMOTE);
- }
- }
- }
-
- @Override
- protected void viewForModelAdded(
- ConferenceCallPeerRenderer callPeerPanel,
- CallPeer callPeer)
- {
- videoContainer.add(
- callPeerPanel.getComponent(),
- VideoLayout.CENTER_REMOTE);
- if ((callPeer != null)
- && (callPeerPanel instanceof ConferenceParticipantContainer))
- {
- addConferenceMemberContainers(
- (ConferenceParticipantContainer) callPeerPanel);
- }
- }
-
- @Override
- protected void viewForModelRemoved(
- ConferenceCallPeerRenderer callPeerPanel,
- CallPeer callPeer)
- {
- videoContainer.remove(callPeerPanel.getComponent());
- if ((callPeer != null)
- && (callPeerPanel instanceof ConferenceParticipantContainer))
- {
- removeConferenceMemberContainers(
- (ConferenceParticipantContainer) callPeerPanel,
- true);
- }
- }
-
- /**
- * Implements an AWT <tt>Component</tt> which contains the user interface
- * elements depicting a specific participant in the telephony conference
- * depicted by a <tt>VideoConferenceCallPanel</tt>.
- */
- private class ConferenceParticipantContainer
- extends TransparentPanel
- implements ConferenceCallPeerRenderer
- {
- /**
- * The list of <tt>ConferenceParticipantContainer</tt>s which represent
- * the <tt>ConferenceMember</tt>s of the participant represented by this
- * instance. Since a <tt>CallPeer</tt> may send the local peer/user
- * multiple videos without providing a way to associate a
- * ConferenceMember with each one of them, the list may contain
- * <tt>ConferenceParticipantContainer</tt>s which do not represent a
- * specific <tt>ConferenceMember</tt> instance but rather a video sent
- * by a <tt>CallPeer</tt> to the local peer/user which looks like (in
- * the terms of <tt>VideoConferenceCallPanel) a member of a conference
- * organized by the <tt>CallPeer</tt> in question.
- * <p>
- * Implements a state which is private to
- * <tt>VideoConferenceCallPanel</tt> and is of no concern to
- * <tt>ConferenceParticipantContainer</tt>.
- * </p>
- */
- List<ConferenceParticipantContainer> conferenceMemberContainers;
-
- /**
- * The indicator which determines whether this instance is to be removed
- * because it has become out-of-date, obsolete, unnecessary.
- * <p>
- * Implements a state which is private to
- * <tt>VideoConferenceCallPanel</tt> and is of no concern to
- * <tt>ConferenceParticipantContainer</tt>.
- * </p>
- */
- boolean toBeRemoved;
-
- /**
- * The <tt>BasicConferenceParticipantPanel</tt> which is displayed at
- * the bottom of this instance, bellow the {@link #video} (i.e.
- * {@link #videoContainer}) and is referred to as the tool bar.
- */
- private final BasicConferenceParticipantPanel<?> toolBar;
-
- /**
- * The visual <tt>Component</tt>, if any, displaying video which is
- * depicted by this instance.
- */
- private Component video;
-
- /**
- * The <tt>VideoContainer</tt> which lays out the video depicted by this
- * instance i.e. {@link #video}.
- */
- private final VideoContainer videoContainer;
-
- /**
- * The <tt>CallPeer</tt> associated with this container, if it has been
- * created to represent a <tt>CallPeer</tt>.
- */
- private CallPeer callPeer;
-
- /**
- * The <tt>conferenceMember</tt> associated with this container, if it
- * has been created to represent a <tt>conferenceMember</tt>.
- */
- private ConferenceMember conferenceMember;
-
- /**
- * Indicates that this container contains information for the local
- * user.
- */
- private boolean isLocalUser;
-
- /**
- * Initializes a new <tt>ConferenceParticipantContainer</tt> instance
- * which is to depict the local peer/user.
- *
- * @param call a <tt>Call</tt> which is to provide information about the
- * local peer/user
- * @param video the visual <tt>Component</tt>, if any, displaying the
- * video streaming from the local peer/user to the remote peer(s)
- */
- public ConferenceParticipantContainer(Call call, Component video)
- {
- this(
- createDefaultPhotoPanel(call),
- video,
- new ConferencePeerPanel(
- VideoConferenceCallPanel.this,
- call,
- true),
- null, null, true);
- }
-
- public ConferenceParticipantContainer(
- CallPeer callPeer,
- Component video)
- {
- this( createDefaultPhotoPanel(callPeer),
- video,
- new ConferencePeerPanel(
- VideoConferenceCallPanel.this,
- callPeer,
- true),
- callPeer, null, false);
- }
-
- private ConferenceParticipantContainer(
- Component noVideo,
- Component video,
- BasicConferenceParticipantPanel<?> toolBar,
- CallPeer callPeer,
- ConferenceMember conferenceMember,
- boolean isLocalUser)
- {
- super(new BorderLayout());
-
- this.callPeer = callPeer;
- this.conferenceMember = conferenceMember;
- this.isLocalUser = isLocalUser;
-
- videoContainer = new VideoContainer(noVideo, false);
- add(videoContainer, BorderLayout.CENTER);
-
- this.toolBar = toolBar;
- if (this.toolBar != null)
- add(this.toolBar, BorderLayout.SOUTH);
-
- if (video != null)
- {
- setVideo(video);
- }
- else
- setVisible(false);
- }
-
- public ConferenceParticipantContainer(
- ConferenceMember conferenceMember,
- Component video)
- {
- this(
- createDefaultPhotoPanel(conferenceMember),
- video,
- new ConferenceMemberPanel(
- VideoConferenceCallPanel.this,
- conferenceMember,
- true),
- null, conferenceMember, false);
- }
-
- public void dispose()
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.dispose();
-
- // Dispose of the conferenceMemberContainers if any.
- /*
- * XXX The field conferenceMemberContainers implements a state
- * private to VideoConferenceCallPanel which the latter makes sure
- * to access on the AWT event dispatching thread only. Since we are
- * going out of our way here to help VideoConferenceCallPanel,
- * ensure that the mentioned synchronization rule is not violated.
- */
- CallManager.assertIsEventDispatchingThread();
- if (conferenceMemberContainers != null)
- {
- for (ConferenceParticipantContainer cmc
- : conferenceMemberContainers)
- {
- cmc.dispose();
- }
- }
- }
-
- public CallPanel getCallPanel()
- {
- return getCallRenderer().getCallContainer();
- }
-
- public SwingCallRenderer getCallRenderer()
- {
- return VideoConferenceCallPanel.this;
- }
-
- public Component getComponent()
- {
- return this;
- }
-
- private ConferenceCallPeerRenderer
- getConferenceCallPeerRendererDelegate()
- {
- return
- (toolBar instanceof ConferenceCallPeerRenderer)
- ? (ConferenceCallPeerRenderer) toolBar
- : null;
- }
-
- /**
- * Gets the conference participant depicted by this instance.
- *
- * @return the conference participant depicted by this instance
- */
- public Object getParticipant()
- {
- return (toolBar == null) ? null : toolBar.getParticipant();
- }
-
- public Component getVideo()
- {
- return video;
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only. Otherwise, returns <tt>false</tt>.
- */
- public boolean isLocalVideoVisible()
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- return (delegate == null) ? false : delegate.isLocalVideoVisible();
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void printDTMFTone(char dtmfChar)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.printDTMFTone(dtmfChar);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void securityNegotiationStarted(
- CallPeerSecurityNegotiationStartedEvent ev)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.securityNegotiationStarted(ev);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void securityOff(CallPeerSecurityOffEvent ev)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.securityOff(ev);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void securityOn(CallPeerSecurityOnEvent ev)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.securityOn(ev);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void securityPending()
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.securityPending();
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void securityTimeout(CallPeerSecurityTimeoutEvent ev)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.securityTimeout(ev);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setErrorReason(String reason)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setErrorReason(reason);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setLocalVideoVisible(boolean visible)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setLocalVideoVisible(visible);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setMute(boolean mute)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setMute(mute);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setOnHold(boolean onHold)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setOnHold(onHold);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setPeerImage(byte[] image)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setPeerImage(image);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setPeerName(String name)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setPeerName(name);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setPeerState(
- CallPeerState oldState,
- CallPeerState newState,
- String stateString)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setPeerState(oldState, newState, stateString);
- }
-
- /**
- * {@inheritDoc}
- *
- * Delegates to the <tt>toolBar</tt>, if the latter implements
- * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a
- * container only.
- */
- public void setSecurityPanelVisible(boolean visible)
- {
- ConferenceCallPeerRenderer delegate
- = getConferenceCallPeerRendererDelegate();
-
- if (delegate != null)
- delegate.setSecurityPanelVisible(visible);
- }
-
- /**
- * Sets the visual <tt>Component</tt> displaying the video associated
- * with the participant depicted by this instance.
- *
- * @param video the visual <tt>Component</tt> displaying video which is
- * to be associated with the participant depicted by this instance
- */
- void setVideo(Component video)
- {
- if (this.video != video)
- {
- if (this.video != null)
- videoContainer.remove(this.video);
-
- this.video = video;
-
- if (this.video != null)
- {
- setVisible(true);
- videoContainer.add(this.video, VideoLayout.CENTER_REMOTE);
- }
- else
- setVisible(false);
-
- // Update thumbnails container according to video status.
- if (thumbnailContainer != null)
- {
- if (conferenceMember != null)
- thumbnailContainer
- .updateThumbnail(conferenceMember, (video != null));
- else if (callPeer != null)
- thumbnailContainer
- .updateThumbnail(callPeer, (video != null));
- else if (isLocalUser)
- thumbnailContainer
- .updateThumbnail((video != null));
- }
- }
- }
- }
-}
+package net.java.sip.communicator.impl.gui.main.call.conference; + +import java.awt.*; +import java.util.*; +import java.util.List; + +import javax.swing.*; + +import net.java.sip.communicator.impl.gui.main.call.*; +import net.java.sip.communicator.impl.gui.utils.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.plugin.desktoputil.*; +import net.java.sip.communicator.plugin.desktoputil.TransparentPanel; + +import org.jitsi.util.swing.*; + +/** + * Extends <tt>BasicConferenceCallPanel</tt> to implement a user interface + * <tt>Component</tt> which depicts a <tt>CallConference</tt> with audio and + * video and is contained in a <tt>CallPanel</tt>. + * + * @author Yana Stamcheva + * @author Lyubomir Marinov + */ +public class VideoConferenceCallPanel + extends BasicConferenceCallPanel +{ + /** + * The <tt>Logger</tt> used by the <tt>VideoConferenceCallPanel</tt> class + * and its instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(VideoConferenceCallPanel.class); + + /** + * The compile-time flag which indicates whether each video displayed by + * <tt>VideoConferenceCallPanel</tt> is to be depicted with an associated + * tool bar showing information and controls related to the (local or + * remote) peer sending the respective video. + */ + private static final boolean SHOW_TOOLBARS = true; + + /** + * The facility which aids this instance with the video-related information. + */ + private final UIVideoHandler2 uiVideoHandler; + + /** + * The <tt>Observer</tt> which listens to {@link #uiVideoHandler} about + * changes in the video-related information. + */ + private final Observer uiVideoHandlerObserver + = new Observer() + { + public void update(Observable o, Object arg) + { + updateViewFromModel(); + } + }; + + /** + * The <tt>VideoContainer</tt> which occupies this whole <tt>Component</tt> + * and arranges the visual <tt>Component</tt>s displaying the video + * streaming between the local peer/user and the remote peer(s). + */ + private final VideoContainer videoContainer; + + /** + * The set of visual <tt>Component</tt>s displaying video streaming between + * the local peer/user and the remote peers which are depicted by this + * instance. + */ + private final Set<Component> videos = new HashSet<Component>(); + + /** + * The thumbnail container. + */ + private final ThumbnailConferenceCallPanel thumbnailContainer; + + private final JPanel thumbnailPanel; + + /** + * Initializes a new <tt>VideoConferenceCallPanel</tt> instance which is to + * be used by a specific <tt>CallPanel</tt> to depict a specific + * <tt>CallConference</tt>. The new instance will depict both the + * audio-related and the video-related information. + * + * @param callPanel the <tt>CallPanel</tt> which will use the new instance + * to depict the specified <tt>CallConference</tt>. + * @param callConference the <tt>CallConference</tt> to be depicted by the + * new instance + * @param uiVideoHandler the utility which is to aid the new instance in + * dealing with the video-related information + */ + public VideoConferenceCallPanel( + CallPanel callPanel, + CallConference callConference, + UIVideoHandler2 uiVideoHandler) + { + super(callPanel, callConference); + + this.uiVideoHandler = uiVideoHandler; + + thumbnailPanel = new JPanel(new BorderLayout()); + thumbnailContainer + = new ThumbnailConferenceCallPanel( callPanel, + callConference, + uiVideoHandler); + + videoContainer = createVideoContainer(); + + /* + * Our user interface hierarchy has been initialized so we are ready to + * begin receiving events warranting updates of this view from its + * model. + */ + uiVideoHandler.addObserver(uiVideoHandlerObserver); + + /* + * Notify the super that this instance has completed its initialization + * and the view that it implements is ready to be updated from the + * model. + */ + initializeComplete(); + } + + private void addConferenceMemberContainers( + ConferenceParticipantContainer cpc) + { + List<ConferenceParticipantContainer> cmcs + = cpc.conferenceMemberContainers; + + if ((cmcs != null) && !cmcs.isEmpty()) + { + for (ConferenceParticipantContainer cmc : cmcs) + { + if (!cmc.toBeRemoved) + { + videoContainer.add( + cmc.getComponent(), + VideoLayout.CENTER_REMOTE); + } + } + } + } + + private Component createDefaultPhotoPanel(Call call) + { + OperationSetServerStoredAccountInfo accountInfo + = call.getProtocolProvider().getOperationSet( + OperationSetServerStoredAccountInfo.class); + ImageIcon photoLabelIcon = null; + + if (accountInfo != null) + { + byte[] accountImage = AccountInfoUtils.getImage(accountInfo); + + // do not set empty images + if ((accountImage != null) && (accountImage.length > 0)) + photoLabelIcon = new ImageIcon(accountImage); + } + if (photoLabelIcon == null) + { + photoLabelIcon + = new ImageIcon( + ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO)); + } + + return createDefaultPhotoPanel(photoLabelIcon); + } + + private Component createDefaultPhotoPanel(CallPeer callPeer) + { + byte[] peerImage = CallManager.getPeerImage(callPeer); + ImageIcon photoLabelIcon + = (peerImage == null) + ? new ImageIcon( + ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO)) + : new ImageIcon(peerImage); + + return createDefaultPhotoPanel(photoLabelIcon); + } + + private Component createDefaultPhotoPanel(ConferenceMember conferenceMember) + { + return + createDefaultPhotoPanel( + new ImageIcon( + ImageLoader.getImage( + ImageLoader.DEFAULT_USER_PHOTO))); + } + + /** + * Creates a new <tt>Component</tt> which is to display a specific + * <tt>ImageIcon</tt> representing the photo of a participant in a call. + * + * @param photoLabelIcon the <tt>ImageIcon</tt> which represents the photo + * of a participant in a call and which is to be displayed by the new + * <tt>Component</tt> + * @return a new <tt>Component</tt> which displays the specified + * <tt>photoLabelIcon</tt> + */ + private Component createDefaultPhotoPanel(ImageIcon photoLabelIcon) + { + JLabel photoLabel = new JLabel(); + + photoLabel.setIcon(photoLabelIcon); + + @SuppressWarnings("serial") + JPanel photoPanel + = new TransparentPanel(new GridBagLayout()) + { + /** + * @{inheritDoc} + */ + @Override + public void paintComponent(Graphics g) + { + super.paintComponent(g); + + g = g.create(); + try + { + AntialiasingManager.activateAntialiasing(g); + + g.setColor(Color.GRAY); + g.fillRoundRect( + 0, 0, this.getWidth(), this.getHeight(), + 6, 6); + } + finally + { + g.dispose(); + } + } + }; + + photoPanel.setPreferredSize(new Dimension(320, 240)); + + GridBagConstraints photoPanelConstraints = new GridBagConstraints(); + + photoPanelConstraints.anchor = GridBagConstraints.CENTER; + photoPanelConstraints.fill = GridBagConstraints.NONE; + photoPanel.add(photoLabel, photoPanelConstraints); + + return photoPanel; + } + + /** + * Initializes a new <tt>VideoContainer</tt> instance which is to contain + * the visual/video <tt>Component</tt>s of the telephony conference depicted + * by this instance. + */ + private VideoContainer createVideoContainer() + { + VideoContainer videoContainer = new VideoContainer(null, true); + + thumbnailPanel.setBackground(Color.DARK_GRAY); + thumbnailPanel.add(thumbnailContainer, BorderLayout.NORTH); + + add(thumbnailPanel, BorderLayout.EAST); + add(videoContainer, BorderLayout.CENTER); + + return videoContainer; + } + + /** + * Shows/hides the participants thumbnails list. + * + * @param show <tt>true</tt> to show the participants list, <tt>false</tt> + * to hide it + */ + public void showThumbnailsList(boolean show) + { + thumbnailPanel.setVisible(show); + } + + /** + * {@inheritDoc} + */ + @Override + public void dispose() + { + try + { + uiVideoHandler.deleteObserver(uiVideoHandlerObserver); + } + finally + { + super.dispose(); + } + } + + /** + * Determines whether a specific <tt>ConferenceMember</tt> represents the + * same conference participant as a specific <tt>CallPeer</tt>. If the + * specified <tt>conferenceMember</tt> is <tt>null</tt>, returns + * <tt>true</tt>. Otherwise, determines whether the addresses of the + * specified <tt>conferenceMember</tt> and the specified <tt>callPeer</tt> + * identify one and the same entity. + * + * @param conferenceMember the <tt>ConferenceMember</tt> to be checked + * whether is represents the same conference participant as the specified + * <tt>callPeer</tt>. If it is <tt>null</tt>, <tt>true</tt> is returned. + * @param callPeer the <tt>CallPeer</tt> to be checked whether it represents + * the same conference participant as the specified + * <tt>conferenceMember</tt> + * @return <tt>true</tt> if the specified <tt>conferenceMember</tt> and the + * specified <tt>callPeer</tt> represent the same conference participant or + * the specified <tt>conferenceMember</tt> is <tt>null</tt>; otherwise, + * <tt>false</tt> + */ + private boolean isConferenceMemberCallPeer( + ConferenceMember conferenceMember, + CallPeer callPeer) + { + return + (conferenceMember == null) + ? true + : CallManager.addressesAreEqual( + conferenceMember.getAddress(), + callPeer.getAddress()); + } + + /** + * Determines whether a specific <tt>ConferenceMember</tt> represents the + * local peer/user. Since this instance depicts a whole telephony + * conference, the local peer/user may be participating with multiple + * <tt>Call</tt>s in it. The <tt>Call</tt>s may be through different + * (local) accounts. That's why the implementation determines whether the + * address of the specified <tt>conferenceMember</tt> identifies the address + * of a (local) accounts involved in the telephony conference depicted by + * this instance. + * + * @param conferenceMember the <tt>ConferenceMember</tt> to be checked + * whether it represents the local peer/user + * @return <tt>true</tt> if the specified <tt>conferenceMember</tt> + * represents the local peer/user; otherwise, <tt>false</tt> + */ + private boolean isConferenceMemberLocalUser( + ConferenceMember conferenceMember) + { + String address = conferenceMember.getAddress(); + + for (Call call : callConference.getCalls()) + { + if (CallManager.addressesAreEqual( + address, + call.getProtocolProvider().getAccountID() + .getAccountAddress())) + { + return true; + } + } + return false; + } + + private void removeConferenceMemberContainers( + ConferenceParticipantContainer cpc, + boolean all) + { + List<ConferenceParticipantContainer> cmcs + = cpc.conferenceMemberContainers; + + if ((cmcs != null) && !cmcs.isEmpty()) + { + Iterator<ConferenceParticipantContainer> i = cmcs.iterator(); + + while (i.hasNext()) + { + ConferenceParticipantContainer cmc = i.next(); + + if (all || cmc.toBeRemoved) + { + i.remove(); + + videoContainer.remove(cmc.getComponent()); + cmc.dispose(); + } + } + } + } + + /** + * Updates the <tt>ConferenceParticipantContainer</tt>s which depict the + * <tt>ConferenceMember</tt>s of the <tt>CallPeer</tt> depicted by a + * specific <tt>ConferenceParticipantContainer</tt>. + * + * @param cpc the <tt>ConferenceParticipantContainer</tt> which depicts the + * <tt>CallPeer</tt> whose <tt>ConferenceMember</tt>s are to be depicted + * @param videos the visual <tt>Component</tt>s displaying video streaming + * from the remote peer (represented by <tt>cpc</tt>) to the local peer/user + * @param videoTelephony the <tt>OperationSetVideoTelephony</tt> which + * retrieved the specified <tt>videos</tt> from the <tt>CallPeer</tt> + * depicted by <tt>cpc</tt>. While the <tt>CallPeer</tt> could be queried + * for it, such a query would waste more resources at run time given that + * the invoker has it already. + */ + private void updateConferenceMemberContainers( + ConferenceParticipantContainer cpc, + List<Component> videos, + OperationSetVideoTelephony videoTelephony) + { + CallPeer callPeer = (CallPeer) cpc.getParticipant(); + List<ConferenceParticipantContainer> cmcs + = cpc.conferenceMemberContainers; + + /* + * Invalidate all conferenceMemberContainers. Then validate which of + * them are to remain and which of them are to really be removed + * later on. + */ + if (cmcs != null) + { + for (ConferenceParticipantContainer cmc : cmcs) + cmc.toBeRemoved = true; + } + + /* + * Depict the remote videos. They may or may not be associated with + * ConferenceMembers so the ConferenceMembers which have no + * associated videos will be depicted afterwards. + */ + if (videos != null) + { + Component video = cpc.getVideo(); + + for (Component conferenceMemberVideo : videos) + { + /* + * One of the remote videos is already used to depict the + * callPeer. + */ + if (conferenceMemberVideo == video) + continue; + + boolean addNewConferenceParticipantContainer = true; + ConferenceMember conferenceMember + = videoTelephony.getConferenceMember( + callPeer, + conferenceMemberVideo); + + if (cmcs == null) + { + cmcs = new LinkedList<ConferenceParticipantContainer>(); + cpc.conferenceMemberContainers = cmcs; + } + else + { + for (ConferenceParticipantContainer cmc : cmcs) + { + Object cmcParticipant = cmc.getParticipant(); + + if (conferenceMember == null) + { + if (cmcParticipant == callPeer) + { + Component cmcVideo = cmc.getVideo(); + + if (cmcVideo == null) + { + cmc.setVideo(conferenceMemberVideo); + cmc.toBeRemoved = false; + addNewConferenceParticipantContainer + = false; + break; + } + else if (cmcVideo == conferenceMemberVideo) + { + cmc.toBeRemoved = false; + addNewConferenceParticipantContainer + = false; + break; + } + } + } + else if (cmcParticipant == conferenceMember) + { + cmc.setVideo(conferenceMemberVideo); + cmc.toBeRemoved = false; + addNewConferenceParticipantContainer = false; + break; + } + } + } + + if (addNewConferenceParticipantContainer) + { + ConferenceParticipantContainer cmc + = (conferenceMember == null) + ? new ConferenceParticipantContainer( + callPeer, + conferenceMemberVideo) + : new ConferenceParticipantContainer( + conferenceMember, + conferenceMemberVideo); + + cmcs.add(cmc); + } + } + } + + /* + * Depict the ConferenceMembers which have not been depicted yet. + * They have no associated videos. + */ + List<ConferenceMember> conferenceMembers + = callPeer.getConferenceMembers(); + + if (!conferenceMembers.isEmpty()) + { + if (cmcs == null) + { + cmcs = new LinkedList<ConferenceParticipantContainer>(); + cpc.conferenceMemberContainers = cmcs; + } + for (ConferenceMember conferenceMember : conferenceMembers) + { + /* + * If the callPeer reports itself as a ConferenceMember, then + * we've already depicted it with cpc. + */ + if (isConferenceMemberCallPeer(conferenceMember, callPeer)) + continue; + /* + * If the callPeer reports the local peer/user as a + * ConferenceMember, then we've already depicted it. + */ + if (isConferenceMemberLocalUser(conferenceMember)) + continue; + + boolean addNewConferenceParticipantContainer = true; + + for (ConferenceParticipantContainer cmc : cmcs) + { + if (cmc.getParticipant() == conferenceMember) + { + /* + * It is possible to have a ConferenceMember who is + * sending video but we just do not have the SSRC of + * that video to associate the video with the + * ConferenceMember. In such a case, we may be depicting + * the ConferenceMember twice: once with video without a + * ConferenceMember and once with a ConferenceMember + * without video. This will surely be the case at the + * time of this writing with non-focus participants in a + * telephony conference hosted on a Jitsi Videobridge. + * Such a display is undesirable. If the + * conferenceMember is known to send video, we will not + * display it until we associated it with a video. This + * way, if a ConferenceMember is not sending video, we + * will depict it and we can be sure that no video + * without a ConferenceMember association will be + * depicting it a second time. + */ + if (cmc.toBeRemoved + && !conferenceMember + .getVideoStatus() + .allowsSending()) + { + cmc.setVideo(null); + cmc.toBeRemoved = false; + } + addNewConferenceParticipantContainer = false; + break; + } + } + + if (addNewConferenceParticipantContainer) + { + ConferenceParticipantContainer cmc + = new ConferenceParticipantContainer( + conferenceMember, + null); + + cmcs.add(cmc); + } + } + } + + if ((cmcs != null) && !cmcs.isEmpty()) + { + removeConferenceMemberContainers(cpc, false); + /* + * If cpc is already added to the user interface hierarchy of this + * instance, then it was there before the update procedure and it + * was determined to be appropriate to continue to depict its model. + * Consequently, its Component will be neither added to (because it + * was already added) nor removed from the user interface hierarchy + * of this instance. That's why we have make sure that the + * Components of its conferenceMemberContainers are also added to + * the user interface. + */ + if (UIVideoHandler2.isAncestor(this, cpc.getComponent())) + addConferenceMemberContainers(cpc); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected ConferenceCallPeerRenderer updateViewFromModel( + ConferenceCallPeerRenderer callPeerPanel, + CallPeer callPeer) + { + if (callPeer == null) + { + /* + * The local peer/user will be represented by a Call which has a + * CallPeer who provides local video. However, if the user has + * selected to hide the local video, the local peer/user will not be + * represented at all. + */ + Component video = null; + + if (uiVideoHandler.isLocalVideoVisible()) + { + for (Call aCall : callConference.getCalls()) + { + Iterator<? extends CallPeer> callPeerIter + = aCall.getCallPeers(); + OperationSetVideoTelephony videoTelephony + = aCall.getProtocolProvider().getOperationSet( + OperationSetVideoTelephony.class); + + while (callPeerIter.hasNext()) + { + callPeer = callPeerIter.next(); + + if (videoTelephony != null) + { + try + { + video + = videoTelephony.getLocalVisualComponent( + callPeer); + } + catch (OperationFailedException ofe) + { + logger.error( + "Failed to retrieve the local video" + + " for display", + ofe); + } + if (video != null) + break; + } + } + if (video != null) + break; + } + } + + if (callPeer == null) + callPeerPanel = null; + else + { + Call call = callPeer.getCall(); + + if (callPeerPanel instanceof ConferenceParticipantContainer) + { + ConferenceParticipantContainer cpc + = (ConferenceParticipantContainer) callPeerPanel; + + if (cpc.getParticipant() == call) + cpc.setVideo(video); + else + callPeerPanel = null; + } + else + callPeerPanel = null; + if (callPeerPanel == null) + { + callPeerPanel + = new ConferenceParticipantContainer(call, video); + } + } + } + else + { + /* + * The specified callPeer will be represented by one of its remote + * videos which is not associated with a ConferenceMember or is + * associated with a ConferenceMember representing the callPeer + * itself. + */ + OperationSetVideoTelephony videoTelephony + = callPeer.getProtocolProvider().getOperationSet( + OperationSetVideoTelephony.class); + List<Component> videos = null; + Component video = null; + + if (videoTelephony != null) + { + videos = videoTelephony.getVisualComponents(callPeer); + if ((videos != null) && !videos.isEmpty()) + { + for (Component aVideo : videos) + { + ConferenceMember conferenceMember + = videoTelephony.getConferenceMember( + callPeer, + aVideo); + + if (isConferenceMemberCallPeer( + conferenceMember, + callPeer)) + { + video = aVideo; + break; + } + } + } + } + + ConferenceParticipantContainer cpc = null; + + if (callPeerPanel instanceof ConferenceParticipantContainer) + { + cpc = (ConferenceParticipantContainer) callPeerPanel; + if (cpc.getParticipant() == callPeer) + cpc.setVideo(video); + else + cpc = null; + } + if (cpc == null) + cpc = new ConferenceParticipantContainer(callPeer, video); + callPeerPanel = cpc; + + // Update the conferenceMemberContainers of the cpc. + updateConferenceMemberContainers(cpc, videos, videoTelephony); + } + return callPeerPanel; + } + + /** + * {@inheritDoc} + * + * If {@link #SHOW_TOOLBARS} is <tt>false</tt>, disables the use of + * <tt>ConferenceParticipantContainer</tt>. A reason for such a value of + * <tt>SHOW_TOOLBARS</tt> may be that the functionality implemented in the + * model may not fully support mapping of visual <tt>Component</tt>s + * displaying video to telephony conference participants (e.g. in telephony + * conferences utilizing the Jitsi Videobridge server-side technology). In + * such a case displays the videos only, does not map videos to participants + * and does not display participants who do not have videos. + */ + @Override + protected void updateViewFromModelInEventDispatchThread() + { + if (SHOW_TOOLBARS) + { + super.updateViewFromModelInEventDispatchThread(); + return; + } + + /* + * Determine the set of visual Components displaying video streaming + * between the local peer/user and the remote peers which are to be + * depicted by this instance. + */ + Component localVideo = null; + Set<Component> videos = new HashSet<Component>(); + + for (Call call : callConference.getCalls()) + { + OperationSetVideoTelephony videoTelephony + = call.getProtocolProvider().getOperationSet( + OperationSetVideoTelephony.class); + + if (videoTelephony == null) + continue; + + Iterator<? extends CallPeer> callPeerIter = call.getCallPeers(); + + while (callPeerIter.hasNext()) + { + CallPeer callPeer = callPeerIter.next(); + + /* + * TODO VideoConferenceCallPanel respects + * UIVideoHandler2.isLocalVideoVisible() in order to react to + * the associated button at the bottom of the CallPanel. + * However, it does not add a close button on top of the local + * video in contrast to OneToOneCallPeerPanel. Overall, the + * result is questionable. + */ + if (uiVideoHandler.isLocalVideoVisible() + && (localVideo == null)) + { + try + { + localVideo + = videoTelephony.getLocalVisualComponent(callPeer); + } + catch (OperationFailedException ofe) + { + /* + * We'll just try to get the local video through another + * CallPeer then. + */ + } + if (localVideo != null) + videos.add(localVideo); + } + + List<Component> callPeerRemoteVideos + = videoTelephony.getVisualComponents(callPeer); + + videos.addAll(callPeerRemoteVideos); + } + } + + /* + * Remove the Components of this view which are no longer present in the + * model. + */ + Iterator<Component> thisVideoIter = this.videos.iterator(); + + while (thisVideoIter.hasNext()) + { + Component thisVideo = thisVideoIter.next(); + + if (!videos.contains(thisVideo)) + { + thisVideoIter.remove(); + videoContainer.remove(thisVideo); + } + + /* + * If a video is known to be depicted by this view and is still + * present in the model, then we could remove it from the set of + * videos present in the model in order to prevent going through the + * procedure of adding it to this view. However, we choose to play + * on the safe side. + */ + } + + /* + * Add the Components of the model which are not depicted by this view. + */ + for (Component video : videos) + { + if (!UIVideoHandler2.isAncestor(videoContainer, video)) + { + this.videos.add(video); + videoContainer.add( + video, + (video == localVideo) + ? VideoLayout.LOCAL + : VideoLayout.CENTER_REMOTE); + } + } + } + + @Override + protected void viewForModelAdded( + ConferenceCallPeerRenderer callPeerPanel, + CallPeer callPeer) + { + videoContainer.add( + callPeerPanel.getComponent(), + VideoLayout.CENTER_REMOTE); + if ((callPeer != null) + && (callPeerPanel instanceof ConferenceParticipantContainer)) + { + addConferenceMemberContainers( + (ConferenceParticipantContainer) callPeerPanel); + } + } + + @Override + protected void viewForModelRemoved( + ConferenceCallPeerRenderer callPeerPanel, + CallPeer callPeer) + { + videoContainer.remove(callPeerPanel.getComponent()); + if ((callPeer != null) + && (callPeerPanel instanceof ConferenceParticipantContainer)) + { + removeConferenceMemberContainers( + (ConferenceParticipantContainer) callPeerPanel, + true); + } + } + + /** + * Implements an AWT <tt>Component</tt> which contains the user interface + * elements depicting a specific participant in the telephony conference + * depicted by a <tt>VideoConferenceCallPanel</tt>. + */ + private class ConferenceParticipantContainer + extends TransparentPanel + implements ConferenceCallPeerRenderer + { + /** + * The list of <tt>ConferenceParticipantContainer</tt>s which represent + * the <tt>ConferenceMember</tt>s of the participant represented by this + * instance. Since a <tt>CallPeer</tt> may send the local peer/user + * multiple videos without providing a way to associate a + * ConferenceMember with each one of them, the list may contain + * <tt>ConferenceParticipantContainer</tt>s which do not represent a + * specific <tt>ConferenceMember</tt> instance but rather a video sent + * by a <tt>CallPeer</tt> to the local peer/user which looks like (in + * the terms of <tt>VideoConferenceCallPanel) a member of a conference + * organized by the <tt>CallPeer</tt> in question. + * <p> + * Implements a state which is private to + * <tt>VideoConferenceCallPanel</tt> and is of no concern to + * <tt>ConferenceParticipantContainer</tt>. + * </p> + */ + List<ConferenceParticipantContainer> conferenceMemberContainers; + + /** + * The indicator which determines whether this instance is to be removed + * because it has become out-of-date, obsolete, unnecessary. + * <p> + * Implements a state which is private to + * <tt>VideoConferenceCallPanel</tt> and is of no concern to + * <tt>ConferenceParticipantContainer</tt>. + * </p> + */ + boolean toBeRemoved; + + /** + * The <tt>BasicConferenceParticipantPanel</tt> which is displayed at + * the bottom of this instance, bellow the {@link #video} (i.e. + * {@link #videoContainer}) and is referred to as the tool bar. + */ + private final BasicConferenceParticipantPanel<?> toolBar; + + /** + * The visual <tt>Component</tt>, if any, displaying video which is + * depicted by this instance. + */ + private Component video; + + /** + * The <tt>VideoContainer</tt> which lays out the video depicted by this + * instance i.e. {@link #video}. + */ + private final VideoContainer videoContainer; + + /** + * The <tt>CallPeer</tt> associated with this container, if it has been + * created to represent a <tt>CallPeer</tt>. + */ + private CallPeer callPeer; + + /** + * The <tt>conferenceMember</tt> associated with this container, if it + * has been created to represent a <tt>conferenceMember</tt>. + */ + private ConferenceMember conferenceMember; + + /** + * Indicates that this container contains information for the local + * user. + */ + private boolean isLocalUser; + + /** + * Initializes a new <tt>ConferenceParticipantContainer</tt> instance + * which is to depict the local peer/user. + * + * @param call a <tt>Call</tt> which is to provide information about the + * local peer/user + * @param video the visual <tt>Component</tt>, if any, displaying the + * video streaming from the local peer/user to the remote peer(s) + */ + public ConferenceParticipantContainer(Call call, Component video) + { + this( + createDefaultPhotoPanel(call), + video, + new ConferencePeerPanel( + VideoConferenceCallPanel.this, + call, + true), + null, null, true); + } + + public ConferenceParticipantContainer( + CallPeer callPeer, + Component video) + { + this( createDefaultPhotoPanel(callPeer), + video, + new ConferencePeerPanel( + VideoConferenceCallPanel.this, + callPeer, + true), + callPeer, null, false); + } + + private ConferenceParticipantContainer( + Component noVideo, + Component video, + BasicConferenceParticipantPanel<?> toolBar, + CallPeer callPeer, + ConferenceMember conferenceMember, + boolean isLocalUser) + { + super(new BorderLayout()); + + this.callPeer = callPeer; + this.conferenceMember = conferenceMember; + this.isLocalUser = isLocalUser; + + videoContainer = new VideoContainer(noVideo, false); + add(videoContainer, BorderLayout.CENTER); + + this.toolBar = toolBar; + if (this.toolBar != null) + add(this.toolBar, BorderLayout.SOUTH); + + if (video != null) + { + setVideo(video); + } + else + setVisible(false); + } + + public ConferenceParticipantContainer( + ConferenceMember conferenceMember, + Component video) + { + this( + createDefaultPhotoPanel(conferenceMember), + video, + new ConferenceMemberPanel( + VideoConferenceCallPanel.this, + conferenceMember, + true), + null, conferenceMember, false); + } + + public void dispose() + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.dispose(); + + // Dispose of the conferenceMemberContainers if any. + /* + * XXX The field conferenceMemberContainers implements a state + * private to VideoConferenceCallPanel which the latter makes sure + * to access on the AWT event dispatching thread only. Since we are + * going out of our way here to help VideoConferenceCallPanel, + * ensure that the mentioned synchronization rule is not violated. + */ + CallManager.assertIsEventDispatchingThread(); + if (conferenceMemberContainers != null) + { + for (ConferenceParticipantContainer cmc + : conferenceMemberContainers) + { + cmc.dispose(); + } + } + } + + public CallPanel getCallPanel() + { + return getCallRenderer().getCallContainer(); + } + + public SwingCallRenderer getCallRenderer() + { + return VideoConferenceCallPanel.this; + } + + public Component getComponent() + { + return this; + } + + private ConferenceCallPeerRenderer + getConferenceCallPeerRendererDelegate() + { + return + (toolBar instanceof ConferenceCallPeerRenderer) + ? (ConferenceCallPeerRenderer) toolBar + : null; + } + + /** + * Gets the conference participant depicted by this instance. + * + * @return the conference participant depicted by this instance + */ + public Object getParticipant() + { + return (toolBar == null) ? null : toolBar.getParticipant(); + } + + public Component getVideo() + { + return video; + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. Otherwise, returns <tt>false</tt>. + */ + public boolean isLocalVideoVisible() + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + return (delegate == null) ? false : delegate.isLocalVideoVisible(); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void printDTMFTone(char dtmfChar) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.printDTMFTone(dtmfChar); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void securityNegotiationStarted( + CallPeerSecurityNegotiationStartedEvent ev) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.securityNegotiationStarted(ev); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void securityOff(CallPeerSecurityOffEvent ev) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.securityOff(ev); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void securityOn(CallPeerSecurityOnEvent ev) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.securityOn(ev); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void securityPending() + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.securityPending(); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void securityTimeout(CallPeerSecurityTimeoutEvent ev) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.securityTimeout(ev); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void setErrorReason(String reason) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.setErrorReason(reason); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void setLocalVideoVisible(boolean visible) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.setLocalVideoVisible(visible); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void setMute(boolean mute) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.setMute(mute); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void setOnHold(boolean onHold) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.setOnHold(onHold); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void setPeerImage(byte[] image) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.setPeerImage(image); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void setPeerName(String name) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.setPeerName(name); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void setPeerState( + CallPeerState oldState, + CallPeerState newState, + String stateString) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.setPeerState(oldState, newState, stateString); + } + + /** + * {@inheritDoc} + * + * Delegates to the <tt>toolBar</tt>, if the latter implements + * <tt>ConferenceCallPeerRenderer</tt>, because this instance is a + * container only. + */ + public void setSecurityPanelVisible(boolean visible) + { + ConferenceCallPeerRenderer delegate + = getConferenceCallPeerRendererDelegate(); + + if (delegate != null) + delegate.setSecurityPanelVisible(visible); + } + + /** + * Sets the visual <tt>Component</tt> displaying the video associated + * with the participant depicted by this instance. + * + * @param video the visual <tt>Component</tt> displaying video which is + * to be associated with the participant depicted by this instance + */ + void setVideo(Component video) + { + if (this.video != video) + { + if (this.video != null) + videoContainer.remove(this.video); + + this.video = video; + + if (this.video != null) + { + setVisible(true); + videoContainer.add(this.video, VideoLayout.CENTER_REMOTE); + } + else + setVisible(false); + + // Update thumbnails container according to video status. + if (thumbnailContainer != null) + { + if (conferenceMember != null) + thumbnailContainer + .updateThumbnail(conferenceMember, (video != null)); + else if (callPeer != null) + thumbnailContainer + .updateThumbnail(callPeer, (video != null)); + else if (isLocalUser) + thumbnailContainer + .updateThumbnail((video != null)); + } + } + } + } +} |