/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.impl.gui.main.chat; import java.awt.*; import java.awt.event.*; import java.util.*; import javax.swing.*; import org.jitsi.util.*; import net.java.sip.communicator.impl.gui.*; import net.java.sip.communicator.impl.gui.utils.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.util.Logger; import net.java.sip.communicator.util.skin.*; /** * The ChatTransportSelectorBox represents the send via menu in the * chat window. The menu contains all protocol specific transports. In the case * of meta contact these would be all contacts for the currently selected meta * contact chat. * * @author Yana Stamcheva * @author Adam Netocny */ public class ChatTransportSelectorBox extends SIPCommMenuBar implements ActionListener, Skinnable { private static final Logger logger = Logger.getLogger(ChatTransportSelectorBox.class); private static final long serialVersionUID = 0L; private final Map transportMenuItems = new Hashtable(); private final SIPCommMenu menu = new SelectorMenu(); private final ChatSession chatSession; private final ChatPanel chatPanel; /** * Take care for chat transport items, that only one is selected. */ private ButtonGroup buttonGroup = new ButtonGroup(); /** * Creates an instance of ChatTransportSelectorBox. * * @param chatPanel the chat panel * @param chatSession the corresponding chat session * @param selectedChatTransport the chat transport to select by default */ public ChatTransportSelectorBox(ChatPanel chatPanel, ChatSession chatSession, ChatTransport selectedChatTransport) { this.chatPanel = chatPanel; this.chatSession = chatSession; setPreferredSize(new Dimension(30, 28)); setMaximumSize(new Dimension(30, 28)); setMinimumSize(new Dimension(30, 28)); this.menu.setPreferredSize(new Dimension(30, 45)); this.menu.setMaximumSize(new Dimension(30, 45)); this.add(menu); this.setBorder(null); this.menu.setBorder(null); this.menu.setOpaque(false); this.setOpaque(false); // as a default disable the menu, it will be enabled as soon as we add // a valid menu item this.menu.setEnabled(false); Iterator chatTransports = chatSession.getChatTransports(); while (chatTransports.hasNext()) this.addChatTransport(chatTransports.next()); if (this.menu.getItemCount() > 0) { if (selectedChatTransport != null && (selectedChatTransport.allowsInstantMessage() || selectedChatTransport.allowsSmsMessage())) { this.setSelected(selectedChatTransport); } else setSelected(menu.getItem(0)); } } /** * Sets the menu to enabled or disabled. The menu is enabled, as soon as it * contains one or more items. If it is empty, it is disabled. */ private void updateEnableStatus() { this.menu.setEnabled(this.menu.getItemCount() > 0); } /** * Adds the given chat transport to the "send via" menu. * Only add those that support IM. * * @param chatTransport The chat transport to add. */ public void addChatTransport(ChatTransport chatTransport) { if (chatTransport.allowsInstantMessage() || chatTransport.allowsSmsMessage()) { Image img = createTransportStatusImage(chatTransport); boolean isIndent = false; String toString = ""; if (chatTransport.getResourceName() != null && chatTransport.isDisplayResourceOnly()) { toString = chatTransport.getResourceName(); isIndent = true; } else toString = "" + chatTransport.getName() + " " + ((chatTransport.getResourceName() == null) ? "" : chatTransport.getResourceName()) + " (" + GuiActivator.getResources() .getI18NString("service.gui.VIA") + ": " + chatTransport.getProtocolProvider() .getAccountID().getDisplayName() + ")"; JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem( "" + toString + "", new ImageIcon(img)); if (isIndent) menuItem.setBorder( BorderFactory.createEmptyBorder(0, 20, 0, 0)); menuItem.addActionListener(this); this.transportMenuItems.put(chatTransport, menuItem); buttonGroup.add(menuItem); this.menu.add(menuItem); updateEnableStatus(); updateTransportStatus(chatTransport); } if(!allowsInstantMessage() && allowsSmsMessage()) chatPanel.getChatWritePanel().setSmsSelected(true); else chatPanel.getChatWritePanel().setSmsSelected(false); } /** * Removes the given chat transport from the "send via" menu. This method is * used to update the "send via" menu when a protocol contact is moved or * removed from the contact list. * * @param chatTransport the chat transport to be removed */ public void removeChatTransport(ChatTransport chatTransport) { JCheckBoxMenuItem menuItem = transportMenuItems.get(chatTransport); this.menu.remove(menuItem); this.buttonGroup.remove(menuItem); this.transportMenuItems.remove(chatTransport); updateEnableStatus(); } /** * The listener of the chat transport selector box. * * @param e the ActionEvent that notified us */ public void actionPerformed(ActionEvent e) { JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) e.getSource(); for (Map.Entry transportMenuItem : transportMenuItems.entrySet()) { ChatTransport chatTransport = transportMenuItem.getKey(); if (transportMenuItem.getValue().equals(menuItem)) { this.setSelected( menuItem, chatTransport, (ImageIcon) menuItem.getIcon()); chatSession.getChatSessionRenderer() .setChatIcon(new ImageIcon( Constants.getStatusIcon(chatTransport.getStatus()))); return; } } if (logger.isDebugEnabled()) logger.debug( "Could not find contact for menu item " + menuItem.getText() + ". contactsTable(" + transportMenuItems.size()+") is : " + transportMenuItems); } /** * Obtains the status icon for the given chat transport and * adds to it the account index information. * * @param chatTransport The chat transport for which to create the image. * @return The indexed status image. */ public Image createTransportStatusImage(ChatTransport chatTransport) { return ImageLoader.getIndexedProtocolImage( ImageUtils.getBytesInImage( chatTransport.getStatus().getStatusIcon()), chatTransport.getProtocolProvider()); } /** * Updates the chat transport presence status. * * @param chatTransport The chat transport to update. */ public void updateTransportStatus(ChatTransport chatTransport) { JMenuItem menuItem; Icon icon; if (chatTransport.equals(chatSession.getCurrentChatTransport()) && !chatTransport.getStatus().isOnline()) { ChatTransport newChatTransport = getParentContactTransport(chatTransport); ChatTransport onlineTransport = getTransport(true); if(newChatTransport != null && newChatTransport.getStatus().isOnline()) setSelected(newChatTransport); else if (onlineTransport != null) setSelected(onlineTransport); else { // update when going to offline ChatTransport offlineTransport = getTransport(false); if(offlineTransport != null) setSelected(offlineTransport); } } menuItem = transportMenuItems.get(chatTransport); // sometimes it may happen that menuItem is null // it was removed for some reason, this maybe due to other bug // anyway detect it to avoid NPE if(menuItem == null) return; icon = new ImageIcon(createTransportStatusImage(chatTransport)); menuItem.setIcon(icon); if( menu.getSelectedObject() != null && menu.getSelectedObject().equals(chatTransport)) { this.menu.setIcon(icon); this.chatSession.fireCurrentChatTransportUpdated( ChatSessionChangeListener.ICON_UPDATED); } } /** * In the "send via" menu selects the given contact and sets the given icon * to the "send via" menu button. * * @param menuItem the menu item that is selected * @param chatTransport the corresponding chat transport * @param icon */ private void setSelected( JCheckBoxMenuItem menuItem, ChatTransport chatTransport, ImageIcon icon) { menuItem.setSelected(true); this.chatSession.setCurrentChatTransport(chatTransport); SelectedObject selectedObject = new SelectedObject(icon, chatTransport); this.menu.setSelected(selectedObject); this.chatSession.fireCurrentChatTransportUpdated( ChatSessionChangeListener.ICON_UPDATED); String resourceName = (chatTransport.getResourceName() != null) ? " (" + chatTransport.getResourceName() + ")" : ""; String displayName = (!chatTransport.getDisplayName() .equals(chatTransport.getName())) ? chatTransport.getDisplayName() + " (" + chatTransport.getName() + ")" : chatTransport.getDisplayName(); String tooltipText = "" + displayName + "" + resourceName + "
" + GuiActivator.getResources() .getI18NString("service.gui.VIA") + ": " + chatTransport.getProtocolProvider() .getAccountID().getAccountAddress() + "
"; this.menu.setToolTipText(tooltipText); chatPanel.getChatWritePanel().setSmsLabelVisible( chatTransport.allowsSmsMessage()); } /** * Sets the selected contact to the given proto contact. * @param chatTransport the proto contact to select */ public void setSelected(ChatTransport chatTransport) { JCheckBoxMenuItem menuItem = transportMenuItems.get(chatTransport); if (menuItem == null) return; this.setSelected( menuItem, chatTransport, new ImageIcon(createTransportStatusImage(chatTransport))); } /** * Do we have a selected transport. * @return do we have a selected transport. */ boolean hasSelectedTransport() { for(JCheckBoxMenuItem item : transportMenuItems.values()) { if(item.isSelected()) return true; } return false; } /** * Returns the protocol menu. * * @return the protocol menu */ public SIPCommMenu getMenu() { return menu; } /** * Searches online contacts in the send via combo box. * * @param chatTransport the chat transport to check * @return TRUE if the send via combo box contains online contacts, * otherwise returns FALSE. */ private ChatTransport getParentContactTransport(ChatTransport chatTransport) { for (ChatTransport comboChatTransport : transportMenuItems.keySet()) { if(comboChatTransport.getDescriptor() .equals(chatTransport.getDescriptor()) && StringUtils.isNullOrEmpty( comboChatTransport.getResourceName())) return comboChatTransport; } return null; } /** * Searches online contacts in the send via combo box. * * @param online if TRUE will return online transport, otherwise * will return offline one. * @return online or offline contact transport from combo box. */ private ChatTransport getTransport(boolean online) { for (ChatTransport comboChatTransport : transportMenuItems.keySet()) { if(online && comboChatTransport.getStatus().isOnline()) return comboChatTransport; else if(!online && !comboChatTransport.getStatus().isOnline()) return comboChatTransport; } return null; } /** * Returns true if this contains a chat transport that * supports instant messaging, otherwise returns false. * * @return true if this contains a chat transport that * supports instant messaging, otherwise returns false */ private boolean allowsInstantMessage() { for(ChatTransport tr : transportMenuItems.keySet()) { if(tr.allowsInstantMessage()) { return true; } } return false; } /** * Returns true if this contains a chat transport that * supports sms messaging, otherwise returns false. * * @return true if this contains a chat transport that * supports sms messaging, otherwise returns false */ private boolean allowsSmsMessage() { for(ChatTransport tr : transportMenuItems.keySet()) { if(tr.allowsSmsMessage()) return true; } return false; } /** * A custom SIPCommMenu that adds an arrow icon to the right of * the menu image. */ private class SelectorMenu extends SIPCommMenu { private static final long serialVersionUID = 0L; Image image = ImageLoader.getImage(ImageLoader.DOWN_ARROW_ICON); @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(image, getWidth() - image.getWidth(this) - 1, (getHeight() - image.getHeight(this) - 1)/2, this); } } }