aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl/gui/main/call
diff options
context:
space:
mode:
authorWolfgang Wiedmeyer <wolfgit@wiedmeyer.de>2017-03-11 22:15:03 +0100
committerWolfgang Wiedmeyer <wolfgit@wiedmeyer.de>2017-03-11 22:15:03 +0100
commit85901329b0794b136b96bf745f4ab1572806fc89 (patch)
treef23da7e97cae727f39d825f0fef8348cffb238e4 /src/net/java/sip/communicator/impl/gui/main/call
parent3db2e44f186c59429901b2c899e139ea60117a55 (diff)
parentcf5da997da8820b4050f5b87ee9440a0ede36d1f (diff)
downloadjitsi-85901329b0794b136b96bf745f4ab1572806fc89.zip
jitsi-85901329b0794b136b96bf745f4ab1572806fc89.tar.gz
jitsi-85901329b0794b136b96bf745f4ab1572806fc89.tar.bz2
Merge commit 'cf5da99'HEADmaster
Signed-off-by: Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de>
Diffstat (limited to 'src/net/java/sip/communicator/impl/gui/main/call')
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java1234
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java8
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallInfoFrame.java24
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java548
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/FullScreenLayout.java348
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/PreCallDialog.java6
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/SecurityPanel.java230
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/TransferCallDialog.java276
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java1136
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/call/conference/VideoConferenceCallPanel.java2786
10 files changed, 3306 insertions, 3290 deletions
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java
index 697a3fc..de63f79 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallDialog.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,619 +15,619 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call;
-
-import java.awt.*;
-import java.awt.event.*;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
-
-/**
- * The dialog created for a given call.
- *
- * @author Yana Stamcheva
- * @author Adam Netocny
- * @author Lyubomir Marinov
- */
-public class CallDialog
- extends SIPCommFrame
- implements CallContainer,
- CallTitleListener
-{
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- /**
- * Enabling force minimized mode will always open call dialog minimized.
- * The call dialog still can be shown, but by default it will be minimized.
- */
- private static final String FORCE_MINIMIZED_MODE
- = "net.java.sip.communicator.impl.gui.main.call.FORCE_MINIMIZED_MODE";
-
- /**
- * Finds a <tt>Container</tt> which is an ancestor of a specific
- * <tt>Component</tt>, has a set <tt>preferredSize</tt> and is closest to
- * the specified <tt>Component</tt> up the ancestor hierarchy.
- *
- * @param component the <tt>Component</tt> whose ancestor hierarchy is to be
- * searched upwards
- * @return a <tt>Container</tt>, if any, which is an ancestor of the
- * specified <tt>component</tt>, has a set <tt>preferredSize</tt> and is
- * closest to the specified <tt>component</tt> up the ancestor hierarchy
- */
- private static Container findClosestAncestorWithSetPreferredSize(
- Component component)
- {
- if ((component instanceof Container) && component.isPreferredSizeSet())
- return (Container) component;
- else
- {
- Container parent;
-
- while ((parent = component.getParent()) != null)
- {
- if (parent.isPreferredSizeSet())
- return parent;
- else
- component = parent;
- }
- return null;
- }
- }
-
- /**
- * The panel, where all call components are added.
- */
- private CallPanel callPanel;
-
- private final WindowStateListener windowStateListener
- = new WindowStateListener()
- {
- public void windowStateChanged(WindowEvent ev)
- {
- switch (ev.getID())
- {
- case WindowEvent.WINDOW_DEACTIVATED:
- case WindowEvent.WINDOW_ICONIFIED:
- case WindowEvent.WINDOW_LOST_FOCUS:
- setFullScreen(false);
- break;
- }
- }
- };
-
- /**
- * Creates a <tt>CallDialog</tt> by specifying the underlying call panel.
- */
- public CallDialog()
- {
- super(true, false);
-
- setMinimumSize(new Dimension(360, 300));
- }
-
- /**
- * Adds a call panel.
- *
- * @param callPanel the call panel to add to this dialog
- */
- public void addCallPanel(CallPanel callPanel)
- {
- this.callPanel = callPanel;
-
- getContentPane().add(callPanel);
-
- callPanel.addCallTitleListener(this);
- setTitle(callPanel.getCallTitle());
-
- if (!isVisible())
- {
- pack();
-
- // checks whether we need to open the call dialog in minimized mode
- if(GuiActivator.getConfigurationService()
- .getBoolean(FORCE_MINIMIZED_MODE, false))
- {
- setState(ICONIFIED);
- }
- setVisible(true);
- }
- }
-
- /**
- * Called when the title of the given <tt>CallPanel</tt> changes.
- *
- * @param callPanel the <tt>CallPanel</tt>, which title has changed
- */
- public void callTitleChanged(CallPanel callPanel)
- {
- if (this.callPanel.equals(callPanel))
- setTitle(callPanel.getCallTitle());
- }
-
- /**
- * {@inheritDoc}
- *
- * Hang ups the call/telephony conference depicted by this
- * <tt>CallDialog</tt> on close.
- */
- @Override
- protected void close(boolean escape)
- {
- if (escape)
- {
- /*
- * In full-screen mode, ESC does not close this CallDialog but exits
- * from full-screen to windowed mode.
- */
- if (isFullScreen())
- {
- setFullScreen(false);
- return;
- }
- }
- else
- {
- /*
- * If the window has been closed by clicking the X button or
- * pressing the key combination corresponding to the same button we
- * close the window first and then perform all hang up operations.
- */
-
- callPanel.disposeCallInfoFrame();
- // We hide the window here. It will be disposed when the call has
- // been ended.
- setVisible(false);
- }
-
- // Then perform hang up operations.
- callPanel.actionPerformedOnHangupButton(escape);
- }
-
- /**
- * {@inheritDoc}
- *
- * The delay implemented by <tt>CallDialog</tt> is 5 seconds.
- */
- public void close(final CallPanel callPanel, boolean delay)
- {
- if (this.callPanel.equals(callPanel))
- {
- if (delay)
- {
- Timer timer
- = new Timer(
- 5000,
- new ActionListener()
- {
- public void actionPerformed(ActionEvent ev)
- {
- dispose();
- }
- });
-
- timer.setRepeats(false);
- timer.start();
- }
- else
- {
- dispose();
- }
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * <tt>CallDialog</tt> prepares the <tt>CallPanel</tt> it contains for
- * garbage collection.
- */
- @Override
- public void dispose()
- {
- super.dispose();
-
- /*
- * Technically, CallManager adds/removes the callPanel to/from this
- * instance. It may want to just move it to another CallContainer so it
- * does not sound right that we are disposing of it. But we do not have
- * such a case at this time so try to reduce the risk of memory leaks.
- */
- if (this.callPanel != null)
- {
- callPanel.disposeCallInfoFrame();
- callPanel.dispose();
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * Attempts to adjust the size of this <tt>Frame</tt> as requested in the
- * AWT event dispatching thread.
- * <p>
- * The method may be executed on the AWT event dispatching thread only
- * because whoever is making the decision to request an adjustment of the
- * Frame size in relation to a AWT Component should be analyzing that same
- * Component in the AWT event dispatching thread only.
- * </p>
- *
- * @throws RuntimeException if the method is not called on the AWT event
- * dispatching thread
- */
- public void ensureSize(Component component, int width, int height)
- {
- CallManager.assertIsEventDispatchingThread();
-
- Frame frame = CallPeerRendererUtils.getFrame(component);
-
- if (frame == null)
- return;
- else if ((frame.getExtendedState() & Frame.MAXIMIZED_BOTH)
- == Frame.MAXIMIZED_BOTH)
- {
- /*
- * Forcing the size of a Component which is displayed in a maximized
- * window does not sound like anything we want to do.
- */
- return;
- }
- else if (frame.equals(
- frame.getGraphicsConfiguration().getDevice()
- .getFullScreenWindow()))
- {
- /*
- * Forcing the size of a Component which is displayed in a
- * full-screen window does not sound like anything we want to do.
- */
- return;
- }
- else if (!frame.equals(this))
- {
- /* This Frame will try to adjust only its own size. */
- return;
- }
- else if ((component.getHeight() >= height)
- && (component.getWidth() >= width))
- {
- /*
- * We will only enlarge the frame size. If the component has already
- * been given at least what it is requesting, do not enlarge the
- * frame size because the whole calculation is prone to inaccuracy.
- */
- return;
- }
- else
- {
- /*
- * If there is no callPanel, it is unlikely that this CallDialog
- * will be asked to ensureSize. Anyway, support the scenario just in
- * case. In light of the absence of a callPanel to guide this
- * CallDialog about the preferred size, we do not have much of a
- * choice but to trust the method arguments.
- */
- if (callPanel != null)
- {
- /*
- * If there is a callPanel, we are likely to get a much better
- * estimation about the preferred size by asking the callPanel
- * rather than by trusting the method arguments. For example,
- * the visual Component displaying the video streaming from the
- * local user/peer to the remote peer(s) will think that its
- * preferred size is the one to base this Frame's size on but
- * that may be misleading because the local video may not be
- * displayed with its preferred size even if this Frame's size
- * will accommodate it.
- */
- /*
- * Just asking the callPanel about its preferredSize would've
- * been terrificly great. Unfortunately, that is presently
- * futile because the callPanel may have a preferredSize while
- * we are still required to display visual Components displaying
- * video in their non-scaled size. The same goes for any
- * Container which is an ancestor of the specified component.
- */
- Container ancestor
- = findClosestAncestorWithSetPreferredSize(component);
-
- if (ancestor == null)
- ancestor = callPanel;
- /*
- * If the ancestor has a forced preferredSize, its LayoutManager
- * may be able to give a good enough estimation.
- */
- if (ancestor.isPreferredSizeSet())
- {
- LayoutManager ancestorLayout = ancestor.getLayout();
-
- if (ancestorLayout != null)
- {
- Dimension preferredLayoutSize
- = ancestorLayout.preferredLayoutSize(ancestor);
-
- if (preferredLayoutSize != null)
- {
- component = ancestor;
- width = preferredLayoutSize.width;
- height = preferredLayoutSize.height;
- }
- }
- }
- else
- {
- /*
- * If the ancestor doesn't have a preferredSize forced, then
- * we may think that it will calculate an appropriate
- * preferredSize itself.
- */
- Dimension prefSize = ancestor.getPreferredSize();
-
- if (prefSize != null)
- {
- component = ancestor;
- width = prefSize.width;
- height = prefSize.height;
- }
- }
- }
-
- /*
- * If the component (which may be an ancestor of the Component
- * specified as an argument to the ensureSize method at this point)
- * has not been given a size, we will make a mistake if we try to
- * use it for the purposes of determining how much this Frame is to
- * be enlarged.
- */
- Dimension componentSize = component.getSize();
-
- if ((componentSize.width < 1) || (componentSize.height < 1))
- return;
-
- Dimension frameSize = frame.getSize();
- int newFrameWidth = frameSize.width + width - componentSize.width;
- int newFrameHeight
- = frameSize.height + height - componentSize.height;
-
- // Respect the minimum size.
- Dimension minSize = frame.getMinimumSize();
-
- if (newFrameWidth < minSize.width)
- newFrameWidth = minSize.width;
- if (newFrameHeight < minSize.height)
- newFrameHeight = minSize.height;
-
- // Don't get bigger than the screen.
- Rectangle screenBounds
- = frame.getGraphicsConfiguration().getBounds();
-
- if (newFrameWidth > screenBounds.width)
- newFrameWidth = screenBounds.width;
- if (newFrameHeight > screenBounds.height)
- newFrameHeight = screenBounds.height;
-
- /*
- * If we're going to make too small a change, don't even bother.
- * Besides, we don't want some weird recursive resizing.
- * Additionally, do not reduce the Frame size.
- */
- boolean changeWidth = ((newFrameWidth - frameSize.width) > 1);
- boolean changeHeight = ((newFrameHeight - frameSize.height) > 1);
-
- if (changeWidth || changeHeight)
- {
- if (!changeWidth)
- newFrameWidth = frameSize.width;
- else if (!changeHeight)
- newFrameHeight = frameSize.height;
-
- /*
- * The latest requirement with respect to the behavior upon
- * resizing is to center the Frame.
- */
- int newFrameX
- = screenBounds.x
- + (screenBounds.width - newFrameWidth) / 2;
- int newFrameY
- = screenBounds.y
- + (screenBounds.height - newFrameHeight) / 2;
-
- // Do not let the top left go out of the screen.
- if (newFrameX < screenBounds.x)
- newFrameX = screenBounds.x;
- if (newFrameY < screenBounds.y)
- newFrameY = screenBounds.y;
-
- frame.setBounds(
- newFrameX, newFrameY,
- newFrameWidth, newFrameHeight);
-
- /*
- * Make sure that the component which originally requested the
- * update to the size of the frame realizes the change as soon
- * as possible; otherwise, it may request yet another update.
- */
- if (frame.isDisplayable())
- {
- if (frame.isValid())
- frame.doLayout();
- else
- frame.validate();
- frame.repaint();
- }
- else
- frame.doLayout();
- }
- }
- }
-
- /**
- * Returns the frame of the call window.
- *
- * @return the frame of the call window
- */
- public JFrame getFrame()
- {
- return this;
- }
-
- /**
- * Overrides getMinimumSize and checks the minimum size that
- * is needed to display buttons and use it for minimum size if
- * needed.
- * @return minimum size.
- */
- @Override
- public Dimension getMinimumSize()
- {
- Dimension minSize = super.getMinimumSize();
-
- if(callPanel != null)
- {
- int minButtonWidth = callPanel.getMinimumButtonWidth();
-
- if(minButtonWidth > minSize.getWidth())
- minSize = new Dimension(minButtonWidth, 300);
- }
-
- return minSize;
- }
-
- /**
- * Indicates if the given <tt>callPanel</tt> is currently visible.
- *
- * @param callPanel the <tt>CallPanel</tt>, for which we verify
- * @return <tt>true</tt> if the given call container is visible in this
- * call window, otherwise - <tt>false</tt>
- */
- public boolean isCallVisible(CallPanel callPanel)
- {
- return this.callPanel.equals(callPanel) ? isVisible() : false;
- }
-
- /**
- * {@inheritDoc}
- */
- public boolean isFullScreen()
- {
- return isFullScreen(getFrame());
- }
-
- /**
- * Determines whether a specific <tt>Window</tt> is displayed in full-screen
- * mode.
- *
- * @param window the <tt>Window</tt> to be checked whether it is displayed
- * in full-screen mode
- * @return <tt>true</tt> if the specified <tt>window</tt> is displayed in
- * full-screen mode; otherwise, <tt>false</tt>
- */
- public static boolean isFullScreen(Window window)
- {
- GraphicsConfiguration graphicsConfiguration
- = window.getGraphicsConfiguration();
-
- if (graphicsConfiguration != null)
- {
- GraphicsDevice device = graphicsConfiguration.getDevice();
-
- if (device != null)
- return window.equals(device.getFullScreenWindow());
- }
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- public void setFullScreen(boolean fullScreen)
- {
- GraphicsConfiguration graphicsConfiguration
- = getGraphicsConfiguration();
-
- if (graphicsConfiguration != null)
- {
- GraphicsDevice device = graphicsConfiguration.getDevice();
-
- if (device != null)
- {
- boolean thisIsFullScreen = equals(device.getFullScreenWindow());
- boolean firePropertyChange = false;
- boolean setVisible = isVisible();
-
- try
- {
- if (fullScreen)
- {
- if (!thisIsFullScreen)
- {
- /*
- * XXX The setUndecorated method will only work if
- * this Window is not displayable.
- */
- windowDispose();
- setUndecorated(true);
-
- device.setFullScreenWindow(this);
- firePropertyChange = true;
- }
- }
- else if (thisIsFullScreen)
- {
- /*
- * XXX The setUndecorated method will only work if this
- * Window is not displayable.
- */
- windowDispose();
- setUndecorated(false);
-
- device.setFullScreenWindow(null);
- firePropertyChange = true;
- }
-
- if (firePropertyChange)
- {
- if (fullScreen)
- {
- addWindowStateListener(windowStateListener);
-
- /*
- * If full-screen mode, a black background is the
- * most common.
- */
- getContentPane().setBackground(Color.BLACK);
- }
- else
- {
- removeWindowStateListener(windowStateListener);
-
- /*
- * In windowed mode, a system-defined background is
- * the most common.
- */
- getContentPane().setBackground(null);
- }
-
- firePropertyChange(
- PROP_FULL_SCREEN,
- thisIsFullScreen,
- fullScreen);
- }
- }
- finally
- {
- /*
- * Regardless of whether this Window successfully entered or
- * exited full-screen mode, make sure that remains visible.
- */
- if (setVisible)
- setVisible(true);
- }
- }
- }
- }
-}
+package net.java.sip.communicator.impl.gui.main.call;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+
+/**
+ * The dialog created for a given call.
+ *
+ * @author Yana Stamcheva
+ * @author Adam Netocny
+ * @author Lyubomir Marinov
+ */
+public class CallDialog
+ extends SIPCommFrame
+ implements CallContainer,
+ CallTitleListener
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * Enabling force minimized mode will always open call dialog minimized.
+ * The call dialog still can be shown, but by default it will be minimized.
+ */
+ private static final String FORCE_MINIMIZED_MODE
+ = "net.java.sip.communicator.impl.gui.main.call.FORCE_MINIMIZED_MODE";
+
+ /**
+ * Finds a <tt>Container</tt> which is an ancestor of a specific
+ * <tt>Component</tt>, has a set <tt>preferredSize</tt> and is closest to
+ * the specified <tt>Component</tt> up the ancestor hierarchy.
+ *
+ * @param component the <tt>Component</tt> whose ancestor hierarchy is to be
+ * searched upwards
+ * @return a <tt>Container</tt>, if any, which is an ancestor of the
+ * specified <tt>component</tt>, has a set <tt>preferredSize</tt> and is
+ * closest to the specified <tt>component</tt> up the ancestor hierarchy
+ */
+ private static Container findClosestAncestorWithSetPreferredSize(
+ Component component)
+ {
+ if ((component instanceof Container) && component.isPreferredSizeSet())
+ return (Container) component;
+ else
+ {
+ Container parent;
+
+ while ((parent = component.getParent()) != null)
+ {
+ if (parent.isPreferredSizeSet())
+ return parent;
+ else
+ component = parent;
+ }
+ return null;
+ }
+ }
+
+ /**
+ * The panel, where all call components are added.
+ */
+ private CallPanel callPanel;
+
+ private final WindowStateListener windowStateListener
+ = new WindowStateListener()
+ {
+ public void windowStateChanged(WindowEvent ev)
+ {
+ switch (ev.getID())
+ {
+ case WindowEvent.WINDOW_DEACTIVATED:
+ case WindowEvent.WINDOW_ICONIFIED:
+ case WindowEvent.WINDOW_LOST_FOCUS:
+ setFullScreen(false);
+ break;
+ }
+ }
+ };
+
+ /**
+ * Creates a <tt>CallDialog</tt> by specifying the underlying call panel.
+ */
+ public CallDialog()
+ {
+ super(true, false);
+
+ setMinimumSize(new Dimension(360, 300));
+ }
+
+ /**
+ * Adds a call panel.
+ *
+ * @param callPanel the call panel to add to this dialog
+ */
+ public void addCallPanel(CallPanel callPanel)
+ {
+ this.callPanel = callPanel;
+
+ getContentPane().add(callPanel);
+
+ callPanel.addCallTitleListener(this);
+ setTitle(callPanel.getCallTitle());
+
+ if (!isVisible())
+ {
+ pack();
+
+ // checks whether we need to open the call dialog in minimized mode
+ if(GuiActivator.getConfigurationService()
+ .getBoolean(FORCE_MINIMIZED_MODE, false))
+ {
+ setState(ICONIFIED);
+ }
+ setVisible(true);
+ }
+ }
+
+ /**
+ * Called when the title of the given <tt>CallPanel</tt> changes.
+ *
+ * @param callPanel the <tt>CallPanel</tt>, which title has changed
+ */
+ public void callTitleChanged(CallPanel callPanel)
+ {
+ if (this.callPanel.equals(callPanel))
+ setTitle(callPanel.getCallTitle());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Hang ups the call/telephony conference depicted by this
+ * <tt>CallDialog</tt> on close.
+ */
+ @Override
+ protected void close(boolean escape)
+ {
+ if (escape)
+ {
+ /*
+ * In full-screen mode, ESC does not close this CallDialog but exits
+ * from full-screen to windowed mode.
+ */
+ if (isFullScreen())
+ {
+ setFullScreen(false);
+ return;
+ }
+ }
+ else
+ {
+ /*
+ * If the window has been closed by clicking the X button or
+ * pressing the key combination corresponding to the same button we
+ * close the window first and then perform all hang up operations.
+ */
+
+ callPanel.disposeCallInfoFrame();
+ // We hide the window here. It will be disposed when the call has
+ // been ended.
+ setVisible(false);
+ }
+
+ // Then perform hang up operations.
+ callPanel.actionPerformedOnHangupButton(escape);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * The delay implemented by <tt>CallDialog</tt> is 5 seconds.
+ */
+ public void close(final CallPanel callPanel, boolean delay)
+ {
+ if (this.callPanel.equals(callPanel))
+ {
+ if (delay)
+ {
+ Timer timer
+ = new Timer(
+ 5000,
+ new ActionListener()
+ {
+ public void actionPerformed(ActionEvent ev)
+ {
+ dispose();
+ }
+ });
+
+ timer.setRepeats(false);
+ timer.start();
+ }
+ else
+ {
+ dispose();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <tt>CallDialog</tt> prepares the <tt>CallPanel</tt> it contains for
+ * garbage collection.
+ */
+ @Override
+ public void dispose()
+ {
+ super.dispose();
+
+ /*
+ * Technically, CallManager adds/removes the callPanel to/from this
+ * instance. It may want to just move it to another CallContainer so it
+ * does not sound right that we are disposing of it. But we do not have
+ * such a case at this time so try to reduce the risk of memory leaks.
+ */
+ if (this.callPanel != null)
+ {
+ callPanel.disposeCallInfoFrame();
+ callPanel.dispose();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Attempts to adjust the size of this <tt>Frame</tt> as requested in the
+ * AWT event dispatching thread.
+ * <p>
+ * The method may be executed on the AWT event dispatching thread only
+ * because whoever is making the decision to request an adjustment of the
+ * Frame size in relation to a AWT Component should be analyzing that same
+ * Component in the AWT event dispatching thread only.
+ * </p>
+ *
+ * @throws RuntimeException if the method is not called on the AWT event
+ * dispatching thread
+ */
+ public void ensureSize(Component component, int width, int height)
+ {
+ CallManager.assertIsEventDispatchingThread();
+
+ Frame frame = CallPeerRendererUtils.getFrame(component);
+
+ if (frame == null)
+ return;
+ else if ((frame.getExtendedState() & Frame.MAXIMIZED_BOTH)
+ == Frame.MAXIMIZED_BOTH)
+ {
+ /*
+ * Forcing the size of a Component which is displayed in a maximized
+ * window does not sound like anything we want to do.
+ */
+ return;
+ }
+ else if (frame.equals(
+ frame.getGraphicsConfiguration().getDevice()
+ .getFullScreenWindow()))
+ {
+ /*
+ * Forcing the size of a Component which is displayed in a
+ * full-screen window does not sound like anything we want to do.
+ */
+ return;
+ }
+ else if (!frame.equals(this))
+ {
+ /* This Frame will try to adjust only its own size. */
+ return;
+ }
+ else if ((component.getHeight() >= height)
+ && (component.getWidth() >= width))
+ {
+ /*
+ * We will only enlarge the frame size. If the component has already
+ * been given at least what it is requesting, do not enlarge the
+ * frame size because the whole calculation is prone to inaccuracy.
+ */
+ return;
+ }
+ else
+ {
+ /*
+ * If there is no callPanel, it is unlikely that this CallDialog
+ * will be asked to ensureSize. Anyway, support the scenario just in
+ * case. In light of the absence of a callPanel to guide this
+ * CallDialog about the preferred size, we do not have much of a
+ * choice but to trust the method arguments.
+ */
+ if (callPanel != null)
+ {
+ /*
+ * If there is a callPanel, we are likely to get a much better
+ * estimation about the preferred size by asking the callPanel
+ * rather than by trusting the method arguments. For example,
+ * the visual Component displaying the video streaming from the
+ * local user/peer to the remote peer(s) will think that its
+ * preferred size is the one to base this Frame's size on but
+ * that may be misleading because the local video may not be
+ * displayed with its preferred size even if this Frame's size
+ * will accommodate it.
+ */
+ /*
+ * Just asking the callPanel about its preferredSize would've
+ * been terrificly great. Unfortunately, that is presently
+ * futile because the callPanel may have a preferredSize while
+ * we are still required to display visual Components displaying
+ * video in their non-scaled size. The same goes for any
+ * Container which is an ancestor of the specified component.
+ */
+ Container ancestor
+ = findClosestAncestorWithSetPreferredSize(component);
+
+ if (ancestor == null)
+ ancestor = callPanel;
+ /*
+ * If the ancestor has a forced preferredSize, its LayoutManager
+ * may be able to give a good enough estimation.
+ */
+ if (ancestor.isPreferredSizeSet())
+ {
+ LayoutManager ancestorLayout = ancestor.getLayout();
+
+ if (ancestorLayout != null)
+ {
+ Dimension preferredLayoutSize
+ = ancestorLayout.preferredLayoutSize(ancestor);
+
+ if (preferredLayoutSize != null)
+ {
+ component = ancestor;
+ width = preferredLayoutSize.width;
+ height = preferredLayoutSize.height;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * If the ancestor doesn't have a preferredSize forced, then
+ * we may think that it will calculate an appropriate
+ * preferredSize itself.
+ */
+ Dimension prefSize = ancestor.getPreferredSize();
+
+ if (prefSize != null)
+ {
+ component = ancestor;
+ width = prefSize.width;
+ height = prefSize.height;
+ }
+ }
+ }
+
+ /*
+ * If the component (which may be an ancestor of the Component
+ * specified as an argument to the ensureSize method at this point)
+ * has not been given a size, we will make a mistake if we try to
+ * use it for the purposes of determining how much this Frame is to
+ * be enlarged.
+ */
+ Dimension componentSize = component.getSize();
+
+ if ((componentSize.width < 1) || (componentSize.height < 1))
+ return;
+
+ Dimension frameSize = frame.getSize();
+ int newFrameWidth = frameSize.width + width - componentSize.width;
+ int newFrameHeight
+ = frameSize.height + height - componentSize.height;
+
+ // Respect the minimum size.
+ Dimension minSize = frame.getMinimumSize();
+
+ if (newFrameWidth < minSize.width)
+ newFrameWidth = minSize.width;
+ if (newFrameHeight < minSize.height)
+ newFrameHeight = minSize.height;
+
+ // Don't get bigger than the screen.
+ Rectangle screenBounds
+ = frame.getGraphicsConfiguration().getBounds();
+
+ if (newFrameWidth > screenBounds.width)
+ newFrameWidth = screenBounds.width;
+ if (newFrameHeight > screenBounds.height)
+ newFrameHeight = screenBounds.height;
+
+ /*
+ * If we're going to make too small a change, don't even bother.
+ * Besides, we don't want some weird recursive resizing.
+ * Additionally, do not reduce the Frame size.
+ */
+ boolean changeWidth = ((newFrameWidth - frameSize.width) > 1);
+ boolean changeHeight = ((newFrameHeight - frameSize.height) > 1);
+
+ if (changeWidth || changeHeight)
+ {
+ if (!changeWidth)
+ newFrameWidth = frameSize.width;
+ else if (!changeHeight)
+ newFrameHeight = frameSize.height;
+
+ /*
+ * The latest requirement with respect to the behavior upon
+ * resizing is to center the Frame.
+ */
+ int newFrameX
+ = screenBounds.x
+ + (screenBounds.width - newFrameWidth) / 2;
+ int newFrameY
+ = screenBounds.y
+ + (screenBounds.height - newFrameHeight) / 2;
+
+ // Do not let the top left go out of the screen.
+ if (newFrameX < screenBounds.x)
+ newFrameX = screenBounds.x;
+ if (newFrameY < screenBounds.y)
+ newFrameY = screenBounds.y;
+
+ frame.setBounds(
+ newFrameX, newFrameY,
+ newFrameWidth, newFrameHeight);
+
+ /*
+ * Make sure that the component which originally requested the
+ * update to the size of the frame realizes the change as soon
+ * as possible; otherwise, it may request yet another update.
+ */
+ if (frame.isDisplayable())
+ {
+ if (frame.isValid())
+ frame.doLayout();
+ else
+ frame.validate();
+ frame.repaint();
+ }
+ else
+ frame.doLayout();
+ }
+ }
+ }
+
+ /**
+ * Returns the frame of the call window.
+ *
+ * @return the frame of the call window
+ */
+ public JFrame getFrame()
+ {
+ return this;
+ }
+
+ /**
+ * Overrides getMinimumSize and checks the minimum size that
+ * is needed to display buttons and use it for minimum size if
+ * needed.
+ * @return minimum size.
+ */
+ @Override
+ public Dimension getMinimumSize()
+ {
+ Dimension minSize = super.getMinimumSize();
+
+ if(callPanel != null)
+ {
+ int minButtonWidth = callPanel.getMinimumButtonWidth();
+
+ if(minButtonWidth > minSize.getWidth())
+ minSize = new Dimension(minButtonWidth, 300);
+ }
+
+ return minSize;
+ }
+
+ /**
+ * Indicates if the given <tt>callPanel</tt> is currently visible.
+ *
+ * @param callPanel the <tt>CallPanel</tt>, for which we verify
+ * @return <tt>true</tt> if the given call container is visible in this
+ * call window, otherwise - <tt>false</tt>
+ */
+ public boolean isCallVisible(CallPanel callPanel)
+ {
+ return this.callPanel.equals(callPanel) ? isVisible() : false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isFullScreen()
+ {
+ return isFullScreen(getFrame());
+ }
+
+ /**
+ * Determines whether a specific <tt>Window</tt> is displayed in full-screen
+ * mode.
+ *
+ * @param window the <tt>Window</tt> to be checked whether it is displayed
+ * in full-screen mode
+ * @return <tt>true</tt> if the specified <tt>window</tt> is displayed in
+ * full-screen mode; otherwise, <tt>false</tt>
+ */
+ public static boolean isFullScreen(Window window)
+ {
+ GraphicsConfiguration graphicsConfiguration
+ = window.getGraphicsConfiguration();
+
+ if (graphicsConfiguration != null)
+ {
+ GraphicsDevice device = graphicsConfiguration.getDevice();
+
+ if (device != null)
+ return window.equals(device.getFullScreenWindow());
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setFullScreen(boolean fullScreen)
+ {
+ GraphicsConfiguration graphicsConfiguration
+ = getGraphicsConfiguration();
+
+ if (graphicsConfiguration != null)
+ {
+ GraphicsDevice device = graphicsConfiguration.getDevice();
+
+ if (device != null)
+ {
+ boolean thisIsFullScreen = equals(device.getFullScreenWindow());
+ boolean firePropertyChange = false;
+ boolean setVisible = isVisible();
+
+ try
+ {
+ if (fullScreen)
+ {
+ if (!thisIsFullScreen)
+ {
+ /*
+ * XXX The setUndecorated method will only work if
+ * this Window is not displayable.
+ */
+ windowDispose();
+ setUndecorated(true);
+
+ device.setFullScreenWindow(this);
+ firePropertyChange = true;
+ }
+ }
+ else if (thisIsFullScreen)
+ {
+ /*
+ * XXX The setUndecorated method will only work if this
+ * Window is not displayable.
+ */
+ windowDispose();
+ setUndecorated(false);
+
+ device.setFullScreenWindow(null);
+ firePropertyChange = true;
+ }
+
+ if (firePropertyChange)
+ {
+ if (fullScreen)
+ {
+ addWindowStateListener(windowStateListener);
+
+ /*
+ * If full-screen mode, a black background is the
+ * most common.
+ */
+ getContentPane().setBackground(Color.BLACK);
+ }
+ else
+ {
+ removeWindowStateListener(windowStateListener);
+
+ /*
+ * In windowed mode, a system-defined background is
+ * the most common.
+ */
+ getContentPane().setBackground(null);
+ }
+
+ firePropertyChange(
+ PROP_FULL_SCREEN,
+ thisIsFullScreen,
+ fullScreen);
+ }
+ }
+ finally
+ {
+ /*
+ * Regardless of whether this Window successfully entered or
+ * exited full-screen mode, make sure that remains visible.
+ */
+ if (setVisible)
+ setVisible(true);
+ }
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java b/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java
index 095b3dd..d1c0a42 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallHistoryButton.java
@@ -130,6 +130,14 @@ public class CallHistoryButton
}
/**
+ * Does nothing because this class causes the clearing.
+ */
+ @Override
+ public void notificationCleared(UINotification notification)
+ {
+ }
+
+ /**
* Sets the history view.
*/
private void setHistoryView()
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallInfoFrame.java b/src/net/java/sip/communicator/impl/gui/main/call/CallInfoFrame.java
index ad8f6fd..872e861 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallInfoFrame.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallInfoFrame.java
@@ -34,6 +34,7 @@ import net.java.sip.communicator.util.*;
import org.ice4j.ice.*;
import org.jitsi.service.neomedia.*;
+import org.jitsi.service.neomedia.stats.*;
import org.jitsi.service.resources.*;
import org.jitsi.util.*;
@@ -469,7 +470,7 @@ public class CallInfoFrame
StringBuffer stringBuffer,
MediaType mediaType)
{
- MediaStreamStats mediaStreamStats
+ MediaStreamStats2 mediaStreamStats
= mediaStream.getMediaStreamStats();
if(mediaStreamStats == null)
@@ -624,18 +625,19 @@ public class CallInfoFrame
resources.getI18NString(
"service.gui.callinfo.BANDWITH"),
"&darr; "
- + (int) mediaStreamStats.getDownloadRateKiloBitPerSec()
+ + (int) mediaStreamStats.getReceiveStats().getBitrate()/1024
+ " Kbps "
+ " &uarr; "
- + (int) mediaStreamStats.getUploadRateKiloBitPerSec()
+ + (int) mediaStreamStats.getSendStats().getBitrate()/1024
+ " Kbps"));
stringBuffer.append(
getLineString(
resources.getI18NString("service.gui.callinfo.LOSS_RATE"),
- "&darr; " + (int) mediaStreamStats.getDownloadPercentLoss()
+ "&darr;"
+ + (int) (mediaStreamStats.getReceiveStats().getLossRate() * 100)
+ "% &uarr; "
- + (int) mediaStreamStats.getUploadPercentLoss()
+ + (int) (mediaStreamStats.getSendStats().getLossRate() * 100)
+ "%"));
stringBuffer.append(
getLineString(
@@ -668,21 +670,23 @@ public class CallInfoFrame
+ mediaStreamStats.getPacketQueueCountPackets() + "/"
+ mediaStreamStats.getPacketQueueSize() + " packets"));
- long rttMs = mediaStreamStats.getRttMs();
- if(rttMs != -1)
+ long sendRttMs = mediaStreamStats.getSendStats().getRtt();
+ long recvRttMs = mediaStreamStats.getReceiveStats().getRtt();
+ if(recvRttMs != -1 || sendRttMs != -1)
{
stringBuffer.append(
getLineString(resources.getI18NString(
"service.gui.callinfo.RTT"),
- rttMs + " ms"));
+ (recvRttMs != -1 ? "&darr; " + recvRttMs + " ms" : "") +
+ (sendRttMs != -1 ? "&uarr; " + sendRttMs + " ms" : "")));
}
stringBuffer.append(
getLineString(resources.getI18NString(
"service.gui.callinfo.JITTER"),
- "&darr; " + (int) mediaStreamStats.getDownloadJitterMs()
+ "&darr; " + (int) mediaStreamStats.getReceiveStats().getJitter()
+ " ms &uarr; "
- + (int) mediaStreamStats.getUploadJitterMs() + " ms"));
+ + (int) mediaStreamStats.getSendStats().getJitter() + " ms"));
}
/**
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java b/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java
index 54f48ee..6235e1c 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallTransferHandler.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,276 +15,276 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call;
-
-import java.awt.datatransfer.*;
-import java.awt.im.*;
-import java.io.*;
-import java.util.*;
-
-import javax.swing.*;
-
-import org.jitsi.service.resources.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.impl.gui.main.contactlist.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * A <tt>TransferHandler</tt> that handles dropping of <tt>UIContact</tt>s or
- * <tt>String</tt> addresses on a <tt>CallConference</tt>. Dropping such data on
- * the <tt>CallDialog</tt> will turn a one-to-one <tt>Call</tt> into a telephony
- * conference.
- *
- * @author Yana Stamcheva
- */
-public class CallTransferHandler
- extends ExtendedTransferHandler
-{
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- /**
- * The data flavor used when transferring <tt>UIContact</tt>s.
- */
- protected static final DataFlavor uiContactDataFlavor
- = new DataFlavor(UIContact.class, "UIContact");
-
- /**
- * The logger.
- */
- private static final Logger logger
- = Logger.getLogger(CallTransferHandler.class);
-
- /**
- * The <tt>CallConference</tt> into which the dropped callees are to be
- * invited.
- */
- private final CallConference callConference;
-
- /**
- * Initializes a new <tt>CallTransferHandler</tt> instance which is to
- * invite dropped callees to a telephony conference specified by a specific
- * <tt>Call</tt> which participates in it.
- *
- * @param call the <tt>Call</tt> which specifies the telephony conference to
- * which dropped callees are to be invited
- */
- public CallTransferHandler(Call call)
- {
- this(call.getConference());
- }
-
- /**
- * Initializes a new <tt>CallTransferHandler</tt> instance which is to
- * invite dropped callees to a specific <tt>CallConference</tt>.
- *
- * @param callConference the <tt>CallConference</tt> to which dropped
- * callees are to be invited
- */
- public CallTransferHandler(CallConference callConference)
- {
- this.callConference = callConference;
- }
-
- /**
- * Indicates whether a component will accept an import of the given
- * set of data flavors prior to actually attempting to import it. We return
- * <tt>true</tt> to indicate that the transfer with at least one of the
- * given flavors would work and <tt>false</tt> to reject the transfer.
- * <p>
- * @param comp component
- * @param flavor the data formats available
- * @return true if the data can be inserted into the component, false
- * otherwise
- * @throws NullPointerException if <code>support</code> is {@code null}
- */
- @Override
- public boolean canImport(JComponent comp, DataFlavor[] flavor)
- {
- for (DataFlavor f : flavor)
- {
- if (f.equals(DataFlavor.stringFlavor)
- || f.equals(uiContactDataFlavor))
- {
- return (comp instanceof JPanel);
- }
- }
- return false;
- }
-
- /**
- * Handles transfers to the chat panel from the clip board or a
- * DND drop operation. The <tt>Transferable</tt> parameter contains the
- * data that needs to be imported.
- * <p>
- * @param comp the component to receive the transfer;
- * @param t the data to import
- * @return true if the data was inserted into the component and false
- * otherwise
- */
- @Override
- public boolean importData(JComponent comp, Transferable t)
- {
- String callee = null;
- ProtocolProviderService provider = null;
-
- if (t.isDataFlavorSupported(uiContactDataFlavor))
- {
- Object o = null;
-
- try
- {
- o = t.getTransferData(uiContactDataFlavor);
- }
- catch (UnsupportedFlavorException e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to drop meta contact.", e);
- }
- catch (IOException e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to drop meta contact.", e);
- }
-
- if (o instanceof ContactNode)
- {
- UIContact uiContact = ((ContactNode) o).getContactDescriptor();
- Iterator<UIContactDetail> contactDetails
- = uiContact
- .getContactDetailsForOperationSet(
- OperationSetBasicTelephony.class)
- .iterator();
-
- while (contactDetails.hasNext())
- {
- UIContactDetail detail = contactDetails.next();
- ProtocolProviderService detailProvider
- = detail.getPreferredProtocolProvider(
- OperationSetBasicTelephony.class);
-
- if (detailProvider != null)
- {
- /*
- * Currently for videobridge conferences we only support
- * adding contacts via the account with the videobridge
- */
- if (callConference.isJitsiVideobridge())
- {
- for (Call call : callConference.getCalls())
- {
- if (detailProvider == call.getProtocolProvider())
- {
- callee = detail.getAddress();
- provider = detailProvider;
- break;
- }
- }
- }
- else
- {
- callee = detail.getAddress();
- provider = detailProvider;
- break;
- }
- }
- }
-
- if (callee == null)
- {
- /*
- * It turns out that the error message to be reported would
- * like to display information about the account which could
- * not add the dropped callee to the telephony conference.
- * Unfortunately, a telephony conference may have multiple
- * accounts involved. Anyway, choose the first account
- * involved in the telephony conference.
- */
- ProtocolProviderService callProvider
- = callConference.getCalls().get(0)
- .getProtocolProvider();
-
- ResourceManagementService resources
- = GuiActivator.getResources();
- AccountID accountID = callProvider.getAccountID();
-
- new ErrorDialog(null,
- resources.getI18NString("service.gui.ERROR"),
- resources.getI18NString(
- "service.gui.CALL_NOT_SUPPORTING_PARTICIPANT",
- new String[]
- {
- accountID.getService(),
- accountID.getUserID(),
- uiContact.getDisplayName()
- }))
- .showDialog();
- }
- }
- }
- else if (t.isDataFlavorSupported(DataFlavor.stringFlavor))
- {
- InputContext inputContext = comp.getInputContext();
-
- if (inputContext != null)
- inputContext.endComposition();
-
- try
- {
- BufferedReader reader
- = new BufferedReader(
- DataFlavor.stringFlavor.getReaderForText(t));
-
- try
- {
- String line;
- StringBuilder calleeBuilder = new StringBuilder();
-
- while ((line = reader.readLine()) != null)
- calleeBuilder.append(line);
-
- callee = calleeBuilder.toString();
- /*
- * The value of the local variable provider will be null
- * because we have a String only and hence we have no
- * associated ProtocolProviderService.
- * CallManager.inviteToConferenceCall will accept it.
- */
- }
- finally
- {
- reader.close();
- }
- }
- catch (UnsupportedFlavorException e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to drop string.", e);
- }
- catch (IOException e)
- {
- if (logger.isDebugEnabled())
- logger.debug("Failed to drop string.", e);
- }
- }
-
- if (callee == null)
- return false;
- else
- {
- Map<ProtocolProviderService, List<String>> callees
- = new HashMap<ProtocolProviderService, List<String>>();
-
- callees.put(provider, Arrays.asList(callee));
- CallManager.inviteToConferenceCall(callees, callConference);
-
- return true;
- }
- }
-}
+package net.java.sip.communicator.impl.gui.main.call;
+
+import java.awt.datatransfer.*;
+import java.awt.im.*;
+import java.io.*;
+import java.util.*;
+
+import javax.swing.*;
+
+import org.jitsi.service.resources.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * A <tt>TransferHandler</tt> that handles dropping of <tt>UIContact</tt>s or
+ * <tt>String</tt> addresses on a <tt>CallConference</tt>. Dropping such data on
+ * the <tt>CallDialog</tt> will turn a one-to-one <tt>Call</tt> into a telephony
+ * conference.
+ *
+ * @author Yana Stamcheva
+ */
+public class CallTransferHandler
+ extends ExtendedTransferHandler
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * The data flavor used when transferring <tt>UIContact</tt>s.
+ */
+ protected static final DataFlavor uiContactDataFlavor
+ = new DataFlavor(UIContact.class, "UIContact");
+
+ /**
+ * The logger.
+ */
+ private static final Logger logger
+ = Logger.getLogger(CallTransferHandler.class);
+
+ /**
+ * The <tt>CallConference</tt> into which the dropped callees are to be
+ * invited.
+ */
+ private final CallConference callConference;
+
+ /**
+ * Initializes a new <tt>CallTransferHandler</tt> instance which is to
+ * invite dropped callees to a telephony conference specified by a specific
+ * <tt>Call</tt> which participates in it.
+ *
+ * @param call the <tt>Call</tt> which specifies the telephony conference to
+ * which dropped callees are to be invited
+ */
+ public CallTransferHandler(Call call)
+ {
+ this(call.getConference());
+ }
+
+ /**
+ * Initializes a new <tt>CallTransferHandler</tt> instance which is to
+ * invite dropped callees to a specific <tt>CallConference</tt>.
+ *
+ * @param callConference the <tt>CallConference</tt> to which dropped
+ * callees are to be invited
+ */
+ public CallTransferHandler(CallConference callConference)
+ {
+ this.callConference = callConference;
+ }
+
+ /**
+ * Indicates whether a component will accept an import of the given
+ * set of data flavors prior to actually attempting to import it. We return
+ * <tt>true</tt> to indicate that the transfer with at least one of the
+ * given flavors would work and <tt>false</tt> to reject the transfer.
+ * <p>
+ * @param comp component
+ * @param flavor the data formats available
+ * @return true if the data can be inserted into the component, false
+ * otherwise
+ * @throws NullPointerException if <code>support</code> is {@code null}
+ */
+ @Override
+ public boolean canImport(JComponent comp, DataFlavor[] flavor)
+ {
+ for (DataFlavor f : flavor)
+ {
+ if (f.equals(DataFlavor.stringFlavor)
+ || f.equals(uiContactDataFlavor))
+ {
+ return (comp instanceof JPanel);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Handles transfers to the chat panel from the clip board or a
+ * DND drop operation. The <tt>Transferable</tt> parameter contains the
+ * data that needs to be imported.
+ * <p>
+ * @param comp the component to receive the transfer;
+ * @param t the data to import
+ * @return true if the data was inserted into the component and false
+ * otherwise
+ */
+ @Override
+ public boolean importData(JComponent comp, Transferable t)
+ {
+ String callee = null;
+ ProtocolProviderService provider = null;
+
+ if (t.isDataFlavorSupported(uiContactDataFlavor))
+ {
+ Object o = null;
+
+ try
+ {
+ o = t.getTransferData(uiContactDataFlavor);
+ }
+ catch (UnsupportedFlavorException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop meta contact.", e);
+ }
+ catch (IOException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop meta contact.", e);
+ }
+
+ if (o instanceof ContactNode)
+ {
+ UIContact uiContact = ((ContactNode) o).getContactDescriptor();
+ Iterator<UIContactDetail> contactDetails
+ = uiContact
+ .getContactDetailsForOperationSet(
+ OperationSetBasicTelephony.class)
+ .iterator();
+
+ while (contactDetails.hasNext())
+ {
+ UIContactDetail detail = contactDetails.next();
+ ProtocolProviderService detailProvider
+ = detail.getPreferredProtocolProvider(
+ OperationSetBasicTelephony.class);
+
+ if (detailProvider != null)
+ {
+ /*
+ * Currently for videobridge conferences we only support
+ * adding contacts via the account with the videobridge
+ */
+ if (callConference.isJitsiVideobridge())
+ {
+ for (Call call : callConference.getCalls())
+ {
+ if (detailProvider == call.getProtocolProvider())
+ {
+ callee = detail.getAddress();
+ provider = detailProvider;
+ break;
+ }
+ }
+ }
+ else
+ {
+ callee = detail.getAddress();
+ provider = detailProvider;
+ break;
+ }
+ }
+ }
+
+ if (callee == null)
+ {
+ /*
+ * It turns out that the error message to be reported would
+ * like to display information about the account which could
+ * not add the dropped callee to the telephony conference.
+ * Unfortunately, a telephony conference may have multiple
+ * accounts involved. Anyway, choose the first account
+ * involved in the telephony conference.
+ */
+ ProtocolProviderService callProvider
+ = callConference.getCalls().get(0)
+ .getProtocolProvider();
+
+ ResourceManagementService resources
+ = GuiActivator.getResources();
+ AccountID accountID = callProvider.getAccountID();
+
+ new ErrorDialog(null,
+ resources.getI18NString("service.gui.ERROR"),
+ resources.getI18NString(
+ "service.gui.CALL_NOT_SUPPORTING_PARTICIPANT",
+ new String[]
+ {
+ accountID.getService(),
+ accountID.getUserID(),
+ uiContact.getDisplayName()
+ }))
+ .showDialog();
+ }
+ }
+ }
+ else if (t.isDataFlavorSupported(DataFlavor.stringFlavor))
+ {
+ InputContext inputContext = comp.getInputContext();
+
+ if (inputContext != null)
+ inputContext.endComposition();
+
+ try
+ {
+ BufferedReader reader
+ = new BufferedReader(
+ DataFlavor.stringFlavor.getReaderForText(t));
+
+ try
+ {
+ String line;
+ StringBuilder calleeBuilder = new StringBuilder();
+
+ while ((line = reader.readLine()) != null)
+ calleeBuilder.append(line);
+
+ callee = calleeBuilder.toString();
+ /*
+ * The value of the local variable provider will be null
+ * because we have a String only and hence we have no
+ * associated ProtocolProviderService.
+ * CallManager.inviteToConferenceCall will accept it.
+ */
+ }
+ finally
+ {
+ reader.close();
+ }
+ }
+ catch (UnsupportedFlavorException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop string.", e);
+ }
+ catch (IOException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Failed to drop string.", e);
+ }
+ }
+
+ if (callee == null)
+ return false;
+ else
+ {
+ Map<ProtocolProviderService, List<String>> callees
+ = new HashMap<ProtocolProviderService, List<String>>();
+
+ callees.put(provider, Arrays.asList(callee));
+ CallManager.inviteToConferenceCall(callees, callConference);
+
+ return true;
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/FullScreenLayout.java b/src/net/java/sip/communicator/impl/gui/main/call/FullScreenLayout.java
index 3e823ba..c56a415 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/FullScreenLayout.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/FullScreenLayout.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,176 +15,176 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call;
-
-import java.awt.*;
-import java.util.*;
-import java.util.List;
-
-/**
- * Implements a <tt>LayoutManager</tt> for the full-screen <tt>Call</tt>
- * display.
- *
- * @author Lyubomir Marinov
- */
-public class FullScreenLayout
- implements LayoutManager
-{
- public static final String CENTER = "CENTER";
-
- public static final String SOUTH = "SOUTH";
-
- private Component center;
-
- /**
- * The indicator which determines whether {@link #south} is to be laid out
- * on top of {@link #center} i.e. as an overlay.
- */
- private final boolean overlay;
-
- private Component south;
-
- /**
- * The vertical gap between the center and the south components.
- */
- private int yGap = 0;
-
- /**
- * Initializes a new <tt>FullScreenLayout</tt> instance.
- *
- * @param overlay <tt>true</tt> to lay out the <tt>Component</tt> at
- * {@link #SOUTH} on top of the <tt>Component</tt> at {@link #CENTER} i.e as
- * an overlay; otherwise, <tt>false</tt>
- * @oaram yGap the gap betwen the center and the south component
- */
- public FullScreenLayout(boolean overlay, int yGap)
- {
- this.overlay = overlay;
- this.yGap = yGap;
- }
-
- /**
- * Adds the given component to this layout.
- *
- * @param name the name of the constraint (CENTER or SOUTH)
- * @param comp the component to add to this layout
- */
- public void addLayoutComponent(String name, Component comp)
- {
- if (CENTER.equals(name))
- center = comp;
- else if (SOUTH.equals(name))
- south = comp;
- }
-
- /**
- * Gets a <tt>List</tt> of the <tt>Component</tt>s to be laid out by this
- * <tt>LayoutManager</tt> i.e. the non-<tt>null</tt> of {@link #center}
- * and {@link #south}.
- *
- * @return a <tt>List</tt> of the <tt>Component</tt>s to be laid out by this
- * <tt>LayoutManager</tt>
- */
- private List<Component> getLayoutComponents()
- {
- List<Component> layoutComponents = new ArrayList<Component>(2);
-
- if (center != null)
- layoutComponents.add(center);
- if (south != null)
- layoutComponents.add(south);
- return layoutComponents;
- }
-
- /**
- * Lays out the components added in the given parent container
- *
- * @param parent the parent container to lay out
- */
- public void layoutContainer(Container parent)
- {
- int southWidth;
- int southHeight;
-
- if (south == null)
- {
- southWidth = southHeight = 0;
- }
- else
- {
- Dimension southSize = south.getPreferredSize();
-
- southWidth = southSize.width;
- southHeight = southSize.height;
- }
-
- Dimension parentSize = parent.getSize();
-
- if (center != null)
- {
- /*
- * If the Component at the SOUTH is not to be shown as an overlay,
- * make room for it bellow the Component at the CENTER.
- */
- int yOffset = overlay ? 0 : southHeight + yGap;
-
- center.setBounds(
- 0,
- 0,
- parentSize.width,
- parentSize.height - yOffset);
- }
- if (south != null)
- {
- south.setBounds(
- (parentSize.width - southWidth) / 2,
- parentSize.height - southHeight,
- southWidth,
- southHeight);
- }
- }
-
- public Dimension minimumLayoutSize(Container parent)
- {
- List<Component> components = getLayoutComponents();
- Dimension size = new Dimension(0, 0);
-
- for (Component component : components)
- {
- Dimension componentSize = component.getMinimumSize();
-
- size.width = Math.max(size.width, componentSize.width);
- if (overlay)
- size.height = Math.max(size.height, componentSize.height);
- else
- size.height += componentSize.height;
- }
- return size;
- }
-
- public Dimension preferredLayoutSize(Container parent)
- {
- List<Component> components = getLayoutComponents();
- Dimension size = new Dimension(0, 0);
-
- for (Component component : components)
- {
- Dimension componentSize = component.getPreferredSize();
-
- size.width = Math.max(size.width, componentSize.width);
- if (overlay)
- size.height = Math.max(size.height, componentSize.height);
- else
- size.height += componentSize.height;
- }
- return size;
- }
-
- public void removeLayoutComponent(Component comp)
- {
- if (comp.equals(center))
- center = null;
- else if (comp.equals(south))
- south = null;
- }
-}
+package net.java.sip.communicator.impl.gui.main.call;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+/**
+ * Implements a <tt>LayoutManager</tt> for the full-screen <tt>Call</tt>
+ * display.
+ *
+ * @author Lyubomir Marinov
+ */
+public class FullScreenLayout
+ implements LayoutManager
+{
+ public static final String CENTER = "CENTER";
+
+ public static final String SOUTH = "SOUTH";
+
+ private Component center;
+
+ /**
+ * The indicator which determines whether {@link #south} is to be laid out
+ * on top of {@link #center} i.e. as an overlay.
+ */
+ private final boolean overlay;
+
+ private Component south;
+
+ /**
+ * The vertical gap between the center and the south components.
+ */
+ private int yGap = 0;
+
+ /**
+ * Initializes a new <tt>FullScreenLayout</tt> instance.
+ *
+ * @param overlay <tt>true</tt> to lay out the <tt>Component</tt> at
+ * {@link #SOUTH} on top of the <tt>Component</tt> at {@link #CENTER} i.e as
+ * an overlay; otherwise, <tt>false</tt>
+ * @oaram yGap the gap betwen the center and the south component
+ */
+ public FullScreenLayout(boolean overlay, int yGap)
+ {
+ this.overlay = overlay;
+ this.yGap = yGap;
+ }
+
+ /**
+ * Adds the given component to this layout.
+ *
+ * @param name the name of the constraint (CENTER or SOUTH)
+ * @param comp the component to add to this layout
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ if (CENTER.equals(name))
+ center = comp;
+ else if (SOUTH.equals(name))
+ south = comp;
+ }
+
+ /**
+ * Gets a <tt>List</tt> of the <tt>Component</tt>s to be laid out by this
+ * <tt>LayoutManager</tt> i.e. the non-<tt>null</tt> of {@link #center}
+ * and {@link #south}.
+ *
+ * @return a <tt>List</tt> of the <tt>Component</tt>s to be laid out by this
+ * <tt>LayoutManager</tt>
+ */
+ private List<Component> getLayoutComponents()
+ {
+ List<Component> layoutComponents = new ArrayList<Component>(2);
+
+ if (center != null)
+ layoutComponents.add(center);
+ if (south != null)
+ layoutComponents.add(south);
+ return layoutComponents;
+ }
+
+ /**
+ * Lays out the components added in the given parent container
+ *
+ * @param parent the parent container to lay out
+ */
+ public void layoutContainer(Container parent)
+ {
+ int southWidth;
+ int southHeight;
+
+ if (south == null)
+ {
+ southWidth = southHeight = 0;
+ }
+ else
+ {
+ Dimension southSize = south.getPreferredSize();
+
+ southWidth = southSize.width;
+ southHeight = southSize.height;
+ }
+
+ Dimension parentSize = parent.getSize();
+
+ if (center != null)
+ {
+ /*
+ * If the Component at the SOUTH is not to be shown as an overlay,
+ * make room for it bellow the Component at the CENTER.
+ */
+ int yOffset = overlay ? 0 : southHeight + yGap;
+
+ center.setBounds(
+ 0,
+ 0,
+ parentSize.width,
+ parentSize.height - yOffset);
+ }
+ if (south != null)
+ {
+ south.setBounds(
+ (parentSize.width - southWidth) / 2,
+ parentSize.height - southHeight,
+ southWidth,
+ southHeight);
+ }
+ }
+
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ List<Component> components = getLayoutComponents();
+ Dimension size = new Dimension(0, 0);
+
+ for (Component component : components)
+ {
+ Dimension componentSize = component.getMinimumSize();
+
+ size.width = Math.max(size.width, componentSize.width);
+ if (overlay)
+ size.height = Math.max(size.height, componentSize.height);
+ else
+ size.height += componentSize.height;
+ }
+ return size;
+ }
+
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ List<Component> components = getLayoutComponents();
+ Dimension size = new Dimension(0, 0);
+
+ for (Component component : components)
+ {
+ Dimension componentSize = component.getPreferredSize();
+
+ size.width = Math.max(size.width, componentSize.width);
+ if (overlay)
+ size.height = Math.max(size.height, componentSize.height);
+ else
+ size.height += componentSize.height;
+ }
+ return size;
+ }
+
+ public void removeLayoutComponent(Component comp)
+ {
+ if (comp.equals(center))
+ center = null;
+ else if (comp.equals(south))
+ south = null;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/PreCallDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/PreCallDialog.java
index 76c4139..0d6206a 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/PreCallDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/PreCallDialog.java
@@ -256,8 +256,12 @@ public abstract class PreCallDialog
receivedCallWindow.setAlwaysOnTop(true);
+ // Register the JFrame so the dialog window is able to get dragged
+ // across the screen
+ ComponentMover.registerComponent(receivedCallWindow);
+
// prevents dialog window to get unwanted key events and when going
- // on top on linux, it steals focus and if we are accidently
+ // on top on linux, it steals focus and if we are accidentally
// writing something and pressing enter a call get answered
receivedCallWindow.setFocusableWindowState(false);
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/SecurityPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/SecurityPanel.java
index 2e2ed5b..b49e0a3 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/SecurityPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/SecurityPanel.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,117 +15,117 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call;
-
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.service.protocol.event.*;
-import net.java.sip.communicator.util.skin.*;
-
-import org.jitsi.service.neomedia.*;
-
-/**
- * Base class for security panels that show encryption specific UI controls.
- *
- * @author Ingo Bauersachs
- */
-public abstract class SecurityPanel<T extends SrtpControl>
- extends FadeInBalloonPanel
- implements Skinnable
-{
- /**
- * The currently used security control.
- */
- protected T securityControl;
-
- /**
- * Create security panel using the security control.
- * @param securityControl
- */
- public SecurityPanel(T securityControl)
- {
- this.securityControl = securityControl;
- }
-
- /**
- * The currently used security control.
- * @return
- */
- public T getSecurityControl()
- {
- return securityControl;
- }
-
- /**
- * The currently used security control.
- * @return
- */
- public void setSecurityControl(T securityControl)
- {
- this.securityControl = securityControl;
- }
-
- /**
- * Creates the security panel depending on the concrete implementation of
- * the passed security controller.
- *
- * @param srtpControl the security controller that provides the information
- * to be shown on the UI
- * @return An instance of a {@link SecurityPanel} for the security
- * controller or an {@link TransparentPanel} if the controller is
- * unknown or does not have any controls to show.
- */
- public static SecurityPanel<?> create(
- SwingCallPeerRenderer peerRenderer,
- CallPeer callPeer,
- SrtpControl srtpControl)
- {
- if(srtpControl instanceof ZrtpControl)
- {
- return
- new ZrtpSecurityPanel(
- peerRenderer,
- callPeer,
- (ZrtpControl) srtpControl);
- }
- else
- {
- return
- new SecurityPanel<SrtpControl>(srtpControl)
- {
- public void loadSkin() {}
-
- @Override
- public void securityOn(CallPeerSecurityOnEvent evt) {}
-
- @Override
- public void securityOff(CallPeerSecurityOffEvent evt) {}
-
- @Override
- public void securityTimeout(
- CallPeerSecurityTimeoutEvent evt) {}
- };
- }
- }
-
- /**
- * Indicates that the security is turned on.
- *
- * @param evt details about the event that caused this message.
- */
- public abstract void securityOn(CallPeerSecurityOnEvent evt);
-
- /**
- * Indicates that the security is turned off.
- *
- * @param evt details about the event that caused this message.
- */
- public abstract void securityOff(CallPeerSecurityOffEvent evt);
-
- /**
- * Indicates that the security is timeouted, is not supported by the
- * other end.
- * @param evt Details about the event that caused this message.
- */
- public abstract void securityTimeout(CallPeerSecurityTimeoutEvent evt);
-}
+package net.java.sip.communicator.impl.gui.main.call;
+
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.skin.*;
+
+import org.jitsi.service.neomedia.*;
+
+/**
+ * Base class for security panels that show encryption specific UI controls.
+ *
+ * @author Ingo Bauersachs
+ */
+public abstract class SecurityPanel<T extends SrtpControl>
+ extends FadeInBalloonPanel
+ implements Skinnable
+{
+ /**
+ * The currently used security control.
+ */
+ protected T securityControl;
+
+ /**
+ * Create security panel using the security control.
+ * @param securityControl
+ */
+ public SecurityPanel(T securityControl)
+ {
+ this.securityControl = securityControl;
+ }
+
+ /**
+ * The currently used security control.
+ * @return
+ */
+ public T getSecurityControl()
+ {
+ return securityControl;
+ }
+
+ /**
+ * The currently used security control.
+ * @return
+ */
+ public void setSecurityControl(T securityControl)
+ {
+ this.securityControl = securityControl;
+ }
+
+ /**
+ * Creates the security panel depending on the concrete implementation of
+ * the passed security controller.
+ *
+ * @param srtpControl the security controller that provides the information
+ * to be shown on the UI
+ * @return An instance of a {@link SecurityPanel} for the security
+ * controller or an {@link TransparentPanel} if the controller is
+ * unknown or does not have any controls to show.
+ */
+ public static SecurityPanel<?> create(
+ SwingCallPeerRenderer peerRenderer,
+ CallPeer callPeer,
+ SrtpControl srtpControl)
+ {
+ if(srtpControl instanceof ZrtpControl)
+ {
+ return
+ new ZrtpSecurityPanel(
+ peerRenderer,
+ callPeer,
+ (ZrtpControl) srtpControl);
+ }
+ else
+ {
+ return
+ new SecurityPanel<SrtpControl>(srtpControl)
+ {
+ public void loadSkin() {}
+
+ @Override
+ public void securityOn(CallPeerSecurityOnEvent evt) {}
+
+ @Override
+ public void securityOff(CallPeerSecurityOffEvent evt) {}
+
+ @Override
+ public void securityTimeout(
+ CallPeerSecurityTimeoutEvent evt) {}
+ };
+ }
+ }
+
+ /**
+ * Indicates that the security is turned on.
+ *
+ * @param evt details about the event that caused this message.
+ */
+ public abstract void securityOn(CallPeerSecurityOnEvent evt);
+
+ /**
+ * Indicates that the security is turned off.
+ *
+ * @param evt details about the event that caused this message.
+ */
+ public abstract void securityOff(CallPeerSecurityOffEvent evt);
+
+ /**
+ * Indicates that the security is timeouted, is not supported by the
+ * other end.
+ * @param evt Details about the event that caused this message.
+ */
+ public abstract void securityTimeout(CallPeerSecurityTimeoutEvent evt);
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/TransferCallDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/TransferCallDialog.java
index f2f2bd2..982738b 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/TransferCallDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/TransferCallDialog.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,140 +15,140 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package net.java.sip.communicator.impl.gui.main.call;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.util.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
-import net.java.sip.communicator.impl.gui.utils.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.protocol.*;
-
-/**
- * Represents a <tt>Dialog</tt> which allows specifying the target contact
- * address of a transfer-call operation.
- *
- * @author Yana Stamcheva
- */
-public class TransferCallDialog
- extends OneChoiceInviteDialog
-{
- /**
- * The peer to transfer.
- */
- private final CallPeer transferPeer;
-
- /**
- * Creates a <tt>TransferCallDialog</tt> by specifying the peer to transfer
- * @param peer the peer to transfer
- */
- public TransferCallDialog(final CallPeer peer)
- {
- super(GuiActivator.getResources()
- .getI18NString("service.gui.TRANSFER_CALL_TITLE"));
-
- this.transferPeer = peer;
-
- this.initContactListData(peer.getProtocolProvider());
-
- this.setInfoText(GuiActivator.getResources()
- .getI18NString("service.gui.TRANSFER_CALL_MSG"));
- this.setOkButtonText(GuiActivator.getResources()
- .getI18NString("service.gui.TRANSFER"));
-
- this.setMinimumSize(new Dimension(300, 300));
-
- addOkButtonListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- UIContact uiContact = getSelectedContact();
-
- if (uiContact != null)
- {
- transferToContact(uiContact);
- }
-
- setVisible(false);
- dispose();
- }
- });
- addCancelButtonListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- setVisible(false);
- dispose();
- }
- });
- }
-
- /**
- * Initializes contact list sources.
- */
- private void initContactSources()
- {
- DemuxContactSourceService demuxCSService
- = GuiActivator.getDemuxContactSourceService();
-
- // If the DemuxContactSourceService isn't registered we use the default
- // contact source set.
- if (demuxCSService == null)
- return;
-
- Iterator<UIContactSource> sourcesIter
- = new ArrayList<UIContactSource>(
- contactList.getContactSources()).iterator();
-
- contactList.removeAllContactSources();
-
- while (sourcesIter.hasNext())
- {
- ContactSourceService contactSource
- = sourcesIter.next().getContactSourceService();
-
- contactList.addContactSource(
- demuxCSService.createDemuxContactSource(contactSource));
- }
- }
-
- /**
- * Initializes the left contact list with the contacts that could be added
- * to the current chat session.
- *
- * @param protocolProvider the protocol provider from which to initialize
- * the contact list data
- */
- private void initContactListData(ProtocolProviderService protocolProvider)
- {
- initContactSources();
-
- contactList.addContactSource(
- new ProtocolContactSourceServiceImpl(
- protocolProvider, OperationSetBasicTelephony.class));
- contactList.addContactSource(
- new StringContactSourceServiceImpl(
- protocolProvider, OperationSetBasicTelephony.class));
-
- contactList.applyDefaultFilter();
- }
-
- /**
- * Transfer the transfer peer to the given <tt>UIContact</tt>.
- *
- * @param uiContact the contact to transfer to
- */
- private void transferToContact(UIContact uiContact)
- {
- UIContactDetail contactDetail = uiContact
- .getDefaultContactDetail(
- OperationSetBasicTelephony.class);
-
- CallManager.transferCall( transferPeer,
- contactDetail.getAddress());
- }
-}
+package net.java.sip.communicator.impl.gui.main.call;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * Represents a <tt>Dialog</tt> which allows specifying the target contact
+ * address of a transfer-call operation.
+ *
+ * @author Yana Stamcheva
+ */
+public class TransferCallDialog
+ extends OneChoiceInviteDialog
+{
+ /**
+ * The peer to transfer.
+ */
+ private final CallPeer transferPeer;
+
+ /**
+ * Creates a <tt>TransferCallDialog</tt> by specifying the peer to transfer
+ * @param peer the peer to transfer
+ */
+ public TransferCallDialog(final CallPeer peer)
+ {
+ super(GuiActivator.getResources()
+ .getI18NString("service.gui.TRANSFER_CALL_TITLE"));
+
+ this.transferPeer = peer;
+
+ this.initContactListData(peer.getProtocolProvider());
+
+ this.setInfoText(GuiActivator.getResources()
+ .getI18NString("service.gui.TRANSFER_CALL_MSG"));
+ this.setOkButtonText(GuiActivator.getResources()
+ .getI18NString("service.gui.TRANSFER"));
+
+ this.setMinimumSize(new Dimension(300, 300));
+
+ addOkButtonListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ UIContact uiContact = getSelectedContact();
+
+ if (uiContact != null)
+ {
+ transferToContact(uiContact);
+ }
+
+ setVisible(false);
+ dispose();
+ }
+ });
+ addCancelButtonListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ setVisible(false);
+ dispose();
+ }
+ });
+ }
+
+ /**
+ * Initializes contact list sources.
+ */
+ private void initContactSources()
+ {
+ DemuxContactSourceService demuxCSService
+ = GuiActivator.getDemuxContactSourceService();
+
+ // If the DemuxContactSourceService isn't registered we use the default
+ // contact source set.
+ if (demuxCSService == null)
+ return;
+
+ Iterator<UIContactSource> sourcesIter
+ = new ArrayList<UIContactSource>(
+ contactList.getContactSources()).iterator();
+
+ contactList.removeAllContactSources();
+
+ while (sourcesIter.hasNext())
+ {
+ ContactSourceService contactSource
+ = sourcesIter.next().getContactSourceService();
+
+ contactList.addContactSource(
+ demuxCSService.createDemuxContactSource(contactSource));
+ }
+ }
+
+ /**
+ * Initializes the left contact list with the contacts that could be added
+ * to the current chat session.
+ *
+ * @param protocolProvider the protocol provider from which to initialize
+ * the contact list data
+ */
+ private void initContactListData(ProtocolProviderService protocolProvider)
+ {
+ initContactSources();
+
+ contactList.addContactSource(
+ new ProtocolContactSourceServiceImpl(
+ protocolProvider, OperationSetBasicTelephony.class));
+ contactList.addContactSource(
+ new StringContactSourceServiceImpl(
+ protocolProvider, OperationSetBasicTelephony.class));
+
+ contactList.applyDefaultFilter();
+ }
+
+ /**
+ * Transfer the transfer peer to the given <tt>UIContact</tt>.
+ *
+ * @param uiContact the contact to transfer to
+ */
+ private void transferToContact(UIContact uiContact)
+ {
+ UIContactDetail contactDetail = uiContact
+ .getDefaultContactDetail(
+ OperationSetBasicTelephony.class);
+
+ CallManager.transferCall( transferPeer,
+ contactDetail.getAddress());
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java
index 80eedc6..bb32858 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceInviteDialog.java
@@ -1,4 +1,4 @@
-/*
+/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
@@ -15,570 +15,570 @@
* 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.awt.event.*;
-import java.util.*;
-import java.util.List;
-
-import javax.swing.*;
-
-import net.java.sip.communicator.impl.gui.*;
-import net.java.sip.communicator.impl.gui.main.call.*;
-import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
-import net.java.sip.communicator.impl.gui.utils.*;
-import net.java.sip.communicator.plugin.desktoputil.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.gui.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * The invite dialog is the one shown when the user clicks on the conference
- * button in the chat toolbar.
- *
- * @author Yana Stamcheva
- * @author Lyubomir Marinov
- */
-public class ConferenceInviteDialog
- extends InviteDialog
-{
- /**
- * Serial version UID.
- */
- private static final long serialVersionUID = 0L;
-
- /**
- * The account selector box.
- */
- private final JComboBox accountSelectorBox = new JComboBox();
-
- /**
- * The last selected account.
- */
- private Object lastSelectedAccount;
-
- /**
- * The telephony conference into which this instance is to invite
- * participants.
- */
- private final CallConference conference;
-
- /**
- * The current provider contact source.
- */
- private ContactSourceService currentProviderContactSource;
-
- /**
- * The current string contact source.
- */
- private ContactSourceService currentStringContactSource;
-
- /**
- * The previously selected protocol provider, with which this dialog has
- * been instantiated.
- */
- private ProtocolProviderService preselectedProtocolProvider;
-
- /**
- * Indicates whether this conference invite dialog is associated with a
- * Jitsi Videobridge invite.
- */
- private final boolean isJitsiVideobridge;
-
- /**
- * Initializes a new <tt>ConferenceInviteDialog</tt> instance which is to
- * invite contacts/participants in a specific telephony conference.
- *
- * @param conference the telephony conference in which the new instance is
- * to invite contacts/participants
- */
- public ConferenceInviteDialog(
- CallConference conference,
- ProtocolProviderService preselectedProvider,
- List<ProtocolProviderService> protocolProviders,
- final boolean isJitsiVideobridge)
- {
- // Set the correct dialog title depending if we're going to create a
- // video bridge conference call
- super((isJitsiVideobridge
- ? GuiActivator.getResources()
- .getI18NString("service.gui.INVITE_CONTACT_TO_VIDEO_BRIDGE")
- : GuiActivator.getResources()
- .getI18NString("service.gui.INVITE_CONTACT_TO_CALL")),
- false);
-
- this.conference = conference;
- this.preselectedProtocolProvider = preselectedProvider;
- this.isJitsiVideobridge = isJitsiVideobridge;
-
- if (preselectedProtocolProvider == null)
- initAccountSelectorPanel(protocolProviders);
-
- // init the list, as we check whether features are supported
- // it may take some time if we have too much contacts
- SwingUtilities.invokeLater(new Runnable()
- {
- public void run()
- {
- initContactSources();
-
- // Initialize the list of contacts to select from.
- if (preselectedProtocolProvider != null)
- initContactListData(preselectedProtocolProvider);
- else
- initContactListData(
- (ProtocolProviderService) accountSelectorBox
- .getSelectedItem());
- }
- });
-
- this.addInviteButtonListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- Collection<UIContact> selectedContacts
- = destContactList.getContacts(null);
-
- if (selectedContacts != null && selectedContacts.size() > 0)
- {
- if (preselectedProtocolProvider == null)
- preselectedProtocolProvider
- = (ProtocolProviderService) accountSelectorBox
- .getSelectedItem();
-
- if (isJitsiVideobridge)
- inviteJitsiVideobridgeContacts( preselectedProtocolProvider,
- selectedContacts);
- else
- inviteContacts(selectedContacts);
-
- // Store the last used account in order to pre-select it
- // next time.
- ConfigurationUtils.setLastCallConferenceProvider(
- preselectedProtocolProvider);
-
- dispose();
- }
- else
- {
- // TODO: The underlying invite dialog should show a message
- // to the user that she should select at least two contacts
- // in order to create a conference.
- }
- }
- });
-
- this.addCancelButtonListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- dispose();
- }
- });
- }
-
- /**
- * Constructs the <tt>ConferenceInviteDialog</tt>.
- */
- public ConferenceInviteDialog()
- {
- this(null, null, null, false);
- }
-
- /**
- * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying an
- * already created conference. To use when inviting contacts to an existing
- * conference is needed.
- *
- * @param conference the existing <tt>CallConference</tt>
- */
- public ConferenceInviteDialog(CallConference conference)
- {
- this(conference, null, null, false);
- }
-
- /**
- * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying an
- * already created conference. To use when inviting contacts to an existing
- * conference is needed.
- *
- * @param conference the existing <tt>CallConference</tt>
- */
- public ConferenceInviteDialog(
- CallConference conference,
- ProtocolProviderService preselectedProtocolProvider,
- boolean isJitsiVideobridge)
- {
- this(conference, preselectedProtocolProvider, null, isJitsiVideobridge);
- }
-
- /**
- * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying a
- * preselected protocol provider to be used and if this is an invite for
- * a video bridge conference.
- *
- * @param protocolProviders the protocol providers list
- * @param isJitsiVideobridge <tt>true</tt> if this dialog should create a
- * conference through a Jitsi Videobridge; otherwise, <tt>false</tt>
- */
- public ConferenceInviteDialog(
- List<ProtocolProviderService> protocolProviders,
- boolean isJitsiVideobridge)
- {
- this(null, null, protocolProviders, isJitsiVideobridge);
- }
-
- /**
- * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying a
- * preselected protocol provider to be used and if this is an invite for
- * a video bridge conference.
- *
- * @param selectedConfProvider the preselected protocol provider
- * @param isJitsiVideobridge <tt>true</tt> if this dialog should create a
- * conference through a Jitsi Videobridge; otherwise, <tt>false</tt>
- */
- public ConferenceInviteDialog(
- ProtocolProviderService selectedConfProvider,
- boolean isJitsiVideobridge)
- {
- this(null, selectedConfProvider, null, isJitsiVideobridge);
- }
-
- /**
- * Initializes the account selector panel.
- *
- * @param protocolProviders the list of protocol providers we'd like to
- * show in the account selector box
- */
- private void initAccountSelectorPanel(
- List<ProtocolProviderService> protocolProviders)
- {
- JLabel accountSelectorLabel = new JLabel(
- GuiActivator.getResources().getI18NString("service.gui.CALL_VIA"));
-
- TransparentPanel accountSelectorPanel
- = new TransparentPanel(new BorderLayout());
-
- accountSelectorPanel.setBorder(
- BorderFactory.createEmptyBorder(5, 5, 5, 5));
- accountSelectorPanel.add(accountSelectorLabel, BorderLayout.WEST);
- accountSelectorPanel.add(accountSelectorBox, BorderLayout.CENTER);
-
- // Initialize the account selector box.
- if (protocolProviders != null && protocolProviders.size() > 0)
- this.initAccountListData(protocolProviders);
- else
- this.initAccountListData();
-
- this.accountSelectorBox.setRenderer(new DefaultListCellRenderer()
- {
- private static final long serialVersionUID = 0L;
-
- @Override
- public Component getListCellRendererComponent(JList list,
- Object value, int index, boolean isSelected,
- boolean cellHasFocus)
- {
- ProtocolProviderService protocolProvider
- = (ProtocolProviderService) value;
-
- if (protocolProvider != null)
- {
- this.setText(
- protocolProvider.getAccountID().getDisplayName());
- this.setIcon(
- ImageLoader.getAccountStatusImage(protocolProvider));
- }
-
- return this;
- }
- });
-
- this.accountSelectorBox.addActionListener(new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- Object accountSelectorBoxSelectedItem
- = accountSelectorBox.getSelectedItem();
-
- if (lastSelectedAccount == null
- || !lastSelectedAccount
- .equals(accountSelectorBoxSelectedItem))
- {
- lastSelectedAccount = accountSelectorBoxSelectedItem;
-
- initContactListData(
- (ProtocolProviderService) accountSelectorBox
- .getSelectedItem());
-
- if (isJitsiVideobridge)
- destContactList.removeAll();
- }
- }
- });
-
- this.getContentPane().add(accountSelectorPanel, BorderLayout.NORTH);
- }
-
- /**
- * Initializes the account selector box with the given list of
- * <tt>ProtocolProviderService</tt>-s.
- *
- * @param protocolProviders the list of <tt>ProtocolProviderService</tt>-s
- * we'd like to show in the account selector box
- */
- private void initAccountListData(
- List<ProtocolProviderService> protocolProviders)
- {
- Iterator<ProtocolProviderService> providersIter
- = protocolProviders.iterator();
-
- while (providersIter.hasNext())
- {
- ProtocolProviderService protocolProvider
- = providersIter.next();
-
- accountSelectorBox.addItem(protocolProvider);
- }
-
- if (accountSelectorBox.getItemCount() > 0)
- accountSelectorBox.setSelectedIndex(0);
- }
-
- /**
- * Initializes the account list.
- */
- private void initAccountListData()
- {
- Iterator<ProtocolProviderService> protocolProviders
- = GuiActivator.getUIService().getMainFrame().getProtocolProviders();
-
- while(protocolProviders.hasNext())
- {
- ProtocolProviderService protocolProvider
- = protocolProviders.next();
- OperationSet opSet
- = protocolProvider.getOperationSet(
- OperationSetTelephonyConferencing.class);
-
- if ((opSet != null) && protocolProvider.isRegistered())
- accountSelectorBox.addItem(protocolProvider);
- }
-
- // Try to select the last used account if available.
- ProtocolProviderService pps
- = ConfigurationUtils.getLastCallConferenceProvider();
-
- if (pps == null && conference != null)
- {
- /*
- * Pick up the first account from the ones participating in the
- * associated telephony conference which supports
- * OperationSetTelephonyConferencing.
- */
- for (Call call : conference.getCalls())
- {
- ProtocolProviderService callPps = call.getProtocolProvider();
-
- if (callPps.getOperationSet(
- OperationSetTelephonyConferencing.class)
- != null)
- {
- pps = callPps;
- break;
- }
- }
- }
-
- if (pps != null)
- accountSelectorBox.setSelectedItem(pps);
- else if (accountSelectorBox.getItemCount() > 0)
- accountSelectorBox.setSelectedIndex(0);
- }
-
- /**
- * Initializes contact list sources.
- */
- private void initContactSources()
- {
- DemuxContactSourceService demuxCSService
- = GuiActivator.getDemuxContactSourceService();
-
- // If the DemuxContactSourceService isn't registered we use the default
- // contact source set.
- if (demuxCSService == null)
- return;
-
- Iterator<UIContactSource> sourcesIter
- = new ArrayList<UIContactSource>(
- srcContactList.getContactSources()).iterator();
-
- srcContactList.removeAllContactSources();
-
- while (sourcesIter.hasNext())
- {
- ContactSourceService contactSource
- = sourcesIter.next().getContactSourceService();
-
- srcContactList.addContactSource(
- demuxCSService.createDemuxContactSource(contactSource));
- }
- }
-
- /**
- * Initializes the left contact list with the contacts that could be added
- * to the current chat session.
- * @param protocolProvider the protocol provider from which to initialize
- * the contact list data
- */
- private void initContactListData(ProtocolProviderService protocolProvider)
- {
- this.setCurrentProvider(protocolProvider);
-
- Iterator<UIContactSource> sourcesIter
- = new ArrayList<UIContactSource>(
- srcContactList.getContactSources()).iterator();
-
- while (sourcesIter.hasNext())
- {
- ContactSourceService contactSource
- = sourcesIter.next().getContactSourceService();
-
- if (contactSource instanceof ProtocolAwareContactSourceService)
- {
- ((ProtocolAwareContactSourceService) contactSource)
- .setPreferredProtocolProvider(
- OperationSetBasicTelephony.class, protocolProvider);
- }
- }
-
- srcContactList.removeContactSource(currentProviderContactSource);
- srcContactList.removeContactSource(currentStringContactSource);
-
- currentProviderContactSource
- = new ProtocolContactSourceServiceImpl(
- protocolProvider,
- OperationSetBasicTelephony.class);
- currentStringContactSource
- = new StringContactSourceServiceImpl(
- protocolProvider,
- OperationSetBasicTelephony.class);
-
- srcContactList.addContactSource(currentProviderContactSource);
- srcContactList.addContactSource(currentStringContactSource);
-
- srcContactList.applyDefaultFilter();
- }
-
- /**
- * Invites the contacts to the chat conference.
- *
- * @param contacts the list of contacts to invite
- */
- private void inviteContacts(Collection<UIContact> contacts)
- {
- ProtocolProviderService selectedProvider;
- Map<ProtocolProviderService, List<String>> selectedProviderCallees
- = new HashMap<ProtocolProviderService, List<String>>();
- List<String> callees;
-
- Iterator<UIContact> contactsIter = contacts.iterator();
-
- while (contactsIter.hasNext())
- {
- UIContact uiContact = contactsIter.next();
-
- Iterator<UIContactDetail> contactDetailsIter = uiContact
- .getContactDetailsForOperationSet(
- OperationSetBasicTelephony.class).iterator();
-
- // We invite the first protocol contact that corresponds to the
- // invite provider.
- if (contactDetailsIter.hasNext())
- {
- UIContactDetail inviteDetail = contactDetailsIter.next();
- selectedProvider = inviteDetail
- .getPreferredProtocolProvider(
- OperationSetBasicTelephony.class);
-
- if (selectedProvider == null)
- {
- selectedProvider
- = (ProtocolProviderService)
- accountSelectorBox.getSelectedItem();
- }
-
- if(selectedProvider != null
- && selectedProviderCallees.get(selectedProvider) != null)
- {
- callees = selectedProviderCallees.get(selectedProvider);
- }
- else
- {
- callees = new ArrayList<String>();
- }
-
- callees.add(inviteDetail.getAddress());
- selectedProviderCallees.put(selectedProvider, callees);
- }
- }
-
- if(conference != null)
- {
- CallManager.inviteToConferenceCall(
- selectedProviderCallees,
- conference);
- }
- else
- {
- CallManager.createConferenceCall(selectedProviderCallees);
- }
- }
-
- /**
- * Invites the contacts to the chat conference.
- *
- * @param contacts the list of contacts to invite
- */
- private void inviteJitsiVideobridgeContacts(
- ProtocolProviderService preselectedProvider,
- Collection<UIContact> contacts)
- {
- List<String> callees = new ArrayList<String>();
-
- Iterator<UIContact> contactsIter = contacts.iterator();
-
- while (contactsIter.hasNext())
- {
- UIContact uiContact = contactsIter.next();
-
- Iterator<UIContactDetail> contactDetailsIter = uiContact
- .getContactDetailsForOperationSet(
- OperationSetBasicTelephony.class).iterator();
-
- // We invite the first protocol contact that corresponds to the
- // invite provider.
- if (contactDetailsIter.hasNext())
- {
- UIContactDetail inviteDetail = contactDetailsIter.next();
-
- callees.add(inviteDetail.getAddress());
- }
- }
-
- if(conference != null)
- {
- CallManager.inviteToJitsiVideobridgeConfCall(
- callees.toArray(new String[callees.size()]),
- conference.getCalls().get(0));
- }
- else
- {
- CallManager.createJitsiVideobridgeConfCall(
- preselectedProvider,
- callees.toArray(new String[callees.size()]));
- }
- }
-}
+package net.java.sip.communicator.impl.gui.main.call.conference;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.List;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.impl.gui.*;
+import net.java.sip.communicator.impl.gui.main.call.*;
+import net.java.sip.communicator.impl.gui.main.contactlist.contactsource.*;
+import net.java.sip.communicator.impl.gui.utils.*;
+import net.java.sip.communicator.plugin.desktoputil.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * The invite dialog is the one shown when the user clicks on the conference
+ * button in the chat toolbar.
+ *
+ * @author Yana Stamcheva
+ * @author Lyubomir Marinov
+ */
+public class ConferenceInviteDialog
+ extends InviteDialog
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * The account selector box.
+ */
+ private final JComboBox accountSelectorBox = new JComboBox();
+
+ /**
+ * The last selected account.
+ */
+ private Object lastSelectedAccount;
+
+ /**
+ * The telephony conference into which this instance is to invite
+ * participants.
+ */
+ private final CallConference conference;
+
+ /**
+ * The current provider contact source.
+ */
+ private ContactSourceService currentProviderContactSource;
+
+ /**
+ * The current string contact source.
+ */
+ private ContactSourceService currentStringContactSource;
+
+ /**
+ * The previously selected protocol provider, with which this dialog has
+ * been instantiated.
+ */
+ private ProtocolProviderService preselectedProtocolProvider;
+
+ /**
+ * Indicates whether this conference invite dialog is associated with a
+ * Jitsi Videobridge invite.
+ */
+ private final boolean isJitsiVideobridge;
+
+ /**
+ * Initializes a new <tt>ConferenceInviteDialog</tt> instance which is to
+ * invite contacts/participants in a specific telephony conference.
+ *
+ * @param conference the telephony conference in which the new instance is
+ * to invite contacts/participants
+ */
+ public ConferenceInviteDialog(
+ CallConference conference,
+ ProtocolProviderService preselectedProvider,
+ List<ProtocolProviderService> protocolProviders,
+ final boolean isJitsiVideobridge)
+ {
+ // Set the correct dialog title depending if we're going to create a
+ // video bridge conference call
+ super((isJitsiVideobridge
+ ? GuiActivator.getResources()
+ .getI18NString("service.gui.INVITE_CONTACT_TO_VIDEO_BRIDGE")
+ : GuiActivator.getResources()
+ .getI18NString("service.gui.INVITE_CONTACT_TO_CALL")),
+ false);
+
+ this.conference = conference;
+ this.preselectedProtocolProvider = preselectedProvider;
+ this.isJitsiVideobridge = isJitsiVideobridge;
+
+ if (preselectedProtocolProvider == null)
+ initAccountSelectorPanel(protocolProviders);
+
+ // init the list, as we check whether features are supported
+ // it may take some time if we have too much contacts
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ initContactSources();
+
+ // Initialize the list of contacts to select from.
+ if (preselectedProtocolProvider != null)
+ initContactListData(preselectedProtocolProvider);
+ else
+ initContactListData(
+ (ProtocolProviderService) accountSelectorBox
+ .getSelectedItem());
+ }
+ });
+
+ this.addInviteButtonListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ Collection<UIContact> selectedContacts
+ = destContactList.getContacts(null);
+
+ if (selectedContacts != null && selectedContacts.size() > 0)
+ {
+ if (preselectedProtocolProvider == null)
+ preselectedProtocolProvider
+ = (ProtocolProviderService) accountSelectorBox
+ .getSelectedItem();
+
+ if (isJitsiVideobridge)
+ inviteJitsiVideobridgeContacts( preselectedProtocolProvider,
+ selectedContacts);
+ else
+ inviteContacts(selectedContacts);
+
+ // Store the last used account in order to pre-select it
+ // next time.
+ ConfigurationUtils.setLastCallConferenceProvider(
+ preselectedProtocolProvider);
+
+ dispose();
+ }
+ else
+ {
+ // TODO: The underlying invite dialog should show a message
+ // to the user that she should select at least two contacts
+ // in order to create a conference.
+ }
+ }
+ });
+
+ this.addCancelButtonListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ dispose();
+ }
+ });
+ }
+
+ /**
+ * Constructs the <tt>ConferenceInviteDialog</tt>.
+ */
+ public ConferenceInviteDialog()
+ {
+ this(null, null, null, false);
+ }
+
+ /**
+ * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying an
+ * already created conference. To use when inviting contacts to an existing
+ * conference is needed.
+ *
+ * @param conference the existing <tt>CallConference</tt>
+ */
+ public ConferenceInviteDialog(CallConference conference)
+ {
+ this(conference, null, null, false);
+ }
+
+ /**
+ * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying an
+ * already created conference. To use when inviting contacts to an existing
+ * conference is needed.
+ *
+ * @param conference the existing <tt>CallConference</tt>
+ */
+ public ConferenceInviteDialog(
+ CallConference conference,
+ ProtocolProviderService preselectedProtocolProvider,
+ boolean isJitsiVideobridge)
+ {
+ this(conference, preselectedProtocolProvider, null, isJitsiVideobridge);
+ }
+
+ /**
+ * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying a
+ * preselected protocol provider to be used and if this is an invite for
+ * a video bridge conference.
+ *
+ * @param protocolProviders the protocol providers list
+ * @param isJitsiVideobridge <tt>true</tt> if this dialog should create a
+ * conference through a Jitsi Videobridge; otherwise, <tt>false</tt>
+ */
+ public ConferenceInviteDialog(
+ List<ProtocolProviderService> protocolProviders,
+ boolean isJitsiVideobridge)
+ {
+ this(null, null, protocolProviders, isJitsiVideobridge);
+ }
+
+ /**
+ * Creates an instance of <tt>ConferenceInviteDialog</tt> by specifying a
+ * preselected protocol provider to be used and if this is an invite for
+ * a video bridge conference.
+ *
+ * @param selectedConfProvider the preselected protocol provider
+ * @param isJitsiVideobridge <tt>true</tt> if this dialog should create a
+ * conference through a Jitsi Videobridge; otherwise, <tt>false</tt>
+ */
+ public ConferenceInviteDialog(
+ ProtocolProviderService selectedConfProvider,
+ boolean isJitsiVideobridge)
+ {
+ this(null, selectedConfProvider, null, isJitsiVideobridge);
+ }
+
+ /**
+ * Initializes the account selector panel.
+ *
+ * @param protocolProviders the list of protocol providers we'd like to
+ * show in the account selector box
+ */
+ private void initAccountSelectorPanel(
+ List<ProtocolProviderService> protocolProviders)
+ {
+ JLabel accountSelectorLabel = new JLabel(
+ GuiActivator.getResources().getI18NString("service.gui.CALL_VIA"));
+
+ TransparentPanel accountSelectorPanel
+ = new TransparentPanel(new BorderLayout());
+
+ accountSelectorPanel.setBorder(
+ BorderFactory.createEmptyBorder(5, 5, 5, 5));
+ accountSelectorPanel.add(accountSelectorLabel, BorderLayout.WEST);
+ accountSelectorPanel.add(accountSelectorBox, BorderLayout.CENTER);
+
+ // Initialize the account selector box.
+ if (protocolProviders != null && protocolProviders.size() > 0)
+ this.initAccountListData(protocolProviders);
+ else
+ this.initAccountListData();
+
+ this.accountSelectorBox.setRenderer(new DefaultListCellRenderer()
+ {
+ private static final long serialVersionUID = 0L;
+
+ @Override
+ public Component getListCellRendererComponent(JList list,
+ Object value, int index, boolean isSelected,
+ boolean cellHasFocus)
+ {
+ ProtocolProviderService protocolProvider
+ = (ProtocolProviderService) value;
+
+ if (protocolProvider != null)
+ {
+ this.setText(
+ protocolProvider.getAccountID().getDisplayName());
+ this.setIcon(
+ ImageLoader.getAccountStatusImage(protocolProvider));
+ }
+
+ return this;
+ }
+ });
+
+ this.accountSelectorBox.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ Object accountSelectorBoxSelectedItem
+ = accountSelectorBox.getSelectedItem();
+
+ if (lastSelectedAccount == null
+ || !lastSelectedAccount
+ .equals(accountSelectorBoxSelectedItem))
+ {
+ lastSelectedAccount = accountSelectorBoxSelectedItem;
+
+ initContactListData(
+ (ProtocolProviderService) accountSelectorBox
+ .getSelectedItem());
+
+ if (isJitsiVideobridge)
+ destContactList.removeAll();
+ }
+ }
+ });
+
+ this.getContentPane().add(accountSelectorPanel, BorderLayout.NORTH);
+ }
+
+ /**
+ * Initializes the account selector box with the given list of
+ * <tt>ProtocolProviderService</tt>-s.
+ *
+ * @param protocolProviders the list of <tt>ProtocolProviderService</tt>-s
+ * we'd like to show in the account selector box
+ */
+ private void initAccountListData(
+ List<ProtocolProviderService> protocolProviders)
+ {
+ Iterator<ProtocolProviderService> providersIter
+ = protocolProviders.iterator();
+
+ while (providersIter.hasNext())
+ {
+ ProtocolProviderService protocolProvider
+ = providersIter.next();
+
+ accountSelectorBox.addItem(protocolProvider);
+ }
+
+ if (accountSelectorBox.getItemCount() > 0)
+ accountSelectorBox.setSelectedIndex(0);
+ }
+
+ /**
+ * Initializes the account list.
+ */
+ private void initAccountListData()
+ {
+ Iterator<ProtocolProviderService> protocolProviders
+ = GuiActivator.getUIService().getMainFrame().getProtocolProviders();
+
+ while(protocolProviders.hasNext())
+ {
+ ProtocolProviderService protocolProvider
+ = protocolProviders.next();
+ OperationSet opSet
+ = protocolProvider.getOperationSet(
+ OperationSetTelephonyConferencing.class);
+
+ if ((opSet != null) && protocolProvider.isRegistered())
+ accountSelectorBox.addItem(protocolProvider);
+ }
+
+ // Try to select the last used account if available.
+ ProtocolProviderService pps
+ = ConfigurationUtils.getLastCallConferenceProvider();
+
+ if (pps == null && conference != null)
+ {
+ /*
+ * Pick up the first account from the ones participating in the
+ * associated telephony conference which supports
+ * OperationSetTelephonyConferencing.
+ */
+ for (Call call : conference.getCalls())
+ {
+ ProtocolProviderService callPps = call.getProtocolProvider();
+
+ if (callPps.getOperationSet(
+ OperationSetTelephonyConferencing.class)
+ != null)
+ {
+ pps = callPps;
+ break;
+ }
+ }
+ }
+
+ if (pps != null)
+ accountSelectorBox.setSelectedItem(pps);
+ else if (accountSelectorBox.getItemCount() > 0)
+ accountSelectorBox.setSelectedIndex(0);
+ }
+
+ /**
+ * Initializes contact list sources.
+ */
+ private void initContactSources()
+ {
+ DemuxContactSourceService demuxCSService
+ = GuiActivator.getDemuxContactSourceService();
+
+ // If the DemuxContactSourceService isn't registered we use the default
+ // contact source set.
+ if (demuxCSService == null)
+ return;
+
+ Iterator<UIContactSource> sourcesIter
+ = new ArrayList<UIContactSource>(
+ srcContactList.getContactSources()).iterator();
+
+ srcContactList.removeAllContactSources();
+
+ while (sourcesIter.hasNext())
+ {
+ ContactSourceService contactSource
+ = sourcesIter.next().getContactSourceService();
+
+ srcContactList.addContactSource(
+ demuxCSService.createDemuxContactSource(contactSource));
+ }
+ }
+
+ /**
+ * Initializes the left contact list with the contacts that could be added
+ * to the current chat session.
+ * @param protocolProvider the protocol provider from which to initialize
+ * the contact list data
+ */
+ private void initContactListData(ProtocolProviderService protocolProvider)
+ {
+ this.setCurrentProvider(protocolProvider);
+
+ Iterator<UIContactSource> sourcesIter
+ = new ArrayList<UIContactSource>(
+ srcContactList.getContactSources()).iterator();
+
+ while (sourcesIter.hasNext())
+ {
+ ContactSourceService contactSource
+ = sourcesIter.next().getContactSourceService();
+
+ if (contactSource instanceof ProtocolAwareContactSourceService)
+ {
+ ((ProtocolAwareContactSourceService) contactSource)
+ .setPreferredProtocolProvider(
+ OperationSetBasicTelephony.class, protocolProvider);
+ }
+ }
+
+ srcContactList.removeContactSource(currentProviderContactSource);
+ srcContactList.removeContactSource(currentStringContactSource);
+
+ currentProviderContactSource
+ = new ProtocolContactSourceServiceImpl(
+ protocolProvider,
+ OperationSetBasicTelephony.class);
+ currentStringContactSource
+ = new StringContactSourceServiceImpl(
+ protocolProvider,
+ OperationSetBasicTelephony.class);
+
+ srcContactList.addContactSource(currentProviderContactSource);
+ srcContactList.addContactSource(currentStringContactSource);
+
+ srcContactList.applyDefaultFilter();
+ }
+
+ /**
+ * Invites the contacts to the chat conference.
+ *
+ * @param contacts the list of contacts to invite
+ */
+ private void inviteContacts(Collection<UIContact> contacts)
+ {
+ ProtocolProviderService selectedProvider;
+ Map<ProtocolProviderService, List<String>> selectedProviderCallees
+ = new HashMap<ProtocolProviderService, List<String>>();
+ List<String> callees;
+
+ Iterator<UIContact> contactsIter = contacts.iterator();
+
+ while (contactsIter.hasNext())
+ {
+ UIContact uiContact = contactsIter.next();
+
+ Iterator<UIContactDetail> contactDetailsIter = uiContact
+ .getContactDetailsForOperationSet(
+ OperationSetBasicTelephony.class).iterator();
+
+ // We invite the first protocol contact that corresponds to the
+ // invite provider.
+ if (contactDetailsIter.hasNext())
+ {
+ UIContactDetail inviteDetail = contactDetailsIter.next();
+ selectedProvider = inviteDetail
+ .getPreferredProtocolProvider(
+ OperationSetBasicTelephony.class);
+
+ if (selectedProvider == null)
+ {
+ selectedProvider
+ = (ProtocolProviderService)
+ accountSelectorBox.getSelectedItem();
+ }
+
+ if(selectedProvider != null
+ && selectedProviderCallees.get(selectedProvider) != null)
+ {
+ callees = selectedProviderCallees.get(selectedProvider);
+ }
+ else
+ {
+ callees = new ArrayList<String>();
+ }
+
+ callees.add(inviteDetail.getAddress());
+ selectedProviderCallees.put(selectedProvider, callees);
+ }
+ }
+
+ if(conference != null)
+ {
+ CallManager.inviteToConferenceCall(
+ selectedProviderCallees,
+ conference);
+ }
+ else
+ {
+ CallManager.createConferenceCall(selectedProviderCallees);
+ }
+ }
+
+ /**
+ * Invites the contacts to the chat conference.
+ *
+ * @param contacts the list of contacts to invite
+ */
+ private void inviteJitsiVideobridgeContacts(
+ ProtocolProviderService preselectedProvider,
+ Collection<UIContact> contacts)
+ {
+ List<String> callees = new ArrayList<String>();
+
+ Iterator<UIContact> contactsIter = contacts.iterator();
+
+ while (contactsIter.hasNext())
+ {
+ UIContact uiContact = contactsIter.next();
+
+ Iterator<UIContactDetail> contactDetailsIter = uiContact
+ .getContactDetailsForOperationSet(
+ OperationSetBasicTelephony.class).iterator();
+
+ // We invite the first protocol contact that corresponds to the
+ // invite provider.
+ if (contactDetailsIter.hasNext())
+ {
+ UIContactDetail inviteDetail = contactDetailsIter.next();
+
+ callees.add(inviteDetail.getAddress());
+ }
+ }
+
+ if(conference != null)
+ {
+ CallManager.inviteToJitsiVideobridgeConfCall(
+ callees.toArray(new String[callees.size()]),
+ conference.getCalls().get(0));
+ }
+ else
+ {
+ CallManager.createJitsiVideobridgeConfCall(
+ preselectedProvider,
+ callees.toArray(new String[callees.size()]));
+ }
+ }
+}
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));
+ }
+ }
+ }
+ }
+}