Пример #1
1
  /**
   * Sets the mute status icon to the status panel.
   *
   * @param isMute indicates if the call with this peer is muted
   */
  public void setMute(final boolean isMute) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              setMute(isMute);
            }
          });
      return;
    }

    if (isMute) {
      muteStatusLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.MUTE_STATUS_ICON)));
      muteStatusLabel.setBorder(BorderFactory.createEmptyBorder(2, 3, 2, 3));
    } else {
      muteStatusLabel.setIcon(null);
      muteStatusLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
    }

    // Update input volume control button state to reflect the current
    // mute status.
    if (localLevel.isSelected() != isMute) localLevel.setSelected(isMute);

    this.revalidate();
    this.repaint();
  }
  /** Reload images and colors. */
  public void loadSkin() {
    errorIconLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.EXCLAMATION_MARK)));

    if (downloadFile != null)
      imageLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.DEFAULT_FILE_ICON)));

    errorArea.setForeground(new Color(resources.getColor("service.gui.ERROR_FOREGROUND")));
  }
Пример #3
0
 /** Sets the icons of the call buttons depending on the chat session type. */
 private void setCallButtonsIcons() {
   if (chatSession instanceof ConferenceChatSession) {
     callButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_ROOM_CALL));
     callVideoButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_ROOM_VIDEO_CALL));
     callButton.setPreferredSize(new Dimension(29, 25));
     callVideoButton.setPreferredSize(new Dimension(29, 25));
   } else {
     callButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_CALL));
     callVideoButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_VIDEO_CALL));
     callButton.setPreferredSize(new Dimension(25, 25));
     callVideoButton.setPreferredSize(new Dimension(25, 25));
   }
   callButton.repaint();
   callVideoButton.repaint();
 }
Пример #4
0
  /** Reloads all icons. */
  public void loadSkin() {
    if (localLevel != null)
      localLevel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.MICROPHONE)));

    if (remoteLevel != null && remoteLevel instanceof Skinnable)
      ((Skinnable) remoteLevel).loadSkin();

    if (muteStatusLabel.getIcon() != null)
      muteStatusLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.MUTE_STATUS_ICON)));

    if (holdStatusLabel.getIcon() != null)
      holdStatusLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.HOLD_STATUS_ICON)));

    securityStatusLabel.setIcon(new ImageIcon(ImageLoader.getImage(securityImageID)));

    if (peerImage == null) {
      photoLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO)));
    }
  }
Пример #5
0
  /** Reloads icons for buttons. */
  public void loadSkin() {
    inviteButton.setIconImage(ImageLoader.getImage(ImageLoader.ADD_TO_CHAT_ICON));

    historyButton.loadSkin();

    sendFileButton.setIconImage(ImageLoader.getImage(ImageLoader.SEND_FILE_ICON));

    fontButton.setIconImage(ImageLoader.getImage(ImageLoader.FONT_ICON));

    previousButton.setIconImage(ImageLoader.getImage(ImageLoader.PREVIOUS_ICON));

    nextButton.setIconImage(ImageLoader.getImage(ImageLoader.NEXT_ICON));

    leaveChatRoomButton.setIconImage(ImageLoader.getImage(ImageLoader.LEAVE_ICON));

    desktopSharingButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_DESKTOP_SHARING));

    optionsButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_CONFIGURE_ICON));

    setCallButtonsIcons();
  }
Пример #6
0
  /**
   * Sets the "on hold" property value.
   *
   * @param isOnHold indicates if the call with this peer is put on hold
   */
  public void setOnHold(final boolean isOnHold) {
    if (!SwingUtilities.isEventDispatchThread()) {
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              setOnHold(isOnHold);
            }
          });
      return;
    }

    if (isOnHold) {
      holdStatusLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.HOLD_STATUS_ICON)));
      holdStatusLabel.setBorder(BorderFactory.createEmptyBorder(2, 3, 2, 3));
    } else {
      holdStatusLabel.setIcon(null);
      holdStatusLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
    }

    this.revalidate();
    this.repaint();
  }
Пример #7
0
 /**
  * Gets the <tt>Icon</tt> to be displayed in {@link #photoLabel}.
  *
  * @return the <tt>Icon</tt> to be displayed in {@link #photoLabel}
  */
 private ImageIcon getPhotoLabelIcon() {
   return (peerImage == null)
       ? new ImageIcon(ImageLoader.getImage(ImageLoader.DEFAULT_USER_PHOTO))
       : peerImage;
 }
Пример #8
0
/**
 * The <tt>CallManager</tt> is the one that handles calls. It contains also the "Call" and "Hangup"
 * buttons panel. Here are handles incoming and outgoing calls from and to the call operation set.
 *
 * @author Yana Stamcheva
 */
public class CallManager extends JPanel
    implements ActionListener, CallListener, ListSelectionListener, ChangeListener {
  private Logger logger = Logger.getLogger(CallManager.class.getName());

  private CallComboBox phoneNumberCombo;

  private JPanel comboPanel = new JPanel(new BorderLayout());

  private JPanel buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));

  private JLabel callViaLabel = new JLabel(Messages.getI18NString("callVia").getText() + " ");

  private JPanel callViaPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 4));

  private AccountSelectorBox accountSelectorBox;

  private SIPCommButton callButton =
      new SIPCommButton(
          ImageLoader.getImage(ImageLoader.CALL_BUTTON_BG),
          ImageLoader.getImage(ImageLoader.CALL_ROLLOVER_BUTTON_BG),
          null,
          ImageLoader.getImage(ImageLoader.CALL_BUTTON_PRESSED_BG));

  private SIPCommButton hangupButton =
      new SIPCommButton(
          ImageLoader.getImage(ImageLoader.HANGUP_BUTTON_BG),
          ImageLoader.getImage(ImageLoader.HANGUP_ROLLOVER_BUTTON_BG),
          null,
          ImageLoader.getImage(ImageLoader.HANGUP_BUTTON_PRESSED_BG));

  private SIPCommButton minimizeButton =
      new SIPCommButton(
          ImageLoader.getImage(ImageLoader.CALL_PANEL_MINIMIZE_BUTTON),
          ImageLoader.getImage(ImageLoader.CALL_PANEL_MINIMIZE_ROLLOVER_BUTTON));

  private SIPCommButton restoreButton =
      new SIPCommButton(
          ImageLoader.getImage(ImageLoader.CALL_PANEL_RESTORE_BUTTON),
          ImageLoader.getImage(ImageLoader.CALL_PANEL_RESTORE_ROLLOVER_BUTTON));

  private JPanel minimizeButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));

  private MainFrame mainFrame;

  private Hashtable activeCalls = new Hashtable();

  private boolean isCallMetaContact;

  private Hashtable removeCallTimers = new Hashtable();

  private ProtocolProviderService selectedCallProvider;

  /**
   * Creates an instance of <tt>CallManager</tt>.
   *
   * @param mainFrame The main application window.
   */
  public CallManager(MainFrame mainFrame) {
    super(new BorderLayout());

    this.mainFrame = mainFrame;

    this.phoneNumberCombo = new CallComboBox(this);

    this.accountSelectorBox = new AccountSelectorBox(this);

    this.buttonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));

    this.comboPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 0, 5));

    this.init();
  }

  /** Initializes and constructs this panel. */
  private void init() {
    this.phoneNumberCombo.setEditable(true);

    this.callViaPanel.add(callViaLabel);
    this.callViaPanel.add(accountSelectorBox);

    this.comboPanel.add(phoneNumberCombo, BorderLayout.CENTER);

    this.callButton.setName("call");
    this.hangupButton.setName("hangup");
    this.minimizeButton.setName("minimize");
    this.restoreButton.setName("restore");

    this.minimizeButton.setToolTipText(
        Messages.getI18NString("hideCallPanel").getText() + " Ctrl - H");
    this.restoreButton.setToolTipText(
        Messages.getI18NString("showCallPanel").getText() + " Ctrl - H");

    this.callButton.addActionListener(this);
    this.hangupButton.addActionListener(this);
    this.minimizeButton.addActionListener(this);
    this.restoreButton.addActionListener(this);

    this.buttonsPanel.add(callButton);
    this.buttonsPanel.add(hangupButton);

    this.callButton.setEnabled(false);

    this.hangupButton.setEnabled(false);

    this.add(minimizeButtonPanel, BorderLayout.SOUTH);

    this.setCallPanelVisible(ConfigurationManager.isCallPanelShown());
  }

  /**
   * Handles the <tt>ActionEvent</tt> generated when user presses one of the buttons in this panel.
   */
  public void actionPerformed(ActionEvent evt) {
    JButton button = (JButton) evt.getSource();
    String buttonName = button.getName();

    if (buttonName.equals("call")) {
      Component selectedPanel = mainFrame.getSelectedTab();

      // call button is pressed over an already open call panel
      if (selectedPanel != null
          && selectedPanel instanceof CallPanel
          && ((CallPanel) selectedPanel).getCall().getCallState()
              == CallState.CALL_INITIALIZATION) {

        NotificationManager.stopSound(NotificationManager.BUSY_CALL);
        NotificationManager.stopSound(NotificationManager.INCOMING_CALL);

        CallPanel callPanel = (CallPanel) selectedPanel;

        Iterator participantPanels = callPanel.getParticipantsPanels();

        while (participantPanels.hasNext()) {
          CallParticipantPanel panel = (CallParticipantPanel) participantPanels.next();

          panel.setState("Connecting");
        }

        Call call = callPanel.getCall();

        answerCall(call);
      }
      // call button is pressed over the call list
      else if (selectedPanel != null
          && selectedPanel instanceof CallListPanel
          && ((CallListPanel) selectedPanel).getCallList().getSelectedIndex() != -1) {

        CallListPanel callListPanel = (CallListPanel) selectedPanel;

        GuiCallParticipantRecord callRecord =
            (GuiCallParticipantRecord) callListPanel.getCallList().getSelectedValue();

        String stringContact = callRecord.getParticipantName();

        createCall(stringContact);
      }
      // call button is pressed over the contact list
      else if (selectedPanel != null && selectedPanel instanceof ContactListPanel) {
        // call button is pressed when a meta contact is selected
        if (isCallMetaContact) {
          Object[] selectedContacts =
              mainFrame.getContactListPanel().getContactList().getSelectedValues();

          Vector telephonyContacts = new Vector();

          for (int i = 0; i < selectedContacts.length; i++) {

            Object o = selectedContacts[i];

            if (o instanceof MetaContact) {

              Contact contact =
                  ((MetaContact) o).getDefaultContact(OperationSetBasicTelephony.class);

              if (contact != null) telephonyContacts.add(contact);
              else {
                new ErrorDialog(
                        this.mainFrame,
                        Messages.getI18NString("warning").getText(),
                        Messages.getI18NString(
                                "contactNotSupportingTelephony",
                                new String[] {((MetaContact) o).getDisplayName()})
                            .getText())
                    .showDialog();
              }
            }
          }

          if (telephonyContacts.size() > 0) createCall(telephonyContacts);

        } else if (!phoneNumberCombo.isComboFieldEmpty()) {

          // if no contact is selected checks if the user has chosen
          // or has
          // writen something in the phone combo box

          String stringContact = phoneNumberCombo.getEditor().getItem().toString();

          createCall(stringContact);
        }
      } else if (selectedPanel != null && selectedPanel instanceof DialPanel) {
        String stringContact = phoneNumberCombo.getEditor().getItem().toString();
        createCall(stringContact);
      }
    } else if (buttonName.equalsIgnoreCase("hangup")) {
      Component selectedPanel = this.mainFrame.getSelectedTab();

      if (selectedPanel != null && selectedPanel instanceof CallPanel) {

        NotificationManager.stopSound(NotificationManager.BUSY_CALL);
        NotificationManager.stopSound(NotificationManager.INCOMING_CALL);
        NotificationManager.stopSound(NotificationManager.OUTGOING_CALL);

        CallPanel callPanel = (CallPanel) selectedPanel;

        Call call = callPanel.getCall();

        if (removeCallTimers.containsKey(callPanel)) {
          ((Timer) removeCallTimers.get(callPanel)).stop();
          removeCallTimers.remove(callPanel);
        }

        removeCallPanel(callPanel);

        if (call != null) {
          ProtocolProviderService pps = call.getProtocolProvider();

          OperationSetBasicTelephony telephony = mainFrame.getTelephonyOpSet(pps);

          Iterator participants = call.getCallParticipants();

          while (participants.hasNext()) {
            try {
              // now we hang up the first call participant in the
              // call
              telephony.hangupCallParticipant((CallParticipant) participants.next());
            } catch (OperationFailedException e) {
              logger.error("Hang up was not successful: " + e);
            }
          }
        }
      }
    } else if (buttonName.equalsIgnoreCase("minimize")) {
      JCheckBoxMenuItem hideCallPanelItem =
          mainFrame.getMainMenu().getViewMenu().getHideCallPanelItem();

      if (!hideCallPanelItem.isSelected()) hideCallPanelItem.setSelected(true);

      this.setCallPanelVisible(false);
    } else if (buttonName.equalsIgnoreCase("restore")) {

      JCheckBoxMenuItem hideCallPanelItem =
          mainFrame.getMainMenu().getViewMenu().getHideCallPanelItem();

      if (hideCallPanelItem.isSelected()) hideCallPanelItem.setSelected(false);

      this.setCallPanelVisible(true);
    }
  }

  /** Hides the panel containing call and hangup buttons. */
  public void setCallPanelVisible(boolean isVisible) {
    if (isVisible) {
      this.add(comboPanel, BorderLayout.NORTH);
      this.add(buttonsPanel, BorderLayout.CENTER);

      this.minimizeButtonPanel.removeAll();
      this.minimizeButtonPanel.add(minimizeButton);
    } else {
      this.remove(comboPanel);
      this.remove(buttonsPanel);

      this.minimizeButtonPanel.removeAll();
      this.minimizeButtonPanel.add(restoreButton);

      if (mainFrame.isVisible())
        this.mainFrame.getContactListPanel().getContactList().requestFocus();
    }

    if (ConfigurationManager.isCallPanelShown() != isVisible)
      ConfigurationManager.setShowCallPanel(isVisible);

    this.mainFrame.validate();
  }

  /**
   * Adds the given call account to the list of call via accounts.
   *
   * @param pps the protocol provider service corresponding to the account
   */
  public void addCallAccount(ProtocolProviderService pps) {
    if (accountSelectorBox.getAccountsNumber() > 0) {
      this.comboPanel.add(callViaPanel, BorderLayout.SOUTH);
    }
    accountSelectorBox.addAccount(pps);
  }

  /**
   * Removes the account corresponding to the given protocol provider from the call via selector
   * box.
   *
   * @param pps the protocol provider service to remove
   */
  public void removeCallAccount(ProtocolProviderService pps) {
    this.accountSelectorBox.removeAccount(pps);

    if (accountSelectorBox.getAccountsNumber() < 2) {
      this.comboPanel.remove(callViaPanel);
    }
  }

  /**
   * Returns TRUE if the account corresponding to the given protocol provider is already contained
   * in the call via selector box, otherwise returns FALSE.
   *
   * @param pps the protocol provider service for the account
   * @return TRUE if the account corresponding to the given protocol provider is already contained
   *     in the call via selector box, otherwise returns FALSE
   */
  public boolean containsCallAccount(ProtocolProviderService pps) {
    return accountSelectorBox.containsAccount(pps);
  }

  /**
   * Updates the call via account status.
   *
   * @param pps the protocol provider service for the account
   */
  public void updateCallAccountStatus(ProtocolProviderService pps) {
    accountSelectorBox.updateAccountStatus(pps);
  }

  /**
   * Returns the account selector box.
   *
   * @return the account selector box.
   */
  public AccountSelectorBox getAccountSelectorBox() {
    return accountSelectorBox;
  }

  /**
   * Sets the protocol provider to use for a call.
   *
   * @param provider the protocol provider to use for a call.
   */
  public void setCallProvider(ProtocolProviderService provider) {
    this.selectedCallProvider = provider;
  }

  /**
   * Implements CallListener.incomingCallReceived. When a call is received creates a call panel and
   * adds it to the main tabbed pane and plays the ring phone sound to the user.
   */
  public void incomingCallReceived(CallEvent event) {
    Call sourceCall = event.getSourceCall();

    CallPanel callPanel = new CallPanel(this, sourceCall, GuiCallParticipantRecord.INCOMING_CALL);

    mainFrame.addCallPanel(callPanel);

    if (mainFrame.getState() == JFrame.ICONIFIED) mainFrame.setState(JFrame.NORMAL);

    if (!mainFrame.isVisible()) mainFrame.setVisible(true);

    mainFrame.toFront();

    this.callButton.setEnabled(true);
    this.hangupButton.setEnabled(true);

    NotificationManager.fireNotification(
        NotificationManager.INCOMING_CALL,
        null,
        "Incoming call recived from: " + sourceCall.getCallParticipants().next());

    activeCalls.put(sourceCall, callPanel);

    this.setCallPanelVisible(true);
  }

  /**
   * Implements CallListener.callEnded. Stops sounds that are playing at the moment if there're any.
   * Removes the call panel and disables the hangup button.
   */
  public void callEnded(CallEvent event) {
    Call sourceCall = event.getSourceCall();

    NotificationManager.stopSound(NotificationManager.BUSY_CALL);
    NotificationManager.stopSound(NotificationManager.INCOMING_CALL);
    NotificationManager.stopSound(NotificationManager.OUTGOING_CALL);

    if (activeCalls.get(sourceCall) != null) {
      CallPanel callPanel = (CallPanel) activeCalls.get(sourceCall);

      this.removeCallPanelWait(callPanel);
    }
  }

  public void outgoingCallCreated(CallEvent event) {}

  /**
   * Removes the given call panel tab.
   *
   * @param callPanel the CallPanel to remove
   */
  public void removeCallPanelWait(CallPanel callPanel) {
    Timer timer = new Timer(5000, new RemoveCallPanelListener(callPanel));

    this.removeCallTimers.put(callPanel, timer);

    timer.setRepeats(false);
    timer.start();
  }

  /**
   * Removes the given call panel tab.
   *
   * @param callPanel the CallPanel to remove
   */
  private void removeCallPanel(CallPanel callPanel) {
    if (callPanel.getCall() != null && activeCalls.contains(callPanel.getCall())) {
      this.activeCalls.remove(callPanel.getCall());
    }

    mainFrame.removeCallPanel(callPanel);
    updateButtonsStateAccordingToSelectedPanel();
  }

  /** Removes the given CallPanel from the main tabbed pane. */
  private class RemoveCallPanelListener implements ActionListener {
    private CallPanel callPanel;

    public RemoveCallPanelListener(CallPanel callPanel) {
      this.callPanel = callPanel;
    }

    public void actionPerformed(ActionEvent e) {
      removeCallPanel(callPanel);
    }
  }

  /**
   * Implements ListSelectionListener.valueChanged. Enables or disables call and hangup buttons
   * depending on the selection in the contactlist.
   */
  public void valueChanged(ListSelectionEvent e) {
    Object o = mainFrame.getContactListPanel().getContactList().getSelectedValue();

    if ((e.getFirstIndex() != -1 || e.getLastIndex() != -1) && (o instanceof MetaContact)) {
      setCallMetaContact(true);

      // Switch automatically to the appropriate pps in account selector
      // box and enable callButton if telephony is supported.
      Contact contact = ((MetaContact) o).getDefaultContact(OperationSetBasicTelephony.class);

      if (contact != null) {
        callButton.setEnabled(true);

        if (contact.getProtocolProvider().isRegistered())
          getAccountSelectorBox().setSelected(contact.getProtocolProvider());
      } else {
        callButton.setEnabled(false);
      }
    } else if (phoneNumberCombo.isComboFieldEmpty()) {
      callButton.setEnabled(false);
    }
  }

  /**
   * Implements ChangeListener.stateChanged. Enables the hangup button if ones selects a tab in the
   * main tabbed pane that contains a call panel.
   */
  public void stateChanged(ChangeEvent e) {
    this.updateButtonsStateAccordingToSelectedPanel();

    Component selectedPanel = mainFrame.getSelectedTab();
    if (selectedPanel == null || !(selectedPanel instanceof CallPanel)) {
      Iterator callPanels = activeCalls.values().iterator();

      while (callPanels.hasNext()) {
        CallPanel callPanel = (CallPanel) callPanels.next();

        callPanel.removeDialogs();
      }
    }
  }

  /** Updates call and hangup buttons' states aa */
  private void updateButtonsStateAccordingToSelectedPanel() {
    Component selectedPanel = mainFrame.getSelectedTab();
    if (selectedPanel != null && selectedPanel instanceof CallPanel) {
      this.hangupButton.setEnabled(true);
    } else {
      this.hangupButton.setEnabled(false);
    }
  }

  /**
   * Returns the call button.
   *
   * @return the call button
   */
  public SIPCommButton getCallButton() {
    return callButton;
  }

  /**
   * Returns the hangup button.
   *
   * @return the hangup button
   */
  public SIPCommButton getHangupButton() {
    return hangupButton;
  }

  /**
   * Returns the main application frame. Meant to be used from the contained components that do not
   * have direct access to the MainFrame.
   *
   * @return the main application frame
   */
  public MainFrame getMainFrame() {
    return mainFrame;
  }

  /**
   * Returns the combo box, where user enters the phone number to call to.
   *
   * @return the combo box, where user enters the phone number to call to.
   */
  public JComboBox getCallComboBox() {
    return phoneNumberCombo;
  }

  /**
   * Answers the given call.
   *
   * @param call the call to answer
   */
  public void answerCall(Call call) {
    new AnswerCallThread(call).start();
  }

  /**
   * Returns TRUE if this call is a call to an internal meta contact from the contact list,
   * otherwise returns FALSE.
   *
   * @return TRUE if this call is a call to an internal meta contact from the contact list,
   *     otherwise returns FALSE
   */
  public boolean isCallMetaContact() {
    return isCallMetaContact;
  }

  /**
   * Sets the isCallMetaContact variable to TRUE or FALSE. This defines if this call is a call to a
   * given meta contact selected from the contact list or a call to an external contact or phone
   * number.
   *
   * @param isCallMetaContact TRUE to define this call as a call to an internal meta contact and
   *     FALSE to define it as a call to an external contact or phone number.
   */
  public void setCallMetaContact(boolean isCallMetaContact) {
    this.isCallMetaContact = isCallMetaContact;
  }

  /**
   * Creates a call to the contact represented by the given string.
   *
   * @param contact the contact to call to
   */
  public void createCall(String contact) {
    CallPanel callPanel = new CallPanel(this, contact);

    mainFrame.addCallPanel(callPanel);

    new CreateCallThread(contact, callPanel).start();
  }

  /**
   * Creates a call to the given list of contacts.
   *
   * @param contacts the list of contacts to call to
   */
  public void createCall(Vector contacts) {
    CallPanel callPanel = new CallPanel(this, contacts);

    mainFrame.addCallPanel(callPanel);

    new CreateCallThread(contacts, callPanel).start();
  }

  /** Creates a call from a given Contact or a given String. */
  private class CreateCallThread extends Thread {
    Vector contacts;

    CallPanel callPanel;

    String stringContact;

    OperationSetBasicTelephony telephony;

    public CreateCallThread(String contact, CallPanel callPanel) {
      this.stringContact = contact;
      this.callPanel = callPanel;

      if (selectedCallProvider != null)
        telephony = mainFrame.getTelephonyOpSet(selectedCallProvider);
    }

    public CreateCallThread(Vector contacts, CallPanel callPanel) {
      this.contacts = contacts;
      this.callPanel = callPanel;

      if (selectedCallProvider != null)
        telephony = mainFrame.getTelephonyOpSet(selectedCallProvider);
    }

    public void run() {
      if (telephony == null) return;

      Call createdCall = null;

      if (contacts != null) {
        Contact contact = (Contact) contacts.get(0);

        // NOTE: The multi user call is not yet implemented!
        // We just get the first contact and create a call for him.
        try {
          createdCall = telephony.createCall(contact);
        } catch (OperationFailedException e) {
          logger.error("The call could not be created: " + e);

          callPanel.getParticipantPanel(contact.getDisplayName()).setState(e.getMessage());

          removeCallPanelWait(callPanel);
        }

        // If the call is successfully created we set the created
        // Call instance to the already existing CallPanel and we
        // add this call to the active calls.
        if (createdCall != null) {
          callPanel.setCall(createdCall, GuiCallParticipantRecord.OUTGOING_CALL);

          activeCalls.put(createdCall, callPanel);
        }
      } else {
        try {
          createdCall = telephony.createCall(stringContact);
        } catch (ParseException e) {
          logger.error("The call could not be created: " + e);

          callPanel.getParticipantPanel(stringContact).setState(e.getMessage());

          removeCallPanelWait(callPanel);
        } catch (OperationFailedException e) {
          logger.error("The call could not be created: " + e);

          callPanel.getParticipantPanel(stringContact).setState(e.getMessage());

          removeCallPanelWait(callPanel);
        }

        // If the call is successfully created we set the created
        // Call instance to the already existing CallPanel and we
        // add this call to the active calls.
        if (createdCall != null) {
          callPanel.setCall(createdCall, GuiCallParticipantRecord.OUTGOING_CALL);

          activeCalls.put(createdCall, callPanel);
        }
      }
    }
  }

  /** Answers all call participants in the given call. */
  private class AnswerCallThread extends Thread {
    private Call call;

    public AnswerCallThread(Call call) {
      this.call = call;
    }

    public void run() {
      ProtocolProviderService pps = call.getProtocolProvider();

      Iterator participants = call.getCallParticipants();

      while (participants.hasNext()) {
        CallParticipant participant = (CallParticipant) participants.next();

        OperationSetBasicTelephony telephony = mainFrame.getTelephonyOpSet(pps);

        try {
          telephony.answerCallParticipant(participant);
        } catch (OperationFailedException e) {
          logger.error(
              "Could not answer to : " + participant + " caused by the following exception: " + e);
        }
      }
    }
  }
}
/**
 * The <tt>FileTransferConversationComponent</tt> is the parent of all file conversation components
 * - for incoming, outgoing and history file transfers.
 *
 * @author Yana Stamcheva
 * @author Adam Netocny
 */
public abstract class FileTransferConversationComponent extends ChatConversationComponent
    implements ActionListener, FileTransferProgressListener, Skinnable {
  /** The logger for this class. */
  private final Logger logger = Logger.getLogger(FileTransferConversationComponent.class);

  /** Image default width. */
  protected static final int IMAGE_WIDTH = 64;

  /** Image default height. */
  protected static final int IMAGE_HEIGHT = 64;

  /** The image label. */
  protected final FileImageLabel imageLabel;

  /** The title label. */
  protected final JLabel titleLabel = new JLabel();

  /** The file label. */
  protected final JLabel fileLabel = new JLabel();

  /** The error area. */
  private final JTextArea errorArea = new JTextArea();

  /** The error icon label. */
  private final JLabel errorIconLabel =
      new JLabel(new ImageIcon(ImageLoader.getImage(ImageLoader.EXCLAMATION_MARK)));

  /** The cancel button. */
  protected final ChatConversationButton cancelButton = new ChatConversationButton();

  /** The retry button. */
  protected final ChatConversationButton retryButton = new ChatConversationButton();

  /** The accept button. */
  protected final ChatConversationButton acceptButton = new ChatConversationButton();

  /** The reject button. */
  protected final ChatConversationButton rejectButton = new ChatConversationButton();

  /** The open file button. */
  protected final ChatConversationButton openFileButton = new ChatConversationButton();

  /** The open folder button. */
  protected final ChatConversationButton openFolderButton = new ChatConversationButton();

  /** The progress bar. */
  protected final JProgressBar progressBar = new JProgressBar();

  /** The progress properties panel. */
  private final TransparentPanel progressPropertiesPanel =
      new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));

  /** The progress speed label. */
  private final JLabel progressSpeedLabel = new JLabel();

  /** The estimated time label. */
  private final JLabel estimatedTimeLabel = new JLabel();

  /** The download file. */
  private File downloadFile;

  /** The file transfer. */
  private FileTransfer fileTransfer;

  /** The speed calculated delay. */
  private static final int SPEED_CALCULATE_DELAY = 5000;

  /** The transferred file size. */
  private long transferredFileSize = 0;

  /** The time of the last calculated transfer speed. */
  private long lastSpeedTimestamp = 0;

  /** The last estimated time for the transfer. */
  private long lastEstimatedTimeTimestamp = 0;

  /** The number of bytes last transferred. */
  private long lastTransferredBytes = 0;

  /** The last calculated progress speed. */
  private long lastProgressSpeed;

  /** The last estimated time. */
  private long lastEstimatedTime;

  /** Creates a file conversation component. */
  public FileTransferConversationComponent() {
    imageLabel = new FileImageLabel();

    constraints.gridx = 0;
    constraints.gridy = 0;
    constraints.gridwidth = 1;
    constraints.gridheight = 4;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    constraints.insets = new Insets(5, 5, 5, 5);

    add(imageLabel, constraints);
    imageLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.DEFAULT_FILE_ICON)));

    constraints.gridx = 1;
    constraints.gridy = 0;
    constraints.gridwidth = 3;
    constraints.gridheight = 1;
    constraints.fill = GridBagConstraints.HORIZONTAL;
    constraints.weightx = 1.0;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    constraints.insets = new Insets(5, 5, 5, 5);

    add(titleLabel, constraints);
    titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD, 11f));

    constraints.gridx = 1;
    constraints.gridy = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 5, 5);

    add(fileLabel, constraints);

    constraints.gridx = 1;
    constraints.gridy = 2;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.NONE;

    add(errorIconLabel, constraints);
    errorIconLabel.setVisible(false);

    constraints.gridx = 2;
    constraints.gridy = 2;
    constraints.gridwidth = 2;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.HORIZONTAL;

    add(errorArea, constraints);
    errorArea.setForeground(new Color(resources.getColor("service.gui.ERROR_FOREGROUND")));
    setTextAreaStyle(errorArea);
    errorArea.setVisible(false);

    constraints.gridx = 1;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);

    add(retryButton, constraints);
    retryButton.setText(GuiActivator.getResources().getI18NString("service.gui.RETRY"));
    retryButton.setVisible(false);

    constraints.gridx = 1;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);

    add(cancelButton, constraints);
    cancelButton.setText(GuiActivator.getResources().getI18NString("service.gui.CANCEL"));
    cancelButton.addActionListener(this);
    cancelButton.setVisible(false);

    constraints.gridx = 2;
    constraints.gridy = 3;
    constraints.gridwidth = GridBagConstraints.RELATIVE;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.fill = GridBagConstraints.NONE;
    constraints.anchor = GridBagConstraints.EAST;
    constraints.insets = new Insets(0, 5, 0, 5);

    constraints.gridx = 3;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.fill = GridBagConstraints.NONE;
    constraints.anchor = GridBagConstraints.LINE_END;
    constraints.insets = new Insets(0, 5, 0, 5);

    add(progressPropertiesPanel, constraints);

    estimatedTimeLabel.setFont(estimatedTimeLabel.getFont().deriveFont(11f));
    estimatedTimeLabel.setVisible(false);
    progressSpeedLabel.setFont(progressSpeedLabel.getFont().deriveFont(11f));
    progressSpeedLabel.setVisible(false);

    progressPropertiesPanel.add(progressSpeedLabel);
    progressPropertiesPanel.add(estimatedTimeLabel);

    constraints.gridx = 1;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.NONE;

    add(acceptButton, constraints);
    acceptButton.setText(GuiActivator.getResources().getI18NString("service.gui.ACCEPT"));
    acceptButton.setVisible(false);

    constraints.gridx = 2;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.NONE;

    add(rejectButton, constraints);
    rejectButton.setText(GuiActivator.getResources().getI18NString("service.gui.REJECT"));
    rejectButton.setVisible(false);

    constraints.gridx = 1;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.NONE;

    add(openFileButton, constraints);
    openFileButton.setText(GuiActivator.getResources().getI18NString("service.gui.OPEN"));
    openFileButton.setVisible(false);
    openFileButton.addActionListener(this);

    constraints.gridx = 2;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.NONE;

    add(openFolderButton, constraints);
    openFolderButton.setText(GuiActivator.getResources().getI18NString("service.gui.OPEN_FOLDER"));
    openFolderButton.setVisible(false);
    openFolderButton.addActionListener(this);

    constraints.gridx = 1;
    constraints.gridy = 2;
    constraints.gridwidth = 3;
    constraints.gridheight = 1;
    constraints.weightx = 1.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.ipadx = 150;
    constraints.fill = GridBagConstraints.HORIZONTAL;

    add(progressBar, constraints);
    progressBar.setVisible(false);
    progressBar.setStringPainted(true);
  }

  /**
   * Sets a custom style for the given text area.
   *
   * @param textArea the text area to style
   */
  private void setTextAreaStyle(JTextArea textArea) {
    textArea.setOpaque(false);
    textArea.setLineWrap(true);
    textArea.setWrapStyleWord(true);
  }

  /**
   * Shows the given error message in the error area of this component.
   *
   * @param message the message to show
   */
  protected void showErrorMessage(String message) {
    errorArea.setText(message);
    errorIconLabel.setVisible(true);
    errorArea.setVisible(true);
  }

  /**
   * Sets the download file.
   *
   * @param file the file that has been downloaded or sent
   */
  protected void setCompletedDownloadFile(File file) {
    this.downloadFile = file;

    imageLabel.setFile(downloadFile);

    imageLabel.setToolTipText(resources.getI18NString("service.gui.OPEN_FILE_FROM_IMAGE"));

    imageLabel.addMouseListener(
        new MouseAdapter() {
          public void mouseClicked(MouseEvent e) {
            if (e.getClickCount() > 1) {
              openFile(downloadFile);
            }
          }
        });
  }

  /**
   * Sets the file transfer.
   *
   * @param fileTransfer the file transfer
   * @param transferredFileSize the size of the transferred file
   */
  protected void setFileTransfer(FileTransfer fileTransfer, long transferredFileSize) {
    this.fileTransfer = fileTransfer;
    this.transferredFileSize = transferredFileSize;

    fileTransfer.addProgressListener(this);
  }

  /**
   * Handles buttons action events.
   *
   * @param evt the <tt>ActionEvent</tt> that notified us
   */
  public void actionPerformed(ActionEvent evt) {
    JButton sourceButton = (JButton) evt.getSource();

    if (sourceButton.equals(openFileButton)) {
      this.openFile(downloadFile);
    } else if (sourceButton.equals(openFolderButton)) {
      try {
        File downloadDir = GuiActivator.getFileAccessService().getDefaultDownloadDirectory();

        GuiActivator.getDesktopService().open(downloadDir);
      } catch (IllegalArgumentException e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_DOES_NOT_EXIST"));
      } catch (NullPointerException e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_DOES_NOT_EXIST"));
      } catch (UnsupportedOperationException e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FILE_OPEN_NOT_SUPPORTED"));
      } catch (SecurityException e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_NO_PERMISSION"));
      } catch (IOException e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open folder.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_NO_APPLICATION"));
      } catch (Exception e) {
        if (logger.isDebugEnabled()) logger.debug("Unable to open file.", e);

        this.showErrorMessage(resources.getI18NString("service.gui.FOLDER_OPEN_FAILED"));
      }
    } else if (sourceButton.equals(cancelButton)) {
      if (fileTransfer != null) fileTransfer.cancel();
    }
  }

  /**
   * Updates progress bar progress line every time a progress event has been received.
   *
   * @param event the <tt>FileTransferProgressEvent</tt> that notified us
   */
  public void progressChanged(FileTransferProgressEvent event) {
    progressBar.setValue((int) event.getProgress());

    long transferredBytes = event.getFileTransfer().getTransferedBytes();
    long progressTimestamp = event.getTimestamp();

    ByteFormat format = new ByteFormat();
    String bytesString = format.format(transferredBytes);

    if ((progressTimestamp - lastSpeedTimestamp) >= SPEED_CALCULATE_DELAY) {
      lastProgressSpeed = Math.round(calculateProgressSpeed(transferredBytes));

      this.lastSpeedTimestamp = progressTimestamp;
      this.lastTransferredBytes = transferredBytes;
    }

    if ((progressTimestamp - lastEstimatedTimeTimestamp) >= SPEED_CALCULATE_DELAY
        && lastProgressSpeed > 0) {
      lastEstimatedTime =
          Math.round(
              calculateEstimatedTransferTime(
                  lastProgressSpeed, transferredFileSize - transferredBytes));

      lastEstimatedTimeTimestamp = progressTimestamp;
    }

    progressBar.setString(getProgressLabel(bytesString));

    if (lastProgressSpeed > 0) {
      progressSpeedLabel.setText(
          resources.getI18NString("service.gui.SPEED") + format.format(lastProgressSpeed) + "/sec");
      progressSpeedLabel.setVisible(true);
    }

    if (lastEstimatedTime > 0) {
      estimatedTimeLabel.setText(
          resources.getI18NString("service.gui.ESTIMATED_TIME")
              + GuiUtils.formatSeconds(lastEstimatedTime * 1000));
      estimatedTimeLabel.setVisible(true);
    }
  }

  /**
   * Returns the string, showing information for the given file.
   *
   * @param file the file
   * @return the name of the given file
   */
  protected String getFileLabel(File file) {
    String fileName = file.getName();
    long fileSize = file.length();

    ByteFormat format = new ByteFormat();
    String text = format.format(fileSize);

    return fileName + " (" + text + ")";
  }

  /**
   * Returns the string, showing information for the given file.
   *
   * @param fileName the name of the file
   * @param fileSize the size of the file
   * @return the name of the given file
   */
  protected String getFileLabel(String fileName, long fileSize) {
    ByteFormat format = new ByteFormat();
    String text = format.format(fileSize);

    return fileName + " (" + text + ")";
  }

  /** Hides all progress related components. */
  protected void hideProgressRelatedComponents() {
    progressBar.setVisible(false);
    progressSpeedLabel.setVisible(false);
    estimatedTimeLabel.setVisible(false);
  }

  /**
   * Returns the label to show on the progress bar.
   *
   * @param bytesString the bytes that have been transfered
   * @return the label to show on the progress bar
   */
  protected abstract String getProgressLabel(String bytesString);

  /**
   * Returns the speed of the transfer.
   *
   * @param transferredBytes the number of bytes that have been transferred
   * @return the speed of the transfer
   */
  private double calculateProgressSpeed(long transferredBytes) {
    // Bytes per second = bytes / SPEED_CALCULATE_DELAY miliseconds * 1000.
    return (transferredBytes - lastTransferredBytes) / SPEED_CALCULATE_DELAY * 1000;
  }

  /**
   * Returns the estimated transfer time left.
   *
   * @param speed the speed of the transfer
   * @param bytesLeft the size of the file
   * @return the estimated transfer time left
   */
  private double calculateEstimatedTransferTime(double speed, long bytesLeft) {
    return bytesLeft / speed;
  }

  /** Reload images and colors. */
  public void loadSkin() {
    errorIconLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.EXCLAMATION_MARK)));

    if (downloadFile != null)
      imageLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.DEFAULT_FILE_ICON)));

    errorArea.setForeground(new Color(resources.getColor("service.gui.ERROR_FOREGROUND")));
  }
}
  /** Creates a file conversation component. */
  public FileTransferConversationComponent() {
    imageLabel = new FileImageLabel();

    constraints.gridx = 0;
    constraints.gridy = 0;
    constraints.gridwidth = 1;
    constraints.gridheight = 4;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    constraints.insets = new Insets(5, 5, 5, 5);

    add(imageLabel, constraints);
    imageLabel.setIcon(new ImageIcon(ImageLoader.getImage(ImageLoader.DEFAULT_FILE_ICON)));

    constraints.gridx = 1;
    constraints.gridy = 0;
    constraints.gridwidth = 3;
    constraints.gridheight = 1;
    constraints.fill = GridBagConstraints.HORIZONTAL;
    constraints.weightx = 1.0;
    constraints.anchor = GridBagConstraints.NORTHWEST;
    constraints.insets = new Insets(5, 5, 5, 5);

    add(titleLabel, constraints);
    titleLabel.setFont(titleLabel.getFont().deriveFont(Font.BOLD, 11f));

    constraints.gridx = 1;
    constraints.gridy = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 5, 5);

    add(fileLabel, constraints);

    constraints.gridx = 1;
    constraints.gridy = 2;
    constraints.gridwidth = 1;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.NONE;

    add(errorIconLabel, constraints);
    errorIconLabel.setVisible(false);

    constraints.gridx = 2;
    constraints.gridy = 2;
    constraints.gridwidth = 2;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.HORIZONTAL;

    add(errorArea, constraints);
    errorArea.setForeground(new Color(resources.getColor("service.gui.ERROR_FOREGROUND")));
    setTextAreaStyle(errorArea);
    errorArea.setVisible(false);

    constraints.gridx = 1;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);

    add(retryButton, constraints);
    retryButton.setText(GuiActivator.getResources().getI18NString("service.gui.RETRY"));
    retryButton.setVisible(false);

    constraints.gridx = 1;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);

    add(cancelButton, constraints);
    cancelButton.setText(GuiActivator.getResources().getI18NString("service.gui.CANCEL"));
    cancelButton.addActionListener(this);
    cancelButton.setVisible(false);

    constraints.gridx = 2;
    constraints.gridy = 3;
    constraints.gridwidth = GridBagConstraints.RELATIVE;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.fill = GridBagConstraints.NONE;
    constraints.anchor = GridBagConstraints.EAST;
    constraints.insets = new Insets(0, 5, 0, 5);

    constraints.gridx = 3;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.fill = GridBagConstraints.NONE;
    constraints.anchor = GridBagConstraints.LINE_END;
    constraints.insets = new Insets(0, 5, 0, 5);

    add(progressPropertiesPanel, constraints);

    estimatedTimeLabel.setFont(estimatedTimeLabel.getFont().deriveFont(11f));
    estimatedTimeLabel.setVisible(false);
    progressSpeedLabel.setFont(progressSpeedLabel.getFont().deriveFont(11f));
    progressSpeedLabel.setVisible(false);

    progressPropertiesPanel.add(progressSpeedLabel);
    progressPropertiesPanel.add(estimatedTimeLabel);

    constraints.gridx = 1;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.NONE;

    add(acceptButton, constraints);
    acceptButton.setText(GuiActivator.getResources().getI18NString("service.gui.ACCEPT"));
    acceptButton.setVisible(false);

    constraints.gridx = 2;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.NONE;

    add(rejectButton, constraints);
    rejectButton.setText(GuiActivator.getResources().getI18NString("service.gui.REJECT"));
    rejectButton.setVisible(false);

    constraints.gridx = 1;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.NONE;

    add(openFileButton, constraints);
    openFileButton.setText(GuiActivator.getResources().getI18NString("service.gui.OPEN"));
    openFileButton.setVisible(false);
    openFileButton.addActionListener(this);

    constraints.gridx = 2;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    constraints.gridheight = 1;
    constraints.weightx = 0.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.fill = GridBagConstraints.NONE;

    add(openFolderButton, constraints);
    openFolderButton.setText(GuiActivator.getResources().getI18NString("service.gui.OPEN_FOLDER"));
    openFolderButton.setVisible(false);
    openFolderButton.addActionListener(this);

    constraints.gridx = 1;
    constraints.gridy = 2;
    constraints.gridwidth = 3;
    constraints.gridheight = 1;
    constraints.weightx = 1.0;
    constraints.anchor = GridBagConstraints.WEST;
    constraints.insets = new Insets(0, 5, 0, 5);
    constraints.ipadx = 150;
    constraints.fill = GridBagConstraints.HORIZONTAL;

    add(progressBar, constraints);
    progressBar.setVisible(false);
    progressBar.setStringPainted(true);
  }
Пример #11
0
/**
 * The <tt>MainToolBar</tt> is a <tt>JToolBar</tt> which contains buttons for file operations, like
 * save and print, for copy-paste operations, etc. It's the main toolbar in the <tt>ChatWindow</tt>.
 * It contains only <tt>ChatToolbarButton</tt>s, which have a specific background icon and rollover
 * behaviour to differentiates them from normal buttons.
 *
 * @author Yana Stamcheva
 * @author Lubomir Marinov
 * @author Adam Netocny
 */
public class MainToolBar extends TransparentPanel
    implements ActionListener,
        ChatChangeListener,
        ChatSessionChangeListener,
        ChatRoomLocalUserRoleListener,
        Skinnable {
  /** Serial version UID. */
  private static final long serialVersionUID = -5572510509556499465L;

  /** The invite button. */
  private final ChatToolbarButton inviteButton =
      new ChatToolbarButton(ImageLoader.getImage(ImageLoader.ADD_TO_CHAT_ICON));

  /** The history button. */
  private final HistorySelectorBox historyButton;

  /** The send file button. */
  private final ChatToolbarButton sendFileButton =
      new ChatToolbarButton(ImageLoader.getImage(ImageLoader.SEND_FILE_ICON));

  /** The button showing the previous page of the chat history. */
  private final ChatToolbarButton previousButton =
      new ChatToolbarButton(ImageLoader.getImage(ImageLoader.PREVIOUS_ICON));

  /** The button showing the next page from chat history. */
  private final ChatToolbarButton nextButton =
      new ChatToolbarButton(ImageLoader.getImage(ImageLoader.NEXT_ICON));

  /** The leave chat room button. */
  private final ChatToolbarButton leaveChatRoomButton =
      new ChatToolbarButton(ImageLoader.getImage(ImageLoader.LEAVE_ICON));

  /** The call button. */
  private final ChatToolbarButton callButton =
      new ChatToolbarButton(ImageLoader.getImage(ImageLoader.CHAT_CALL));

  /** The call button. */
  private final ChatToolbarButton callVideoButton =
      new ChatToolbarButton(ImageLoader.getImage(ImageLoader.CHAT_VIDEO_CALL));

  /** The options button. */
  private final ChatToolbarButton optionsButton =
      new ChatToolbarButton(ImageLoader.getImage(ImageLoader.CHAT_CONFIGURE_ICON));

  /** The desktop sharing button. */
  private final ChatToolbarButton desktopSharingButton =
      new ChatToolbarButton(ImageLoader.getImage(ImageLoader.CHAT_DESKTOP_SHARING));

  /** The font button. */
  private final ChatToolbarButton fontButton =
      new ChatToolbarButton(ImageLoader.getImage(ImageLoader.FONT_ICON));

  private SmileysSelectorBox smileysBox;

  /**
   * The current <tt>ChatSession</tt> made known to this instance by the last call to its {@link
   * #chatChanged(ChatPanel)}.
   */
  private ChatSession chatSession;

  /** The chat container, where this tool bar is added. */
  protected final ChatContainer chatContainer;

  /** The plug-in container contained in this tool bar. */
  private final PluginContainer pluginContainer;

  /** The phone util used to enable/disable buttons. */
  private MetaContactPhoneUtil contactPhoneUtil = null;

  /**
   * Creates an instance and constructs the <tt>MainToolBar</tt>.
   *
   * @param chatContainer The parent <tt>ChatWindow</tt>.
   */
  public MainToolBar(ChatContainer chatContainer) {
    this.chatContainer = chatContainer;

    historyButton = new HistorySelectorBox(chatContainer);

    init();

    pluginContainer = new PluginContainer(this, Container.CONTAINER_CHAT_TOOL_BAR);

    this.chatContainer.addChatChangeListener(this);
  }

  /** Initializes this component. */
  protected void init() {
    this.setLayout(new FlowLayout(FlowLayout.LEFT, 3, 0));
    this.setOpaque(false);

    this.add(inviteButton);

    // if we leave a chat room when we close the window
    // there is no need for this button
    if (!ConfigurationUtils.isLeaveChatRoomOnWindowCloseEnabled()) this.add(leaveChatRoomButton);

    this.add(callButton);
    this.add(callVideoButton);
    this.add(desktopSharingButton);
    this.add(sendFileButton);

    ChatPanel chatPanel = chatContainer.getCurrentChat();
    if (chatPanel == null || !(chatPanel.getChatSession() instanceof MetaContactChatSession))
      sendFileButton.setEnabled(false);

    if (chatPanel != null && chatPanel.isPrivateMessagingChat()) {
      inviteButton.setEnabled(false);
    }

    if (chatPanel == null || !(chatPanel.getChatSession() instanceof ConferenceChatSession))
      desktopSharingButton.setEnabled(false);

    this.addSeparator();

    SIPCommMenuBar historyMenuBar = new SIPCommMenuBar();
    historyMenuBar.setOpaque(false);
    historyMenuBar.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
    historyMenuBar.add(historyButton);
    this.add(historyMenuBar);

    this.add(previousButton);
    this.add(nextButton);

    // We only add the options button if the property SHOW_OPTIONS_WINDOW
    // specifies so or if it's not set.
    Boolean showOptionsProp =
        GuiActivator.getConfigurationService()
            .getBoolean(ConfigurationFrame.SHOW_OPTIONS_WINDOW_PROPERTY, false);

    if (showOptionsProp.booleanValue()) {
      this.add(optionsButton);
    }

    this.addSeparator();

    if (ConfigurationUtils.isFontSupportEnabled()) {
      this.add(fontButton);
      fontButton.setName("font");
      fontButton.setToolTipText(
          GuiActivator.getResources().getI18NString("service.gui.CHANGE_FONT"));
      fontButton.addActionListener(this);
    }

    initSmiliesSelectorBox();

    this.addSeparator();

    this.inviteButton.setName("invite");
    this.inviteButton.setToolTipText(
        GuiActivator.getResources().getI18NString("service.gui.INVITE"));

    this.leaveChatRoomButton.setName("leave");
    this.leaveChatRoomButton.setToolTipText(
        GuiActivator.getResources().getI18NString("service.gui.LEAVE"));

    setCallButtonsName();
    setCallButtonsIcons();

    this.desktopSharingButton.setName("desktop");
    this.desktopSharingButton.setToolTipText(
        GuiActivator.getResources().getI18NString("service.gui.SHARE_DESKTOP_WITH_CONTACT"));

    optionsButton.setName("options");
    optionsButton.setToolTipText(GuiActivator.getResources().getI18NString("service.gui.OPTIONS"));

    this.sendFileButton.setName("sendFile");
    this.sendFileButton.setToolTipText(
        GuiActivator.getResources().getI18NString("service.gui.SEND_FILE"));

    this.previousButton.setName("previous");
    this.previousButton.setToolTipText(
        GuiActivator.getResources().getI18NString("service.gui.PREVIOUS"));

    this.nextButton.setName("next");
    this.nextButton.setToolTipText(GuiActivator.getResources().getI18NString("service.gui.NEXT"));

    inviteButton.addActionListener(this);
    leaveChatRoomButton.addActionListener(this);
    callButton.addActionListener(this);
    callVideoButton.addActionListener(this);
    desktopSharingButton.addActionListener(this);
    optionsButton.addActionListener(this);
    sendFileButton.addActionListener(this);
    previousButton.addActionListener(this);
    nextButton.addActionListener(this);
  }

  private void initSmiliesSelectorBox() {
    this.smileysBox = new SmileysSelectorBox();

    this.smileysBox.setName("smiley");
    this.smileysBox.setToolTipText(
        GuiActivator.getResources().getI18NString("service.gui.INSERT_SMILEY") + " Ctrl-M");

    SIPCommMenuBar smileyMenuBar = new SIPCommMenuBar();
    smileyMenuBar.setOpaque(false);
    smileyMenuBar.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
    smileyMenuBar.add(smileysBox);

    this.add(smileyMenuBar);
  }

  /**
   * Runs clean-up for associated resources which need explicit disposal (e.g. listeners keeping
   * this instance alive because they were added to the model which operationally outlives this
   * instance).
   */
  public void dispose() {
    pluginContainer.dispose();

    historyButton.dispose();

    if (this.chatSession != null && this.chatSession instanceof MetaContactChatSession)
      this.chatSession.removeChatTransportChangeListener(this);

    if (this.chatSession != null && this.chatSession instanceof ConferenceChatSession)
      ((ConferenceChatSession) this.chatSession).removeLocalUserRoleListener(this);
  }

  /**
   * Implements ChatChangeListener#chatChanged(ChatPanel).
   *
   * @param chatPanel the <tt>ChatPanel</tt>, which changed
   */
  public void chatChanged(ChatPanel chatPanel) {
    if (chatPanel == null) {
      setChatSession(null);
    } else {
      MetaContact contact = GuiActivator.getUIService().getChatContact(chatPanel);

      for (PluginComponent c : pluginContainer.getPluginComponents()) c.setCurrentContact(contact);

      ChatSession chatSession = chatPanel.getChatSession();
      setChatSession(chatSession);

      leaveChatRoomButton.setEnabled(chatSession instanceof ConferenceChatSession);

      desktopSharingButton.setEnabled(!(chatSession instanceof ConferenceChatSession));

      inviteButton.setEnabled(chatPanel.findInviteChatTransport() != null);

      sendFileButton.setEnabled(chatPanel.findFileTransferChatTransport() != null);
      inviteButton.setEnabled(!chatPanel.isPrivateMessagingChat());

      if (chatSession instanceof ConferenceChatSession) {
        updateInviteContactButton();

        callButton.setVisible(false);
        callVideoButton.setVisible(false);
        callButton.setEnabled(true);
        callVideoButton.setEnabled(true);
      } else if (contact != null) {
        callButton.setVisible(true);
        callVideoButton.setVisible(true);
        new UpdateCallButtonWorker(contact).start();
      }

      changeHistoryButtonsState(chatPanel);

      setCallButtonsName();
      setCallButtonsIcons();

      currentChatTransportChanged(chatSession);
    }
  }

  /**
   * Returns list of <tt>ChatTransport</tt> (i.e. contact) that supports the specified
   * <tt>OperationSet</tt>.
   *
   * @param transports list of <tt>ChatTransport</tt>
   * @param opSetClass <tt>OperationSet</tt> to find
   * @return list of <tt>ChatTransport</tt> (i.e. contact) that supports the specified
   *     <tt>OperationSet</tt>.
   */
  private List<ChatTransport> getOperationSetForCapabilities(
      List<ChatTransport> transports, Class<? extends OperationSet> opSetClass) {
    List<ChatTransport> list = new ArrayList<ChatTransport>();

    for (ChatTransport transport : transports) {
      ProtocolProviderService protocolProvider = transport.getProtocolProvider();
      OperationSetContactCapabilities capOpSet =
          protocolProvider.getOperationSet(OperationSetContactCapabilities.class);
      OperationSetPersistentPresence presOpSet =
          protocolProvider.getOperationSet(OperationSetPersistentPresence.class);

      if (capOpSet == null) {
        list.add(transport);
      } else if (presOpSet != null) {
        Contact contact = presOpSet.findContactByID(transport.getName());

        if ((contact != null) && (capOpSet.getOperationSet(contact, opSetClass) != null)) {
          // It supports OpSet for at least one of its
          // ChatTransports
          list.add(transport);
        }
      }
    }

    return list;
  }

  /**
   * Implements ChatSessionChangeListener#currentChatTransportChanged(ChatSession).
   *
   * @param chatSession the <tt>ChatSession</tt>, which transport has changed
   */
  public void currentChatTransportChanged(ChatSession chatSession) {
    if (chatSession == null) return;

    ChatTransport currentTransport = chatSession.getCurrentChatTransport();
    Object currentDescriptor = currentTransport.getDescriptor();

    if (currentDescriptor instanceof Contact) {
      Contact contact = (Contact) currentDescriptor;

      for (PluginComponent c : pluginContainer.getPluginComponents())
        c.setCurrentContact(contact, currentTransport.getResourceName());
    }
  }

  /**
   * When a property of the chatTransport has changed.
   *
   * @param eventID the event id representing the property of the transport that has changed.
   */
  public void currentChatTransportUpdated(int eventID) {}

  /**
   * Handles the <tt>ActionEvent</tt>, when one of the tool bar buttons is clicked.
   *
   * @param e the <tt>ActionEvent</tt> that notified us
   */
  public void actionPerformed(ActionEvent e) {
    AbstractButton button = (AbstractButton) e.getSource();
    String buttonText = button.getName();

    ChatPanel chatPanel = chatContainer.getCurrentChat();

    if (buttonText.equals("previous")) {
      chatPanel.loadPreviousPageFromHistory();
    } else if (buttonText.equals("next")) {
      chatPanel.loadNextPageFromHistory();
    } else if (buttonText.equals("sendFile")) {
      SipCommFileChooser scfc =
          GenericFileDialog.create(
              null,
              "Send file...",
              SipCommFileChooser.LOAD_FILE_OPERATION,
              ConfigurationUtils.getSendFileLastDir());
      File selectedFile = scfc.getFileFromDialog();
      if (selectedFile != null) {
        ConfigurationUtils.setSendFileLastDir(selectedFile.getParent());
        chatContainer.getCurrentChat().sendFile(selectedFile);
      }
    } else if (buttonText.equals("invite")) {
      ChatInviteDialog inviteDialog = new ChatInviteDialog(chatPanel);

      inviteDialog.setVisible(true);
    } else if (buttonText.equals("leave")) {
      ChatRoomWrapper chatRoomWrapper =
          (ChatRoomWrapper) chatPanel.getChatSession().getDescriptor();
      ChatRoomWrapper leavedRoomWrapped =
          GuiActivator.getMUCService().leaveChatRoom(chatRoomWrapper);
    } else if (buttonText.equals("call")) {
      call(false, false);
    } else if (buttonText.equals("callVideo")) {
      call(true, false);
    } else if (buttonText.equals("desktop")) {
      call(true, true);
    } else if (buttonText.equals("options")) {
      GuiActivator.getUIService().getConfigurationContainer().setVisible(true);
    } else if (buttonText.equals("font")) chatPanel.showFontChooserDialog();
    else if (buttonText.equals("createConference")) {
      chatPanel.showChatConferenceDialog();
    }
  }

  /**
   * Returns the button used to show the history window.
   *
   * @return the button used to show the history window.
   */
  public HistorySelectorBox getHistoryButton() {
    return historyButton;
  }

  /**
   * Get the smileys box.
   *
   * @return the smileys box
   */
  public SmileysSelectorBox getSmileysBox() {
    return smileysBox;
  }

  /**
   * Disables/Enables history arrow buttons depending on whether the current page is the first, the
   * last page or a middle page.
   *
   * @param chatPanel the <tt>ChatPanel</tt> which has provoked the change.
   */
  public void changeHistoryButtonsState(ChatPanel chatPanel) {
    ChatConversationPanel convPanel = chatPanel.getChatConversationPanel();

    long firstMsgInHistory = chatPanel.getFirstHistoryMsgTimestamp().getTime();
    long lastMsgInHistory = chatPanel.getLastHistoryMsgTimestamp().getTime();
    Date firstMsgInPage = convPanel.getPageFirstMsgTimestamp();
    Date lastMsgInPage = convPanel.getPageLastMsgTimestamp();

    if (firstMsgInHistory == 0 || lastMsgInHistory == 0) {
      previousButton.setEnabled(false);
      nextButton.setEnabled(false);
      return;
    }

    previousButton.setEnabled(firstMsgInHistory < firstMsgInPage.getTime());

    nextButton.setEnabled(
        (lastMsgInPage.getTime() > 0) && (lastMsgInHistory > lastMsgInPage.getTime()));
  }

  /**
   * Sets the current <tt>ChatSession</tt> made known to this instance by the last call to its
   * {@link #chatChanged(ChatPanel)}.
   *
   * @param chatSession the <tt>ChatSession</tt> to become current for this instance
   */
  private void setChatSession(ChatSession chatSession) {
    if (this.chatSession != chatSession) {
      if (this.chatSession instanceof MetaContactChatSession)
        this.chatSession.removeChatTransportChangeListener(this);

      if (this.chatSession instanceof ConferenceChatSession)
        ((ConferenceChatSession) this.chatSession).removeLocalUserRoleListener(this);

      this.chatSession = chatSession;

      if (this.chatSession instanceof MetaContactChatSession)
        this.chatSession.addChatTransportChangeListener(this);

      if (this.chatSession instanceof ConferenceChatSession)
        ((ConferenceChatSession) this.chatSession).addLocalUserRoleListener(this);
    }
  }

  /** Adds a separator to this tool bar. */
  private void addSeparator() {
    this.add(new JSeparator(SwingConstants.VERTICAL));
  }

  /** Reloads icons for buttons. */
  public void loadSkin() {
    inviteButton.setIconImage(ImageLoader.getImage(ImageLoader.ADD_TO_CHAT_ICON));

    historyButton.loadSkin();

    sendFileButton.setIconImage(ImageLoader.getImage(ImageLoader.SEND_FILE_ICON));

    fontButton.setIconImage(ImageLoader.getImage(ImageLoader.FONT_ICON));

    previousButton.setIconImage(ImageLoader.getImage(ImageLoader.PREVIOUS_ICON));

    nextButton.setIconImage(ImageLoader.getImage(ImageLoader.NEXT_ICON));

    leaveChatRoomButton.setIconImage(ImageLoader.getImage(ImageLoader.LEAVE_ICON));

    desktopSharingButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_DESKTOP_SHARING));

    optionsButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_CONFIGURE_ICON));

    setCallButtonsIcons();
  }

  /**
   * Establishes a call.
   *
   * @param isVideo indicates if a video call should be established.
   * @param isDesktopSharing indicates if a desktopSharing should be established.
   */
  private void call(boolean isVideo, boolean isDesktopSharing) {
    ChatPanel chatPanel = chatContainer.getCurrentChat();

    ChatSession chatSession = chatPanel.getChatSession();

    Class<? extends OperationSet> opSetClass;
    if (isVideo) {
      if (isDesktopSharing) opSetClass = OperationSetDesktopStreaming.class;
      else opSetClass = OperationSetVideoTelephony.class;
    } else opSetClass = OperationSetBasicTelephony.class;

    List<ChatTransport> telTransports = null;
    if (chatSession != null) telTransports = chatSession.getTransportsForOperationSet(opSetClass);

    List<ChatTransport> contactOpSetSupported;

    contactOpSetSupported = getOperationSetForCapabilities(telTransports, opSetClass);

    List<UIContactDetail> res = new ArrayList<UIContactDetail>();
    for (ChatTransport ct : contactOpSetSupported) {
      HashMap<Class<? extends OperationSet>, ProtocolProviderService> m =
          new HashMap<Class<? extends OperationSet>, ProtocolProviderService>();
      m.put(opSetClass, ct.getProtocolProvider());

      UIContactDetailImpl d =
          new UIContactDetailImpl(
              ct.getName(), ct.getDisplayName(), null, null, null, m, null, ct.getName());
      PresenceStatus status = ct.getStatus();
      byte[] statusIconBytes = status.getStatusIcon();

      if (statusIconBytes != null && statusIconBytes.length > 0) {
        d.setStatusIcon(
            new ImageIcon(
                ImageLoader.getIndexedProtocolImage(
                    ImageUtils.getBytesInImage(statusIconBytes), ct.getProtocolProvider())));
      }

      res.add(d);
    }

    Point location = new Point(callButton.getX(), callButton.getY() + callButton.getHeight());

    SwingUtilities.convertPointToScreen(location, this);

    MetaContact metaContact = GuiActivator.getUIService().getChatContact(chatPanel);
    UIContactImpl uiContact = null;
    if (metaContact != null) uiContact = MetaContactListSource.getUIContact(metaContact);

    CallManager.call(res, uiContact, isVideo, isDesktopSharing, callButton, location);
  }

  /** Sets the names of the call buttons depending on the chat session type. */
  private void setCallButtonsName() {
    if (chatSession instanceof ConferenceChatSession) {
      callButton.setName("createConference");
      callVideoButton.setName("createConference");
      this.callButton.setToolTipText(
          GuiActivator.getResources().getI18NString("service.gui.CREATE_JOIN_VIDEO_CONFERENCE"));

      this.callVideoButton.setToolTipText(
          GuiActivator.getResources().getI18NString("service.gui.CREATE_JOIN_VIDEO_CONFERENCE"));
    } else {
      callButton.setName("call");
      callVideoButton.setName("callVideo");
      this.callButton.setToolTipText(
          GuiActivator.getResources().getI18NString("service.gui.CALL_CONTACT"));

      this.callVideoButton.setToolTipText(
          GuiActivator.getResources().getI18NString("service.gui.CALL_CONTACT"));
    }
  }

  /** Sets the icons of the call buttons depending on the chat session type. */
  private void setCallButtonsIcons() {
    if (chatSession instanceof ConferenceChatSession) {
      callButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_ROOM_CALL));
      callVideoButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_ROOM_VIDEO_CALL));
      callButton.setPreferredSize(new Dimension(29, 25));
      callVideoButton.setPreferredSize(new Dimension(29, 25));
    } else {
      callButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_CALL));
      callVideoButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_VIDEO_CALL));
      callButton.setPreferredSize(new Dimension(25, 25));
      callVideoButton.setPreferredSize(new Dimension(25, 25));
    }
    callButton.repaint();
    callVideoButton.repaint();
  }

  /**
   * Fired when local user role has changed.
   *
   * @param evt the <tt>ChatRoomLocalUserRoleChangeEvent</tt> instance
   */
  @Override
  public void localUserRoleChanged(ChatRoomLocalUserRoleChangeEvent evt) {
    updateInviteContactButton();
  }

  /** Updates invite contact button depending on the user role we have. */
  private void updateInviteContactButton() {
    if (chatSession instanceof ConferenceChatSession) {
      ChatRoomMemberRole role =
          ((ChatRoomWrapper) chatSession.getDescriptor()).getChatRoom().getUserRole();

      // it means we are at least a moderator
      inviteButton.setEnabled(role.getRoleIndex() >= 50);
    }
  }

  /**
   * Searches for phone numbers in <tt>MetaContact/tt> operation sets. And changes the call button
   * enable/disable state.
   */
  private class UpdateCallButtonWorker extends SwingWorker {
    /** The current contact. */
    private MetaContact contact;

    /** Has this contact any phone. */
    private boolean isCallEnabled = false;

    /** Has this contact any video phone. */
    private boolean isVideoCallEnabled = false;

    /** Has this contact has desktop sharing enabled. */
    private boolean isDesktopSharingEnabled = false;

    /**
     * Creates worker.
     *
     * @param contact
     */
    UpdateCallButtonWorker(MetaContact contact) {
      this.contact = contact;
    }

    /**
     * Executes in worker thread.
     *
     * @return
     * @throws Exception
     */
    @Override
    protected Object construct() throws Exception {
      contactPhoneUtil = MetaContactPhoneUtil.getPhoneUtil(contact);

      isCallEnabled = contactPhoneUtil.isCallEnabled();
      isVideoCallEnabled = contactPhoneUtil.isVideoCallEnabled();
      isDesktopSharingEnabled = contactPhoneUtil.isDesktopSharingEnabled();

      return null;
    }

    /**
     * Called on the event dispatching thread (not on the worker thread) after the <code>construct
     * </code> method has returned.
     */
    @Override
    protected void finished() {
      callButton.setEnabled(isCallEnabled);
      callVideoButton.setEnabled(isVideoCallEnabled);
      desktopSharingButton.setEnabled(isDesktopSharingEnabled);
    }
  }
}