/**
   * Re-dispatches glass pane mouse events only in case they occur on the security panel.
   *
   * @param glassPane the glass pane
   * @param e the mouse event in question
   */
  private void redispatchMouseEvent(Component glassPane, MouseEvent e) {
    Point glassPanePoint = e.getPoint();

    Point securityPanelPoint =
        SwingUtilities.convertPoint(glassPane, glassPanePoint, securityPanel);

    Component component;
    Point componentPoint;

    if (securityPanelPoint.y > 0) {
      component = securityPanel;
      componentPoint = securityPanelPoint;
    } else {
      Container contentPane =
          callRenderer.getCallContainer().getCallWindow().getFrame().getContentPane();

      Point containerPoint = SwingUtilities.convertPoint(glassPane, glassPanePoint, contentPane);

      component =
          SwingUtilities.getDeepestComponentAt(contentPane, containerPoint.x, containerPoint.y);

      componentPoint = SwingUtilities.convertPoint(contentPane, glassPanePoint, component);
    }

    if (component != null)
      component.dispatchEvent(
          new MouseEvent(
              component,
              e.getID(),
              e.getWhen(),
              e.getModifiers(),
              componentPoint.x,
              componentPoint.y,
              e.getClickCount(),
              e.isPopupTrigger()));

    e.consume();
  }
  /**
   * Shows/hides the security panel.
   *
   * @param isVisible <tt>true</tt> to show the security panel, <tt>false</tt> to hide it
   */
  public void setSecurityPanelVisible(final boolean isVisible) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              setSecurityPanelVisible(isVisible);
            }
          });
      return;
    }

    final JFrame callFrame = callRenderer.getCallContainer().getCallWindow().getFrame();

    final JPanel glassPane = (JPanel) callFrame.getGlassPane();

    if (!isVisible) {
      // Need to hide the security panel explicitly in order to keep the
      // fade effect.
      securityPanel.setVisible(false);
      glassPane.setVisible(false);
      glassPane.removeAll();
    } else {
      glassPane.setLayout(null);
      glassPane.addMouseListener(
          new MouseListener() {
            public void mouseClicked(MouseEvent e) {
              redispatchMouseEvent(glassPane, e);
            }

            public void mouseEntered(MouseEvent e) {
              redispatchMouseEvent(glassPane, e);
            }

            public void mouseExited(MouseEvent e) {
              redispatchMouseEvent(glassPane, e);
            }

            public void mousePressed(MouseEvent e) {
              redispatchMouseEvent(glassPane, e);
            }

            public void mouseReleased(MouseEvent e) {
              redispatchMouseEvent(glassPane, e);
            }
          });

      Point securityLabelPoint = securityStatusLabel.getLocation();

      Point newPoint =
          SwingUtilities.convertPoint(
              securityStatusLabel.getParent(),
              securityLabelPoint.x,
              securityLabelPoint.y,
              callFrame);

      securityPanel.setBeginPoint(new Point((int) newPoint.getX() + 15, 0));
      securityPanel.setBounds(0, (int) newPoint.getY() - 5, this.getWidth(), 130);

      glassPane.add(securityPanel);
      // Need to show the security panel explicitly in order to keep the
      // fade effect.
      securityPanel.setVisible(true);
      glassPane.setVisible(true);

      glassPane.addComponentListener(
          new ComponentAdapter() {
            /** Invoked when the component's size changes. */
            @Override
            public void componentResized(ComponentEvent e) {
              if (glassPane.isVisible()) {
                glassPane.setVisible(false);
                callFrame.removeComponentListener(this);
              }
            }
          });
    }
  }
 /**
  * Returns the parent <tt>CallPanel</tt> containing this renderer.
  *
  * @return the parent <tt>CallPanel</tt> containing this renderer
  */
 public CallPanel getCallPanel() {
   return callRenderer.getCallContainer();
 }
  /**
   * Updates this view i.e. <tt>OneToOneCallPeerPanel</tt> so that it depicts the current state of
   * its model i.e. <tt>callPeer</tt>. The update is performed in the AWT event dispatching thread.
   */
  private void updateViewFromModelInEventDispatchThread() {
    /*
     * We receive events/notifications from various threads and we respond
     * to them in the AWT event dispatching thread. It is possible to first
     * schedule an event to be brought to the AWT event dispatching thread,
     * then to have #dispose() invoked on this instance and, finally, to
     * receive the scheduled event in the AWT event dispatching thread. In
     * such a case, this disposed instance should not respond to the event
     * because it may, for example, steal a visual Components depicting
     * video (which cannot belong to more than one parent at a time) from
     * another non-disposed OneToOneCallPeerPanel.
     */
    if (disposed) return;

    /*
     * Update the display of visual <tt>Component</tt>s depicting video
     * streaming between the local peer/user and the remote peer(s).
     */

    OperationSetVideoTelephony videoTelephony =
        callPeer.getProtocolProvider().getOperationSet(OperationSetVideoTelephony.class);
    Component remoteVideo = null;
    Component localVideo = null;

    if (videoTelephony != null) {
      List<Component> remoteVideos = videoTelephony.getVisualComponents(callPeer);

      if ((remoteVideos != null) && !remoteVideos.isEmpty()) {
        /*
         * TODO OneToOneCallPeerPanel displays a one-to-one conversation
         * between the local peer/user and a specific remote peer. If
         * the remote peer is the focus of a telephony conference of its
         * own, it may be sending multiple videos to the local peer.
         * Switching to a user interface which displays multiple videos
         * is the responsibility of whoever decided that this
         * OneToOneCallPeerPanel is to be used to depict the current
         * state of the CallConference associated with the CallPeer
         * depicted by this instance. If that switching decides that
         * this instance is to continue being the user interface, then
         * we should probably pick up the remote video which is
         * generated by the remote peer and not one of its
         * ConferenceMembers.
         */
        remoteVideo = remoteVideos.get(0);
      }

      if (uiVideoHandler.isLocalVideoVisible()) {
        try {
          localVideo = videoTelephony.getLocalVisualComponent(callPeer);
        } catch (OperationFailedException ofe) {
          /*
           * Well, we cannot do much about the exception. We'll just
           * not display the local video.
           */
          logger.warn("Failed to retrieve local video to be displayed.", ofe);
        }
      }

      /*
       * Determine whether there is actually a change in the local and
       * remote videos which requires an update.
       */
      boolean localVideoChanged =
          ((localVideo != this.localVideo)
              || ((localVideo != null) && !UIVideoHandler2.isAncestor(center, localVideo)));
      boolean remoteVideoChanged =
          ((remoteVideo != this.remoteVideo)
              || ((remoteVideo != null) && !UIVideoHandler2.isAncestor(center, remoteVideo)));

      // If the remote video has changed, maybe the CallPanel can display
      // the LO/SD/HD button.
      if (remoteVideoChanged) {
        // Updates video component which may listen the mouse and key
        // events.
        if (desktopSharingMouseAndKeyboardListener != null) {
          desktopSharingMouseAndKeyboardListener.setVideoComponent(remoteVideo);
        }

        CallPanel callPanel = callRenderer.getCallContainer();
        // The remote video has been added, then tries to display the
        // LO/SD/HD button.
        if (remoteVideo != null) {
          callPanel.addRemoteVideoSpecificComponents(callPeer);
        }
        // The remote video has been removed, then hide the LO/SD/HD
        // button if it is currently displayed.
        else {
          callPanel.removeRemoteVideoSpecificComponents();
        }
      }

      if (localVideoChanged || remoteVideoChanged) {
        /*
         * VideoContainer and JAWTRenderer cannot handle random
         * additions of Components. Removing the localVideo when the
         * user has requests its hiding though, should work without
         * removing all Components from the VideoCotainer and adding
         * them again.
         */
        if (localVideoChanged && !remoteVideoChanged && (localVideo == null)) {
          if (this.localVideo != null) {
            center.remove(this.localVideo);
            this.localVideo = null;

            if (closeLocalVisualComponentButton != null)
              center.remove(closeLocalVisualComponentButton);
          }
        } else {
          center.removeAll();
          this.localVideo = null;
          this.remoteVideo = null;

          /*
           * AWT does not make a guarantee about the Z order even
           * within an operating system i.e. the order of adding the
           * Components to their Container does not mean that they
           * will be determinedly painted in that or reverse order.
           * Anyway, there appears to be an expectation among the
           * developers less acquainted with AWT that AWT paints the
           * Components of a Container in an order that is the reverse
           * of the order of their adding. In order to satisfy that
           * expectation and thus give at least some idea to the
           * developers reading the code bellow, do add the Components
           * according to that expectation.
           */

          if (localVideo != null) {
            if (closeLocalVisualComponentButton == null) {
              closeLocalVisualComponentButton = new CloseLocalVisualComponentButton(uiVideoHandler);
            }
            center.add(closeLocalVisualComponentButton, VideoLayout.CLOSE_LOCAL_BUTTON, -1);

            center.add(localVideo, VideoLayout.LOCAL, -1);
            this.localVideo = localVideo;
          }

          if (remoteVideo != null) {
            center.add(remoteVideo, VideoLayout.CENTER_REMOTE, -1);
            this.remoteVideo = remoteVideo;
          }
        }
      }
    }
  }