/** * 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(); }
/** * Sets the background of the button depending on its position in the button bar. * * @param button the button which background to set * @param gridX the position of the button in the grid * @param isLast indicates if this is the last button in the button bar */ private void setButtonBg(SIPCommButton button, int gridX, boolean isLast) { if (!isLast) { if (gridX == 1) button.setBackgroundImage(ImageLoader.getImage(ImageLoader.CONTACT_LIST_BUTTON_BG_LEFT)); else if (gridX > 1) button.setBackgroundImage(ImageLoader.getImage(ImageLoader.CONTACT_LIST_BUTTON_BG_MIDDLE)); } else { if (gridX == 1) // We have only one button shown. button.setBackgroundImage(ImageLoader.getImage(ImageLoader.CONTACT_LIST_ONE_BUTTON_BG)); else // We set the background of the last button in the toolbar button.setBackgroundImage(ImageLoader.getImage(ImageLoader.CONTACT_LIST_BUTTON_BG_RIGHT)); } }
/** 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(); }
/** * Adds resources for contact. * * @param tip the tool tip * @param protocolContact the protocol contact, which resources we're looking for */ private void addContactResourceTooltipLines(ExtendedTooltip tip, Contact protocolContact) { Collection<ContactResource> contactResources = protocolContact.getResources(); if (contactResources == null) return; Iterator<ContactResource> resourcesIter = contactResources.iterator(); while (resourcesIter.hasNext()) { ContactResource contactResource = resourcesIter.next(); // We only add the status icon if we have more than one resources, // otherwise it will always be identical to the contact status icon. ImageIcon protocolStatusIcon = null; if (contactResources.size() > 1) { protocolStatusIcon = ImageLoader.getIndexedProtocolIcon( ImageUtils.getBytesInImage(contactResource.getPresenceStatus().getStatusIcon()), protocolContact.getProtocolProvider()); } String resourceName = (contactResource.getPriority() >= 0) ? contactResource.getResourceName() + " (" + contactResource.getPriority() + ")" : contactResource.getResourceName(); if (protocolStatusIcon == null) tip.addSubLine(protocolStatusIcon, resourceName, 27); else tip.addSubLine(protocolStatusIcon, resourceName, 20); } tip.revalidate(); tip.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.setIconImage(ImageLoader.getImage(ImageLoader.HISTORY_ICON)); 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)); callButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_CALL)); desktopSharingButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_DESKTOP_SHARING)); optionsButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_CONFIGURE_ICON)); }
/** * 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 = OperationSetDesktopSharingServer.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); }
/** * Creates a new <tt>JMenuItem</tt> and adds it to this <tt>JPopupMenu</tt>. * * @param textKey the key of the internationalized string in the resources of the application * which represents the text of the new <tt>JMenuItem</tt> * @param iconID the <tt>ImageID</tt> of the image in the resources of the application which * represents the icon of the new <tt>JMenuItem</tt> * @param name the name of the new <tt>JMenuItem</tt> * @return a new <tt>JMenuItem</tt> instance which has been added to this <tt>JPopupMenu</tt> */ private JMenuItem createMenuItem(String textKey, ImageID iconID, String name) { ResourceManagementService resources = GuiActivator.getResources(); JMenuItem menuItem = new JMenuItem( resources.getI18NString(textKey), new ImageIcon(ImageLoader.getImage(iconID))); menuItem.setMnemonic(resources.getI18nMnemonic(textKey)); menuItem.setName(name); menuItem.addActionListener(this); add(menuItem); return menuItem; }
/** * 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 avatar of a specific <tt>MetaContact</tt> in the form of an <tt>ImageIcon</tt> value. * * @param isSelected indicates if the contact is selected * @param width the desired icon width * @param height the desired icon height * @return an <tt>ImageIcon</tt> which represents the avatar of the specified <tt>MetaContact</tt> */ public ImageIcon getAvatar(boolean isSelected, int width, int height) { byte[] avatarBytes = metaContact.getAvatar(true); // If there's no avatar we have nothing more to do here. if ((avatarBytes == null) || (avatarBytes.length <= 0)) { if (!subscribed) { return ImageUtils.getScaledRoundedIcon( ImageLoader.getImage(ImageLoader.UNAUTHORIZED_CONTACT_PHOTO), width, height); } return null; } // If the cell is selected we return a zoomed version of the avatar // image. if (isSelected) return ImageUtils.getScaledRoundedIcon(avatarBytes, width, height); // In any other case try to get the avatar from the cache. Object[] avatarCache = (Object[]) metaContact.getData(AVATAR_DATA_KEY); ImageIcon avatar = null; if ((avatarCache != null) && (avatarCache[0] == avatarBytes)) avatar = (ImageIcon) avatarCache[1]; // If the avatar isn't available or it's not up-to-date, create it. if (avatar == null) { avatar = ImageUtils.getScaledRoundedIcon(avatarBytes, width, height); } // Cache the avatar in case it has changed. if (avatarCache == null) { if (avatar != null) metaContact.setData(AVATAR_DATA_KEY, new Object[] {avatarBytes, avatar}); } else { avatarCache[0] = avatarBytes; avatarCache[1] = avatar; } return avatar; }
/** * 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, 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 ChatToolbarButton historyButton = new ChatToolbarButton(ImageLoader.getImage(ImageLoader.HISTORY_ICON)); /** 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; 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); this.addSeparator(); this.add(historyButton); 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")); this.callButton.setName("call"); this.callButton.setToolTipText( GuiActivator.getResources().getI18NString("service.gui.CALL_CONTACT")); this.callVideoButton.setName("callVideo"); this.callVideoButton.setToolTipText( GuiActivator.getResources().getI18NString("service.gui.CALL_CONTACT")); this.desktopSharingButton.setName("desktop"); this.desktopSharingButton.setToolTipText( GuiActivator.getResources().getI18NString("service.gui.SHARE_DESKTOP_WITH_CONTACT")); this.historyButton.setName("history"); this.historyButton.setToolTipText( GuiActivator.getResources().getI18NString("service.gui.HISTORY") + " Ctrl-H"); 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); historyButton.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(); } /** * 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); setChatSession(chatPanel.chatSession); leaveChatRoomButton.setEnabled(chatPanel.chatSession instanceof ConferenceChatSession); inviteButton.setEnabled(chatPanel.findInviteChatTransport() != null); sendFileButton.setEnabled(chatPanel.findFileTransferChatTransport() != null); new UpdateCallButtonWorker(contact).start(); changeHistoryButtonsState(chatPanel); } } /** * 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); } } /** * 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("history")) { HistoryWindow history; HistoryWindowManager historyWindowManager = GuiActivator.getUIService().getHistoryWindowManager(); ChatSession chatSession = chatPanel.getChatSession(); if (historyWindowManager.containsHistoryWindowForContact(chatSession.getDescriptor())) { history = historyWindowManager.getHistoryWindowForContact(chatSession.getDescriptor()); if (history.getState() == JFrame.ICONIFIED) history.setState(JFrame.NORMAL); history.toFront(); } else { history = new HistoryWindow(chatPanel.getChatSession().getDescriptor()); history.setVisible(true); historyWindowManager.addHistoryWindowForContact(chatSession.getDescriptor(), history); } } else if (buttonText.equals("invite")) { ChatInviteDialog inviteDialog = new ChatInviteDialog(chatPanel); inviteDialog.setVisible(true); } else if (buttonText.equals("leave")) { ConferenceChatManager conferenceManager = GuiActivator.getUIService().getConferenceChatManager(); conferenceManager.leaveChatRoom((ChatRoomWrapper) chatPanel.getChatSession().getDescriptor()); } 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(); } /** * Returns the button used to show the history window. * * @return the button used to show the history window. */ public ChatToolbarButton 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); this.chatSession = chatSession; if (this.chatSession instanceof MetaContactChatSession) this.chatSession.addChatTransportChangeListener(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.setIconImage(ImageLoader.getImage(ImageLoader.HISTORY_ICON)); 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)); callButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_CALL)); desktopSharingButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_DESKTOP_SHARING)); optionsButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_CONFIGURE_ICON)); } /** * 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 = OperationSetDesktopSharingServer.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); } /** * 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); } } }
/** * 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; }
/** * Loads the tooltip with the data for current metacontact. * * @param tip the tooltip to fill. */ private void loadTooltip(final ExtendedTooltip tip) { Iterator<Contact> i = metaContact.getContacts(); ContactPhoneUtil contactPhoneUtil = ContactPhoneUtil.getPhoneUtil(metaContact); String statusMessage = null; Contact protocolContact; boolean isLoading = false; while (i.hasNext()) { protocolContact = i.next(); // Set the first found status message. if (statusMessage == null && protocolContact.getStatusMessage() != null && protocolContact.getStatusMessage().length() > 0) statusMessage = protocolContact.getStatusMessage(); if (ConfigurationUtils.isHideAccountStatusSelectorsEnabled()) break; ImageIcon protocolStatusIcon = ImageLoader.getIndexedProtocolIcon( ImageUtils.getBytesInImage(protocolContact.getPresenceStatus().getStatusIcon()), protocolContact.getProtocolProvider()); String contactAddress = protocolContact.getAddress(); // String statusMessage = protocolContact.getStatusMessage(); tip.addLine(protocolStatusIcon, contactAddress); addContactResourceTooltipLines(tip, protocolContact); if (!protocolContact.getProtocolProvider().isRegistered()) continue; contactPhoneUtil.addDetailsResponseListener( protocolContact, new OperationSetServerStoredContactInfo.DetailsResponseListener() { public void detailsRetrieved(final Iterator<GenericDetail> details) { if (!SwingUtilities.isEventDispatchThread()) { SwingUtilities.invokeLater( new Runnable() { public void run() { detailsRetrieved(details); } }); return; } // remove previously shown information // as it contains "Loading..." text tip.removeAllLines(); // load it again loadTooltip(tip); } }); List<String> phones = contactPhoneUtil.getPhones(protocolContact); if (phones != null) { addPhoneTooltipLines(tip, phones.iterator()); } else isLoading = true; } if (isLoading) tip.addLine(null, GuiActivator.getResources().getI18NString("service.gui.LOADING")); if (statusMessage != null) tip.setBottomText(statusMessage); }
/** * 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>ContactListCellRenderer</tt> is the custom cell renderer used in the Jitsi's * <tt>ContactList</tt>. It extends JPanel instead of JLabel, which allows adding different buttons * and icons to the contact cell. The cell border and background are repainted. * * @author Yana Stamcheva * @author Lubomir Marinov * @author Adam Netocny */ public class ContactListTreeCellRenderer extends JPanel implements TreeCellRenderer, Icon, Skinnable { /** Serial version UID. */ private static final long serialVersionUID = 0L; /** The default height of the avatar. */ private static final int AVATAR_HEIGHT = 30; /** The default width of the avatar. */ private static final int AVATAR_WIDTH = 30; /** The extended height of the avatar. */ private static final int EXTENDED_AVATAR_HEIGHT = 45; /** The extended width of the avatar. */ private static final int EXTENDED_AVATAR_WIDTH = 45; /** The default width of the button. */ private static final int BUTTON_WIDTH = 26; /** The default height of the button. */ private static final int BUTTON_HEIGHT = 27; /** Left border value. */ private static final int LEFT_BORDER = 5; /** Left border value. */ private static final int TOP_BORDER = 2; /** Bottom border value. */ private static final int BOTTOM_BORDER = 2; /** Right border value. */ private static final int RIGHT_BORDER = 2; /** The horizontal gap between columns in pixels; */ private static final int H_GAP = 2; /** The vertical gap between rows in pixels; */ private static final int V_GAP = 3; /** The separator image for the button toolbar. */ private static final Image BUTTON_SEPARATOR_IMG = ImageLoader.getImage(ImageLoader.CONTACT_LIST_BUTTON_SEPARATOR); /** The icon used for opened groups. */ private ImageIcon openedGroupIcon; /** The icon used for closed groups. */ private ImageIcon closedGroupIcon; /** The foreground color for groups. */ private Color groupForegroundColor; /** The foreground color for contacts. */ private Color contactForegroundColor; /** The component showing the name of the contact or group. */ private final JLabel nameLabel = new JLabel(); /** The status message label. */ private final JLabel displayDetailsLabel = new JLabel(); /** The call button. */ private final SIPCommButton callButton = new SIPCommButton(); /** The call video button. */ private final SIPCommButton callVideoButton = new SIPCommButton(); /** The desktop sharing button. */ private final SIPCommButton desktopSharingButton = new SIPCommButton(); /** The chat button. */ private final SIPCommButton chatButton = new SIPCommButton(); /** The add contact button. */ private final SIPCommButton addContactButton = new SIPCommButton(); /** The constraints used to align components in the <tt>centerPanel</tt>. */ private final GridBagConstraints constraints = new GridBagConstraints(); /** The component showing the avatar or the contact count in the case of groups. */ protected final JLabel rightLabel = new JLabel(); /** The message received image. */ private Image msgReceivedImage; /** The label containing the status icon. */ private final JLabel statusLabel = new JLabel(); /** The icon showing the contact status. */ protected Icon statusIcon = new ImageIcon(); /** Indicates if the current list cell is selected. */ protected boolean isSelected = false; /** The index of the current cell. */ protected int row = 0; /** Indicates if the current cell contains a leaf or a group. */ protected TreeNode treeNode = null; /** The parent tree. */ private TreeContactList treeContactList; /** A list of the custom action buttons for contacts UIContacts. */ private List<JButton> customActionButtons; /** A list of the custom action buttons for groups. */ private List<JButton> customActionButtonsUIGroup; /** The last added button. */ private SIPCommButton lastAddedButton; /** Initializes the panel containing the node. */ public ContactListTreeCellRenderer() { super(new GridBagLayout()); loadSkin(); this.setOpaque(true); this.nameLabel.setOpaque(false); this.displayDetailsLabel.setFont(getFont().deriveFont(9f)); this.displayDetailsLabel.setForeground(Color.GRAY); this.rightLabel.setHorizontalAlignment(JLabel.RIGHT); // !! IMPORTANT: General insets used for all components if not // overwritten! constraints.insets = new Insets(0, 0, 0, H_GAP); constraints.anchor = GridBagConstraints.WEST; constraints.fill = GridBagConstraints.NONE; constraints.gridx = 0; constraints.gridy = 0; constraints.gridheight = 1; constraints.weightx = 0f; constraints.weighty = 1f; this.add(statusLabel, constraints); addLabels(1); callButton.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { if (treeNode != null && treeNode instanceof ContactNode) { call(treeNode, callButton, false, false); } } }); callVideoButton.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { if (treeNode != null && treeNode instanceof ContactNode) { call(treeNode, callVideoButton, true, false); } } }); desktopSharingButton.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { if (treeNode != null && treeNode instanceof ContactNode) { call(treeNode, desktopSharingButton, true, true); } } }); chatButton.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { if (treeNode != null && treeNode instanceof ContactNode) { UIContact contactDescriptor = ((ContactNode) treeNode).getContactDescriptor(); if (contactDescriptor.getDescriptor() instanceof MetaContact) { GuiActivator.getUIService() .getChatWindowManager() .startChat((MetaContact) contactDescriptor.getDescriptor()); } } } }); addContactButton.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { if (treeNode != null && treeNode instanceof ContactNode) { UIContact contactDescriptor = ((ContactNode) treeNode).getContactDescriptor(); // The add contact function has only sense for external // source contacts. if (contactDescriptor instanceof SourceUIContact) { addContact((SourceUIContact) contactDescriptor); } } } }); initButtonToolTips(); this.setToolTipText(""); } /** * Returns this panel that has been configured to display the meta contact and meta contact group * cells. * * @param tree the source tree * @param value the tree node * @param selected indicates if the node is selected * @param expanded indicates if the node is expanded * @param leaf indicates if the node is a leaf * @param row indicates the row number of the node * @param hasFocus indicates if the node has the focus * @return this panel */ public Component getTreeCellRendererComponent( JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) { this.treeContactList = (TreeContactList) tree; this.row = row; this.isSelected = selected; this.treeNode = (TreeNode) value; this.rightLabel.setIcon(null); DefaultTreeContactList contactList = (DefaultTreeContactList) tree; setBorder(); addLabels(1); // Set background color. if (contactList instanceof TreeContactList) { ContactListFilter filter = ((TreeContactList) contactList).getCurrentFilter(); if (filter != null && filter.equals(TreeContactList.historyFilter) && value instanceof ContactNode && row % 2 == 0) { setBackground(Constants.CALL_HISTORY_EVEN_ROW_COLOR); } else { setBackground(Color.WHITE); } } // Make appropriate adjustments for contact nodes and group nodes. if (value instanceof ContactNode) { UIContactImpl contact = ((ContactNode) value).getContactDescriptor(); String displayName = contact.getDisplayName(); if ((displayName == null || displayName.trim().length() < 1) && !(contact instanceof ShowMoreContact)) { displayName = GuiActivator.getResources().getI18NString("service.gui.UNKNOWN"); } this.nameLabel.setText(displayName); if (statusIcon != null && contactList.isContactActive(contact) && statusIcon instanceof ImageIcon) ((ImageIcon) statusIcon).setImage(msgReceivedImage); else statusIcon = contact.getStatusIcon(); this.statusLabel.setIcon(statusIcon); this.nameLabel.setFont(this.getFont().deriveFont(Font.PLAIN)); if (contactForegroundColor != null) nameLabel.setForeground(contactForegroundColor); // Initializes status message components if the given meta contact // contains a status message. this.initDisplayDetails(contact.getDisplayDetails()); if (this.treeContactList.isContactButtonsVisible()) this.initButtonsPanel(contact); int avatarWidth, avatarHeight; if (isSelected && treeContactList.isContactButtonsVisible()) { avatarWidth = EXTENDED_AVATAR_WIDTH; avatarHeight = EXTENDED_AVATAR_HEIGHT; } else { avatarWidth = AVATAR_WIDTH; avatarHeight = AVATAR_HEIGHT; } Icon avatar = contact.getAvatar(isSelected, avatarWidth, avatarHeight); if (avatar != null) { this.rightLabel.setIcon(avatar); } if (contact instanceof ShowMoreContact) { rightLabel.setFont(rightLabel.getFont().deriveFont(12f)); rightLabel.setForeground(Color.GRAY); rightLabel.setText((String) contact.getDescriptor()); } else { rightLabel.setFont(rightLabel.getFont().deriveFont(9f)); rightLabel.setText(""); } this.setToolTipText(contact.getDescriptor().toString()); } else if (value instanceof GroupNode) { UIGroupImpl groupItem = ((GroupNode) value).getGroupDescriptor(); this.nameLabel.setText(groupItem.getDisplayName()); this.nameLabel.setFont(this.getFont().deriveFont(Font.BOLD)); if (groupForegroundColor != null) this.nameLabel.setForeground(groupForegroundColor); this.remove(displayDetailsLabel); this.remove(callButton); this.remove(callVideoButton); this.remove(desktopSharingButton); this.remove(chatButton); this.remove(addContactButton); clearCustomActionButtons(); statusIcon = expanded ? openedGroupIcon : closedGroupIcon; this.statusLabel.setIcon(expanded ? openedGroupIcon : closedGroupIcon); // We have no photo icon for groups. this.rightLabel.setIcon(null); this.rightLabel.setText(""); if (groupItem.countChildContacts() >= 0) { rightLabel.setFont(rightLabel.getFont().deriveFont(9f)); this.rightLabel.setForeground(Color.BLACK); this.rightLabel.setText( groupItem.countOnlineChildContacts() + "/" + groupItem.countChildContacts()); } this.initDisplayDetails(groupItem.getDisplayDetails()); this.initButtonsPanel(groupItem); this.setToolTipText(groupItem.getDescriptor().toString()); } return this; } /** * Paints a customized background. * * @param g the <tt>Graphics</tt> object through which we paint */ @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g = g.create(); if (!(treeNode instanceof GroupNode) && !isSelected) return; AntialiasingManager.activateAntialiasing(g); Graphics2D g2 = (Graphics2D) g; try { internalPaintComponent(g2); } finally { g.dispose(); } } /** * Paint a background for all groups and a round blue border and background when a cell is * selected. * * @param g2 the <tt>Graphics2D</tt> object through which we paint */ private void internalPaintComponent(Graphics2D g2) { Color borderColor = Color.GRAY; if (isSelected) { g2.setPaint( new GradientPaint( 0, 0, Constants.SELECTED_COLOR, 0, getHeight(), Constants.SELECTED_GRADIENT_COLOR)); borderColor = Constants.SELECTED_COLOR; } else if (treeNode instanceof GroupNode) { g2.setPaint( new GradientPaint( 0, 0, Constants.CONTACT_LIST_GROUP_BG_GRADIENT_COLOR, 0, getHeight(), Constants.CONTACT_LIST_GROUP_BG_COLOR)); borderColor = Constants.CONTACT_LIST_GROUP_BG_COLOR; } g2.fillRect(0, 0, getWidth(), getHeight()); g2.setColor(borderColor); g2.drawLine(0, 0, getWidth(), 0); g2.drawLine(0, getHeight() - 1, getWidth(), getHeight() - 1); } /** * Returns the height of this icon. Used for the drag&drop component. * * @return the height of this icon */ public int getIconHeight() { return getPreferredSize().height + 10; } /** * Returns the width of this icon. Used for the drag&drop component. * * @return the widht of this icon */ public int getIconWidth() { return treeContactList.getWidth() + 10; } /** * Returns the preferred size of this component. * * @return the preferred size of this component */ public Dimension getPreferredSize() { Dimension preferredSize = new Dimension(); int preferredHeight; if (treeNode instanceof ContactNode) { UIContact contact = ((ContactNode) treeNode).getContactDescriptor(); preferredHeight = contact.getPreferredHeight(); if (preferredHeight > 0) preferredSize.height = preferredHeight; else if (contact instanceof ShowMoreContact) preferredSize.height = 20; else if (isSelected && treeContactList.isContactButtonsVisible()) preferredSize.height = 70; else preferredSize.height = 35; } else if (treeNode instanceof GroupNode) { UIGroup group = ((GroupNode) treeNode).getGroupDescriptor(); preferredHeight = group.getPreferredHeight(); if (isSelected && customActionButtonsUIGroup != null && !customActionButtonsUIGroup.isEmpty()) preferredSize.height = 70; else if (preferredHeight > 0) preferredSize.height = preferredHeight; else preferredSize.height = 20; } return preferredSize; } /** * Adds contact entry labels. * * @param nameLabelGridWidth the grid width of the contact entry name label */ private void addLabels(int nameLabelGridWidth) { remove(nameLabel); remove(rightLabel); remove(displayDetailsLabel); if (treeNode != null && !(treeNode instanceof GroupNode)) constraints.insets = new Insets(0, 0, V_GAP, H_GAP); else constraints.insets = new Insets(0, 0, 0, H_GAP); constraints.anchor = GridBagConstraints.WEST; constraints.fill = GridBagConstraints.NONE; constraints.gridx = 1; constraints.gridy = 0; constraints.weightx = 1f; constraints.weighty = 0f; constraints.gridheight = 1; constraints.gridwidth = nameLabelGridWidth; this.add(nameLabel, constraints); constraints.anchor = GridBagConstraints.NORTHEAST; constraints.fill = GridBagConstraints.VERTICAL; constraints.gridx = nameLabelGridWidth + 1; constraints.gridy = 0; constraints.gridheight = 3; constraints.weightx = 0f; constraints.weighty = 1f; this.add(rightLabel, constraints); if (treeNode != null && treeNode instanceof ContactNode) { constraints.anchor = GridBagConstraints.WEST; constraints.fill = GridBagConstraints.NONE; constraints.gridx = 1; constraints.gridy = 1; constraints.weightx = 1f; constraints.weighty = 0f; constraints.gridwidth = nameLabelGridWidth; constraints.gridheight = 1; this.add(displayDetailsLabel, constraints); } else if (treeNode != null && treeNode instanceof GroupNode) { constraints.anchor = GridBagConstraints.WEST; constraints.fill = GridBagConstraints.NONE; constraints.gridx = 1; constraints.gridy = 1; constraints.weightx = 1f; constraints.weighty = 0f; constraints.gridwidth = nameLabelGridWidth; constraints.gridheight = 1; this.add(displayDetailsLabel, constraints); } } /** * Initializes the display details component for the given <tt>UIContact</tt>. * * @param displayDetails the display details to show */ private void initDisplayDetails(String displayDetails) { remove(displayDetailsLabel); displayDetailsLabel.setText(""); if (displayDetails != null && displayDetails.length() > 0) { // Replace all occurrences of new line with slash. displayDetails = Html2Text.extractText(displayDetails); displayDetails = displayDetails.replaceAll("\n|<br>|<br/>", " / "); displayDetailsLabel.setText(displayDetails); } constraints.anchor = GridBagConstraints.WEST; constraints.fill = GridBagConstraints.NONE; constraints.gridx = 1; constraints.gridy = 1; constraints.weightx = 1f; constraints.weighty = 0f; constraints.gridwidth = 1; constraints.gridheight = 1; this.add(displayDetailsLabel, constraints); } /** * Initializes buttons panel. * * @param uiContact the <tt>UIContact</tt> for which we initialize the button panel */ private void initButtonsPanel(UIContact uiContact) { this.remove(chatButton); this.remove(callButton); this.remove(callVideoButton); this.remove(desktopSharingButton); this.remove(addContactButton); clearCustomActionButtons(); if (!isSelected) return; UIContactDetail imContact = null; // For now we support instance messaging only for contacts in our // contact list until it's implemented for external source contacts. if (uiContact.getDescriptor() instanceof MetaContact) imContact = uiContact.getDefaultContactDetail(OperationSetBasicInstantMessaging.class); int x = (statusIcon == null ? 0 : statusIcon.getIconWidth()) + LEFT_BORDER + H_GAP; // Re-initialize the x grid. constraints.gridx = 0; int gridX = 0; if (imContact != null) { x += addButton(chatButton, ++gridX, x, false); } UIContactDetail telephonyContact = uiContact.getDefaultContactDetail(OperationSetBasicTelephony.class); // Check if contact has additional phone numbers, if yes show the // call button ContactPhoneUtil contactPhoneUtil = null; // check for phone stored in contact info only // if telephony contact is missing if (uiContact.getDescriptor() != null && uiContact.getDescriptor() instanceof MetaContact && telephonyContact == null) { contactPhoneUtil = ContactPhoneUtil.getPhoneUtil((MetaContact) uiContact.getDescriptor()); MetaContact metaContact = (MetaContact) uiContact.getDescriptor(); Iterator<Contact> contacts = metaContact.getContacts(); while (contacts.hasNext()) // && !hasPhone) { Contact contact = contacts.next(); if (!contact.getProtocolProvider().isRegistered()) continue; contactPhoneUtil.addDetailsResponseListener( contact, new DetailsListener(treeNode, callButton, uiContact)); } } // for SourceContact in history that do not support telephony, we // show the button but disabled List<ProtocolProviderService> providers = AccountUtils.getOpSetRegisteredProviders(OperationSetBasicTelephony.class, null, null); if ((telephonyContact != null && telephonyContact.getAddress() != null) || (contactPhoneUtil != null && contactPhoneUtil.isCallEnabled() && providers.size() > 0)) { x += addButton(callButton, ++gridX, x, false); } UIContactDetail videoContact = uiContact.getDefaultContactDetail(OperationSetVideoTelephony.class); if (videoContact != null || (contactPhoneUtil != null && contactPhoneUtil.isVideoCallEnabled())) { x += addButton(callVideoButton, ++gridX, x, false); } UIContactDetail desktopContact = uiContact.getDefaultContactDetail(OperationSetDesktopSharingServer.class); if (desktopContact != null || (contactPhoneUtil != null && contactPhoneUtil.isDesktopSharingEnabled())) { x += addButton(desktopSharingButton, ++gridX, x, false); } // enable add contact button if contact source has indicated // that this is possible if (uiContact.getDescriptor() instanceof SourceContact && uiContact.getDefaultContactDetail(OperationSetPersistentPresence.class) != null && AccountUtils.getOpSetRegisteredProviders( OperationSetPersistentPresence.class, null, null) .size() > 0 && !ConfigurationUtils.isAddContactDisabled()) { x += addButton(addContactButton, ++gridX, x, false); } // The list of the contact actions // we will create a button for every action Collection<SIPCommButton> contactActions = uiContact.getContactCustomActionButtons(); int lastGridX = gridX; if (contactActions != null && contactActions.size() > 0) { lastGridX = initContactActionButtons(contactActions, gridX, x); } else { addLabels(gridX); } if (lastAddedButton != null) setButtonBg(lastAddedButton, lastGridX, true); this.setBounds(0, 0, treeContactList.getWidth(), getPreferredSize().height); } /** * Initializes buttons panel. * * @param uiGroup the <tt>UIGroup</tt> for which we initialize the button panel */ private void initButtonsPanel(UIGroup uiGroup) { if (!isSelected) return; int x = (statusIcon == null ? 0 : statusIcon.getIconWidth()) + LEFT_BORDER + H_GAP; int gridX = 0; // The list of the actions // we will create a button for every action Collection<SIPCommButton> contactActions = uiGroup.getCustomActionButtons(); int lastGridX = gridX; if (contactActions != null && contactActions.size() > 0) { lastGridX = initGroupActionButtons(contactActions, gridX, x); } else { addLabels(gridX); } if (lastAddedButton != null) setButtonBg(lastAddedButton, lastGridX, true); this.setBounds(0, 0, treeContactList.getWidth(), getPreferredSize().height); } /** Clears the custom action buttons. */ private void clearCustomActionButtons() { if (customActionButtons != null && customActionButtons.size() > 0) { Iterator<JButton> buttonsIter = customActionButtons.iterator(); while (buttonsIter.hasNext()) { remove(buttonsIter.next()); } customActionButtons.clear(); } if (customActionButtonsUIGroup != null && customActionButtonsUIGroup.size() > 0) { Iterator<JButton> buttonsIter = customActionButtonsUIGroup.iterator(); while (buttonsIter.hasNext()) { remove(buttonsIter.next()); } customActionButtonsUIGroup.clear(); } } /** * Initializes custom contact action buttons. * * @param contactActionButtons the list of buttons to initialize * @param gridX the X grid of the first button * @param xBounds the x bounds of the first button * @return the new grid X coordinate after adding all the buttons */ private int initGroupActionButtons( Collection<SIPCommButton> contactActionButtons, int gridX, int xBounds) { // Reinit the labels to take the whole horizontal space. addLabels(gridX + contactActionButtons.size()); Iterator<SIPCommButton> actionsIter = contactActionButtons.iterator(); while (actionsIter.hasNext()) { final SIPCommButton actionButton = actionsIter.next(); // We need to explicitly remove the buttons from the tooltip manager, // because we're going to manager the tooltip ourselves in the // DefaultTreeContactList class. We need to do this in order to have // a different tooltip for every button and for non button area. ToolTipManager.sharedInstance().unregisterComponent(actionButton); if (customActionButtonsUIGroup == null) customActionButtonsUIGroup = new LinkedList<JButton>(); customActionButtonsUIGroup.add(actionButton); xBounds += addButton(actionButton, ++gridX, xBounds, false); } return gridX; } /** * Initializes custom contact action buttons. * * @param contactActionButtons the list of buttons to initialize * @param gridX the X grid of the first button * @param xBounds the x bounds of the first button * @return the new grid X coordiante after adding all the buttons */ private int initContactActionButtons( Collection<SIPCommButton> contactActionButtons, int gridX, int xBounds) { // Reinit the labels to take the whole horizontal space. addLabels(gridX + contactActionButtons.size()); Iterator<SIPCommButton> actionsIter = contactActionButtons.iterator(); while (actionsIter.hasNext()) { final SIPCommButton actionButton = actionsIter.next(); // We need to explicitly remove the buttons from the tooltip manager, // because we're going to manager the tooltip ourselves in the // DefaultTreeContactList class. We need to do this in order to have // a different tooltip for every button and for non button area. ToolTipManager.sharedInstance().unregisterComponent(actionButton); if (customActionButtons == null) customActionButtons = new LinkedList<JButton>(); customActionButtons.add(actionButton); xBounds += addButton(actionButton, ++gridX, xBounds, false); } return gridX; } /** * Draw the icon at the specified location. Paints this component as an icon. * * @param c the component which can be used as observer * @param g the <tt>Graphics</tt> object used for painting * @param x the position on the X coordinate * @param y the position on the Y coordinate */ public void paintIcon(Component c, Graphics g, int x, int y) { g = g.create(); try { Graphics2D g2 = (Graphics2D) g; AntialiasingManager.activateAntialiasing(g2); g2.setColor(Color.WHITE); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f)); g2.fillRoundRect(x, y, getIconWidth() - 1, getIconHeight() - 1, 10, 10); g2.setColor(Color.DARK_GRAY); g2.drawRoundRect(x, y, getIconWidth() - 1, getIconHeight() - 1, 10, 10); // Indent component content from the border. g2.translate(x + 5, y + 5); super.paint(g2); g2.translate(x, y); } finally { g.dispose(); } } /** * Returns the call button contained in the current cell. * * @return the call button contained in the current cell */ public JButton getChatButton() { return chatButton; } /** * Returns the call button contained in the current cell. * * @return the call button contained in the current cell */ public JButton getCallButton() { return callButton; } /** * Returns the call video button contained in the current cell. * * @return the call video button contained in the current cell */ public JButton getCallVideoButton() { return callVideoButton; } /** * Returns the desktop sharing button contained in the current cell. * * @return the desktop sharing button contained in the current cell */ public JButton getDesktopSharingButton() { return desktopSharingButton; } /** * Returns the add contact button contained in the current cell. * * @return the add contact button contained in the current cell */ public JButton getAddContactButton() { return addContactButton; } /** * Calls the given treeNode. * * @param treeNode the <tt>TreeNode</tt> to call */ private void call(TreeNode treeNode, JButton button, boolean isVideo, boolean isDesktopSharing) { if (!(treeNode instanceof ContactNode)) return; UIContact contactDescriptor = ((ContactNode) treeNode).getContactDescriptor(); Point location = new Point(button.getX(), button.getY() + button.getHeight()); SwingUtilities.convertPointToScreen(location, treeContactList); location.y = location.y + treeContactList.getPathBounds(treeContactList.getSelectionPath()).y; location.x += 8; location.y -= 8; CallManager.call(contactDescriptor, isVideo, isDesktopSharing, treeContactList, location); } /** * Shows the appropriate user interface that would allow the user to add the given * <tt>SourceUIContact</tt> to their contact list. * * @param contact the contact to add */ private void addContact(SourceUIContact contact) { SourceContact sourceContact = (SourceContact) contact.getDescriptor(); List<ContactDetail> details = sourceContact.getContactDetails(OperationSetPersistentPresence.class); int detailsCount = details.size(); if (detailsCount > 1) { JMenuItem addContactMenu = TreeContactList.createAddContactMenu((SourceContact) contact.getDescriptor()); JPopupMenu popupMenu = ((JMenu) addContactMenu).getPopupMenu(); // Add a title label. JLabel infoLabel = new JLabel(); infoLabel.setText( "<html><b>" + GuiActivator.getResources().getI18NString("service.gui.ADD_CONTACT") + "</b></html>"); popupMenu.insert(infoLabel, 0); popupMenu.insert(new Separator(), 1); popupMenu.setFocusable(true); popupMenu.setInvoker(treeContactList); Point location = new Point( addContactButton.getX(), addContactButton.getY() + addContactButton.getHeight()); SwingUtilities.convertPointToScreen(location, treeContactList); location.y = location.y + treeContactList.getPathBounds(treeContactList.getSelectionPath()).y; popupMenu.setLocation(location.x + 8, location.y - 8); popupMenu.setVisible(true); } else if (details.size() == 1) { TreeContactList.showAddContactDialog(details.get(0), sourceContact.getDisplayName()); } } /** * Returns the drag icon used to represent a cell in all drag operations. * * @param tree the parent tree object * @param dragObject the dragged object * @param index the index of the dragged object in the tree * @return the drag icon */ public Icon getDragIcon(JTree tree, Object dragObject, int index) { ContactListTreeCellRenderer dragC = (ContactListTreeCellRenderer) getTreeCellRendererComponent( tree, dragObject, false, // is selected false, // is expanded true, // is leaf index, true // has focus ); // We should explicitly set the bounds of all components in order that // they're correctly painted by paintIcon afterwards. This fixes empty // drag component in contact list! dragC.setBounds(0, 0, dragC.getIconWidth(), dragC.getIconHeight()); Icon rightLabelIcon = rightLabel.getIcon(); int imageHeight = 0; int imageWidth = 0; if (rightLabelIcon != null) { imageWidth = rightLabelIcon.getIconWidth(); imageHeight = rightLabelIcon.getIconHeight(); dragC.rightLabel.setBounds(tree.getWidth() - imageWidth, 0, imageWidth, imageHeight); } dragC.statusLabel.setBounds(0, 0, statusLabel.getWidth(), statusLabel.getHeight()); dragC.nameLabel.setBounds( statusLabel.getWidth(), 0, tree.getWidth() - imageWidth - 5, nameLabel.getHeight()); dragC.displayDetailsLabel.setBounds( displayDetailsLabel.getX(), nameLabel.getHeight(), displayDetailsLabel.getWidth(), displayDetailsLabel.getHeight()); return dragC; } /** Resets the rollover state of all rollover components in the current cell. */ public void resetRolloverState() { chatButton.getModel().setRollover(false); callButton.getModel().setRollover(false); callVideoButton.getModel().setRollover(false); desktopSharingButton.getModel().setRollover(false); addContactButton.getModel().setRollover(false); if (customActionButtons != null) { Iterator<JButton> buttonsIter = customActionButtons.iterator(); while (buttonsIter.hasNext()) { JButton button = buttonsIter.next(); button.getModel().setRollover(false); } } if (customActionButtonsUIGroup != null) { Iterator<JButton> buttonsIter = customActionButtonsUIGroup.iterator(); while (buttonsIter.hasNext()) { JButton button = buttonsIter.next(); button.getModel().setRollover(false); } } } /** * Resets the rollover state of all rollover components in the current cell except the component * given as a parameter. * * @param excludeComponent the component to exclude from the reset */ public void resetRolloverState(Component excludeComponent) { if (!chatButton.equals(excludeComponent)) chatButton.getModel().setRollover(false); if (!callButton.equals(excludeComponent)) callButton.getModel().setRollover(false); if (!callVideoButton.equals(excludeComponent)) callVideoButton.getModel().setRollover(false); if (!desktopSharingButton.equals(excludeComponent)) desktopSharingButton.getModel().setRollover(false); if (!addContactButton.equals(excludeComponent)) addContactButton.getModel().setRollover(false); if (customActionButtons != null) { Iterator<JButton> buttonsIter = customActionButtons.iterator(); while (buttonsIter.hasNext()) { JButton button = buttonsIter.next(); if (!button.equals(excludeComponent)) button.getModel().setRollover(false); } } if (customActionButtonsUIGroup != null) { Iterator<JButton> buttonsIter = customActionButtonsUIGroup.iterator(); while (buttonsIter.hasNext()) { JButton button = buttonsIter.next(); if (!button.equals(excludeComponent)) button.getModel().setRollover(false); } } } /** Loads all images and colors. */ public void loadSkin() { openedGroupIcon = new ImageIcon(ImageLoader.getImage(ImageLoader.OPENED_GROUP_ICON)); closedGroupIcon = new ImageIcon(ImageLoader.getImage(ImageLoader.CLOSED_GROUP_ICON)); callButton.setIconImage(ImageLoader.getImage(ImageLoader.CALL_BUTTON_SMALL)); callButton.setRolloverIcon(ImageLoader.getImage(ImageLoader.CALL_BUTTON_SMALL_ROLLOVER)); callButton.setPressedIcon(ImageLoader.getImage(ImageLoader.CALL_BUTTON_SMALL_PRESSED)); chatButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_BUTTON_SMALL)); chatButton.setRolloverIcon(ImageLoader.getImage(ImageLoader.CHAT_BUTTON_SMALL_ROLLOVER)); chatButton.setPressedIcon(ImageLoader.getImage(ImageLoader.CHAT_BUTTON_SMALL_PRESSED)); msgReceivedImage = ImageLoader.getImage(ImageLoader.MESSAGE_RECEIVED_ICON); int groupForegroundProperty = GuiActivator.getResources().getColor("service.gui.CONTACT_LIST_GROUP_FOREGROUND"); if (groupForegroundProperty > -1) groupForegroundColor = new Color(groupForegroundProperty); int contactForegroundProperty = GuiActivator.getResources().getColor("service.gui.CONTACT_LIST_CONTACT_FOREGROUND"); if (contactForegroundProperty > -1) contactForegroundColor = new Color(contactForegroundProperty); callVideoButton.setIconImage(ImageLoader.getImage(ImageLoader.CALL_VIDEO_BUTTON_SMALL)); callVideoButton.setRolloverIcon( ImageLoader.getImage(ImageLoader.CALL_VIDEO_BUTTON_SMALL_ROLLOVER)); callVideoButton.setPressedIcon( ImageLoader.getImage(ImageLoader.CALL_VIDEO_BUTTON_SMALL_PRESSED)); desktopSharingButton.setIconImage(ImageLoader.getImage(ImageLoader.DESKTOP_BUTTON_SMALL)); desktopSharingButton.setRolloverIcon( ImageLoader.getImage(ImageLoader.DESKTOP_BUTTON_SMALL_ROLLOVER)); desktopSharingButton.setPressedIcon( ImageLoader.getImage(ImageLoader.DESKTOP_BUTTON_SMALL_PRESSED)); addContactButton.setIconImage(ImageLoader.getImage(ImageLoader.ADD_CONTACT_BUTTON_SMALL)); addContactButton.setRolloverIcon( ImageLoader.getImage(ImageLoader.ADD_CONTACT_BUTTON_SMALL_ROLLOVER)); addContactButton.setPressedIcon( ImageLoader.getImage(ImageLoader.ADD_CONTACT_BUTTON_SMALL_PRESSED)); } /** * Listens for contact details if not cached, we will receive when they are retrieved to update * current call button state, if meanwhile user hasn't changed the current contact. */ private class DetailsListener implements OperationSetServerStoredContactInfo.DetailsResponseListener { /** The source this listener is created for, if current tree node changes ignore any event. */ private Object source; /** The button to change. */ private JButton callButton; /** The ui contact to update after changes. */ private UIContact uiContact; /** * Create listener. * * @param source the contact this listener is for, if different than current ignore. * @param callButton * @param uiContact the contact to refresh */ DetailsListener(Object source, JButton callButton, UIContact uiContact) { this.source = source; this.callButton = callButton; this.uiContact = uiContact; } /** * Details have been retrieved. * * @param details the details retrieved if any. */ public void detailsRetrieved(Iterator<GenericDetail> details) { // if treenode has changed ignore if (!source.equals(treeNode)) return; while (details.hasNext()) { GenericDetail d = details.next(); if (d instanceof PhoneNumberDetail && !(d instanceof PagerDetail) && !(d instanceof FaxDetail)) { final PhoneNumberDetail pnd = (PhoneNumberDetail) d; if (pnd.getNumber() != null && pnd.getNumber().length() > 0) { SwingUtilities.invokeLater( new Runnable() { public void run() { callButton.setEnabled(true); if (pnd instanceof VideoDetail) { callVideoButton.setEnabled(true); desktopSharingButton.setEnabled(true); } treeContactList.refreshContact(uiContact); } }); return; } } } } } private int addButton(SIPCommButton button, int gridX, int xBounds, boolean isLast) { lastAddedButton = button; constraints.insets = new Insets(0, 0, V_GAP, 0); constraints.anchor = GridBagConstraints.WEST; constraints.fill = GridBagConstraints.NONE; constraints.gridx = gridX; constraints.gridy = 2; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.weightx = 0f; constraints.weighty = 0f; this.add(button, constraints); int yBounds = TOP_BORDER + BOTTOM_BORDER + 2 * V_GAP + ComponentUtils.getStringSize(nameLabel, nameLabel.getText()).height + ComponentUtils.getStringSize(displayDetailsLabel, displayDetailsLabel.getText()) .height; button.setBounds(xBounds, yBounds, BUTTON_WIDTH, BUTTON_HEIGHT); button.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); setButtonBg(button, gridX, isLast); return button.getWidth(); } /** * Sets the background of the button depending on its position in the button bar. * * @param button the button which background to set * @param gridX the position of the button in the grid * @param isLast indicates if this is the last button in the button bar */ private void setButtonBg(SIPCommButton button, int gridX, boolean isLast) { if (!isLast) { if (gridX == 1) button.setBackgroundImage(ImageLoader.getImage(ImageLoader.CONTACT_LIST_BUTTON_BG_LEFT)); else if (gridX > 1) button.setBackgroundImage(ImageLoader.getImage(ImageLoader.CONTACT_LIST_BUTTON_BG_MIDDLE)); } else { if (gridX == 1) // We have only one button shown. button.setBackgroundImage(ImageLoader.getImage(ImageLoader.CONTACT_LIST_ONE_BUTTON_BG)); else // We set the background of the last button in the toolbar button.setBackgroundImage(ImageLoader.getImage(ImageLoader.CONTACT_LIST_BUTTON_BG_RIGHT)); } } /** Sets the correct border depending on the contained object. */ private void setBorder() { /* * !!! When changing border values we should make sure that we * recalculate the X and Y coordinates of the buttons added in * initButtonsPanel and initContactActionButtons functions. If not * correctly calculated problems may occur when clicking buttons! */ if (treeNode instanceof ContactNode && !(((ContactNode) treeNode).getContactDescriptor() instanceof ShowMoreContact)) { this.setBorder( BorderFactory.createEmptyBorder(TOP_BORDER, LEFT_BORDER, BOTTOM_BORDER, RIGHT_BORDER)); } else // GroupNode || ShowMoreContact { this.setBorder(BorderFactory.createEmptyBorder(0, LEFT_BORDER, 0, RIGHT_BORDER)); } } /** Inializes button tool tips. */ private void initButtonToolTips() { callButton.setToolTipText( GuiActivator.getResources().getI18NString("service.gui.CALL_CONTACT")); callVideoButton.setToolTipText( GuiActivator.getResources().getI18NString("service.gui.VIDEO_CALL")); desktopSharingButton.setToolTipText( GuiActivator.getResources().getI18NString("service.gui.SHARE_DESKTOP")); chatButton.setToolTipText( GuiActivator.getResources().getI18NString("service.gui.SEND_MESSAGE")); addContactButton.setToolTipText( GuiActivator.getResources().getI18NString("service.gui.ADD_CONTACT")); // We need to explicitly remove the buttons from the tooltip manager, // because we're going to manager the tooltip ourselves in the // DefaultTreeContactList class. We need to do this in order to have // a different tooltip for every button and for non button area. ToolTipManager ttManager = ToolTipManager.sharedInstance(); ttManager.unregisterComponent(callButton); ttManager.unregisterComponent(callVideoButton); ttManager.unregisterComponent(desktopSharingButton); ttManager.unregisterComponent(chatButton); ttManager.unregisterComponent(addContactButton); } }
/** Loads all images and colors. */ public void loadSkin() { openedGroupIcon = new ImageIcon(ImageLoader.getImage(ImageLoader.OPENED_GROUP_ICON)); closedGroupIcon = new ImageIcon(ImageLoader.getImage(ImageLoader.CLOSED_GROUP_ICON)); callButton.setIconImage(ImageLoader.getImage(ImageLoader.CALL_BUTTON_SMALL)); callButton.setRolloverIcon(ImageLoader.getImage(ImageLoader.CALL_BUTTON_SMALL_ROLLOVER)); callButton.setPressedIcon(ImageLoader.getImage(ImageLoader.CALL_BUTTON_SMALL_PRESSED)); chatButton.setIconImage(ImageLoader.getImage(ImageLoader.CHAT_BUTTON_SMALL)); chatButton.setRolloverIcon(ImageLoader.getImage(ImageLoader.CHAT_BUTTON_SMALL_ROLLOVER)); chatButton.setPressedIcon(ImageLoader.getImage(ImageLoader.CHAT_BUTTON_SMALL_PRESSED)); msgReceivedImage = ImageLoader.getImage(ImageLoader.MESSAGE_RECEIVED_ICON); int groupForegroundProperty = GuiActivator.getResources().getColor("service.gui.CONTACT_LIST_GROUP_FOREGROUND"); if (groupForegroundProperty > -1) groupForegroundColor = new Color(groupForegroundProperty); int contactForegroundProperty = GuiActivator.getResources().getColor("service.gui.CONTACT_LIST_CONTACT_FOREGROUND"); if (contactForegroundProperty > -1) contactForegroundColor = new Color(contactForegroundProperty); callVideoButton.setIconImage(ImageLoader.getImage(ImageLoader.CALL_VIDEO_BUTTON_SMALL)); callVideoButton.setRolloverIcon( ImageLoader.getImage(ImageLoader.CALL_VIDEO_BUTTON_SMALL_ROLLOVER)); callVideoButton.setPressedIcon( ImageLoader.getImage(ImageLoader.CALL_VIDEO_BUTTON_SMALL_PRESSED)); desktopSharingButton.setIconImage(ImageLoader.getImage(ImageLoader.DESKTOP_BUTTON_SMALL)); desktopSharingButton.setRolloverIcon( ImageLoader.getImage(ImageLoader.DESKTOP_BUTTON_SMALL_ROLLOVER)); desktopSharingButton.setPressedIcon( ImageLoader.getImage(ImageLoader.DESKTOP_BUTTON_SMALL_PRESSED)); addContactButton.setIconImage(ImageLoader.getImage(ImageLoader.ADD_CONTACT_BUTTON_SMALL)); addContactButton.setRolloverIcon( ImageLoader.getImage(ImageLoader.ADD_CONTACT_BUTTON_SMALL_ROLLOVER)); addContactButton.setPressedIcon( ImageLoader.getImage(ImageLoader.ADD_CONTACT_BUTTON_SMALL_PRESSED)); }