/** * 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"))); }
/** 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(); }
/** 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))); } }
/** 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(); }
/** * 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(); }
/** * 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; }
/** * 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); }
/** * 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); } } }