/* * 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.osdependent.jdic; import java.awt.*; import java.awt.event.*; import java.beans.*; import java.util.*; import java.util.List; import javax.swing.*; import net.java.sip.communicator.impl.osdependent.*; import net.java.sip.communicator.plugin.desktoputil.presence.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.globalstatus.*; import net.java.sip.communicator.util.*; import org.osgi.framework.*; /** * The <tt>StatusSubMenu</tt> provides a menu which allow to select the status * for each of the protocol providers registered when the menu appears * * @author Nicolas Chamouard * @author Lyubomir Marinov */ public class StatusSubMenu implements ProviderPresenceStatusListener, RegistrationStateChangeListener, ActionListener, ItemListener { /** * Contains all accounts and corresponding menus. */ private final Map<AccountID, Object> accountSelectors = new Hashtable<AccountID, Object>(); private final Object menu; /** * Hide accounts from accounts status list. */ private static boolean hideAccountStatusSelectors = false; /** * Initializes a new <tt>StatusSubMenu</tt> instance. * * @param swing <tt>true</tt> to represent this instance with a Swing * <tt>JMenu</tt>; <tt>false</tt> to use an AWT <tt>Menu</tt> */ public StatusSubMenu(boolean swing, boolean accountMenuSupported) { String text = Resources.getString("impl.systray.SET_STATUS"); if (swing) { JMenu menu = new JMenu(text); menu.setIcon( Resources.getImage("service.systray.STATUS_MENU_ICON")); /* makes the menu look better */ menu.setPreferredSize(new java.awt.Dimension(28, 24)); this.menu = menu; } else { this.menu = new Menu(text); } if (accountMenuSupported) { String hideAccountStatusSelectorsProperty = "impl.gui.HIDE_ACCOUNT_STATUS_SELECTORS"; String hideAccountsStatusDefaultValue = OsDependentActivator.getResources() .getSettingsString(hideAccountStatusSelectorsProperty); if(hideAccountsStatusDefaultValue != null) hideAccountStatusSelectors = Boolean.parseBoolean( hideAccountsStatusDefaultValue); hideAccountStatusSelectors = OsDependentActivator.getConfigurationService() .getBoolean( hideAccountStatusSelectorsProperty, hideAccountStatusSelectors); } else { hideAccountStatusSelectors = true; } PresenceStatus offlineStatus = null; // creates menu item entry for every global status for(GlobalStatusEnum status : GlobalStatusEnum.globalStatusSet) { createMenuItem(status, swing); if(status.getStatus() < 1) offlineStatus = status; } // initially it is offline selectItemFromStatus(offlineStatus.getStatus()); if (accountMenuSupported) { this.addSeparator(); addMenuItem(menu, new GlobalStatusMessageMenu(swing).getMenu()); } if(!hideAccountStatusSelectors) this.addSeparator(); this.init(); } public Object getMenu() { return menu; } /** * Creates a menu item with the given <tt>textKey</tt>, <tt>iconID</tt> and * <tt>name</tt>. * @return the created <tt>JCheckBoxMenuItem</tt> */ private Object createMenuItem( GlobalStatusEnum status, boolean swing) { Object menuItem = null; if(swing) { JCheckBoxMenuItem mItem = new JCheckBoxMenuItem( GlobalStatusEnum.getI18NStatusName(status), new ImageIcon(status.getStatusIcon())); mItem.setName(status.getStatusName()); mItem.addActionListener(this); menuItem = mItem; } else { CheckboxMenuItem mItem = new CheckboxMenuItem( GlobalStatusEnum.getI18NStatusName(status)); mItem.setName(status.getStatusName()); mItem.addItemListener(this); menuItem = mItem; } addMenuItem(getMenu(), menuItem); return menuItem; } /** * Adds separator to underlying menu implementation. */ private void addSeparator() { if (menu instanceof JMenu) ((JMenu) menu).addSeparator(); else ((Menu) menu).addSeparator(); } /** * Adds the account corresponding to the given protocol provider to this * menu. * * @param protocolProvider the protocol provider corresponding to the * account to add */ private void addAccount(ProtocolProviderService protocolProvider) { if(protocolProvider.getAccountID().isStatusMenuHidden()) return; OperationSetPresence presence = protocolProvider.getOperationSet(OperationSetPresence.class); boolean swing = (menu instanceof JComponent); if (presence == null) { StatusSimpleSelector simpleSelector = new StatusSimpleSelector(protocolProvider, swing); this.accountSelectors.put(protocolProvider.getAccountID(), simpleSelector); if(!hideAccountStatusSelectors) addMenuItem(menu, simpleSelector.getMenu()); protocolProvider.addRegistrationStateChangeListener(this); } else { StatusSelector statusSelector = new StatusSelector(protocolProvider, presence, swing); this.accountSelectors.put(protocolProvider.getAccountID(), statusSelector); if(!hideAccountStatusSelectors) addMenuItem(menu, statusSelector.getMenu()); presence.addProviderPresenceStatusListener(this); } } static void addMenuItem(Object menu, Object menuItem) { if (menu instanceof Container) ((Container) menu).add((Component) menuItem); else ((Menu) menu).add((MenuItem) menuItem); } /** * Removes the account corresponding to the given protocol provider from * this menu. * * @param protocolProvider the protocol provider corresponding to the * account to remove. */ private void removeAccount(ProtocolProviderService protocolProvider) { Object selector = this.accountSelectors.get(protocolProvider.getAccountID()); // no such provider added if(selector == null) return; Object selectorMenu; if (selector instanceof StatusSimpleSelector) selectorMenu = ((StatusSimpleSelector) selector).getMenu(); else selectorMenu = ((StatusSelector) selector).getMenu(); if (menu instanceof Container) ((Container) menu).remove((Component) selectorMenu); else ((MenuContainer) menu).remove((MenuComponent) selectorMenu); /* * Remove the listeners installed in * addAccount(ProtocolProviderService). */ OperationSetPresence presence = protocolProvider.getOperationSet(OperationSetPresence.class); if (presence != null) presence.removeProviderPresenceStatusListener(this); else protocolProvider.removeRegistrationStateChangeListener(this); } /** * We fill the protocolProviderTable with all * running protocol providers at the start of * the bundle. */ private void init() { OsDependentActivator.bundleContext .addServiceListener(new ProtocolProviderServiceListener()); for(ProtocolProviderService provider : getProtocolProviders()) { if(!provider.getAccountID().isHidden()) this.addAccount(provider); } } /** * Obtains all currently registered ProtocolProviderServices. * @return all currently registered ProtocolProviderServices. */ private List<ProtocolProviderService> getProtocolProviders() { BundleContext bundleContext = OsDependentActivator.bundleContext; Collection<ServiceReference<ProtocolProviderService>> ppsRefs = ServiceUtils.getServiceReferences( bundleContext, ProtocolProviderService.class); List<ProtocolProviderService> protocolProviders = new ArrayList<ProtocolProviderService>(); // in case we found any if ((ppsRefs != null) && !ppsRefs.isEmpty()) { for (ServiceReference<ProtocolProviderService> ppsRef : ppsRefs) protocolProviders.add(bundleContext.getService(ppsRef)); } return protocolProviders; } /** * Fired when an account has changed its status. We update the icon * in the menu. * * @param evt */ public void providerStatusChanged(ProviderPresenceStatusChangeEvent evt) { ProtocolProviderService pps = evt.getProvider(); StatusSelector selectorBox = (StatusSelector) accountSelectors.get(pps.getAccountID()); if (selectorBox != null) selectorBox.updateStatus(evt.getNewStatus()); this.updateGlobalStatus(); } /* * ImplementsProviderPresenceStatusListener#providerStatusMessageChanged( * PropertyChangeEvent). */ public void providerStatusMessageChanged(PropertyChangeEvent evt) { } /* * ImplementsRegistrationStateChangeListener#registrationStateChanged( * RegistrationStateChangeEvent). Updates the status of accounts which do * not support presence. */ public void registrationStateChanged(RegistrationStateChangeEvent evt) { ProtocolProviderService pps = evt.getProvider(); StatusSimpleSelector selectorBox = (StatusSimpleSelector) accountSelectors.get(pps.getAccountID()); if (selectorBox != null) selectorBox.updateStatus(); this.updateGlobalStatus(); } /** * Updates the global status by picking the most connected protocol provider * status. */ private void updateGlobalStatus() { int status = 0; boolean hasAvailableProvider = false; for(ProtocolProviderService protocolProvider : getProtocolProviders()) { // We do not show hidden protocols in our status bar, so we do not // care about their status here. if (protocolProvider.getAccountID().isHidden()) continue; if (!protocolProvider.isRegistered()) continue; OperationSetPresence presence = protocolProvider.getOperationSet(OperationSetPresence.class); if(presence == null) { hasAvailableProvider = true; continue; } int presenceStatus = (presence == null) ? PresenceStatus.AVAILABLE_THRESHOLD : presence.getPresenceStatus().getStatus(); if (status < presenceStatus) status = presenceStatus; } // if we have at least one online provider if(status == 0 && hasAvailableProvider) status = PresenceStatus.AVAILABLE_THRESHOLD; selectItemFromStatus(status); } /** * Selects the menu item corresponding to the given status. * For status constants we use here the values defined in the * <tt>PresenceStatus</tt>, but this is only for convenience. * * @param status the status to which the item should correspond */ private void selectItemFromStatus(int status) { String nameToSelect; if(status < PresenceStatus.ONLINE_THRESHOLD) { nameToSelect = GlobalStatusEnum.OFFLINE_STATUS; } else if(status < PresenceStatus.EXTENDED_AWAY_THRESHOLD) { nameToSelect = GlobalStatusEnum.DO_NOT_DISTURB_STATUS; } else if(status < PresenceStatus.AWAY_THRESHOLD) { nameToSelect = GlobalStatusEnum.EXTENDED_AWAY_STATUS; } else if(status < PresenceStatus.AVAILABLE_THRESHOLD) { nameToSelect = GlobalStatusEnum.AWAY_STATUS; } else if(status < PresenceStatus.EAGER_TO_COMMUNICATE_THRESHOLD) { nameToSelect = GlobalStatusEnum.ONLINE_STATUS; } else if(status < PresenceStatus.MAX_STATUS_VALUE) { nameToSelect = GlobalStatusEnum.FREE_FOR_CHAT_STATUS; } else { nameToSelect = GlobalStatusEnum.OFFLINE_STATUS; } if(menu instanceof Menu) { Menu theMenu = (Menu) menu; for(int i =0; i < theMenu.getItemCount(); i++) { MenuItem item = theMenu.getItem(i); if(item instanceof CheckboxMenuItem) { if(item.getName().equals(nameToSelect)) { ((CheckboxMenuItem)item).setState(true); } else { ((CheckboxMenuItem)item).setState(false); } } } } else if(menu instanceof JMenu) { JMenu theMenu = (JMenu) menu; for(int i =0; i < theMenu.getItemCount(); i++) { JMenuItem item = theMenu.getItem(i); if(item instanceof JCheckBoxMenuItem) { if(item.getName().equals(nameToSelect)) item.setSelected(true); else item.setSelected(false); } } } } /** * Change the status of the protocol according to the menu item selected * * @param evt the event containing the menu item name */ public void actionPerformed(ActionEvent evt) { Object source = evt.getSource(); if(source instanceof JMenuItem) changeStatusFromName(((JMenuItem)source).getName()); } /** * Listens for changes in item state (CheckboxMenuItem)s. * @param e the event. */ public void itemStateChanged(ItemEvent e) { Object sourceItem = e.getSource(); if(e.getStateChange() == ItemEvent.SELECTED) { if(sourceItem instanceof CheckboxMenuItem) { changeStatusFromName(((CheckboxMenuItem)sourceItem).getName()); } } else if(e.getStateChange() == ItemEvent.DESELECTED) { if(sourceItem instanceof CheckboxMenuItem) { ((CheckboxMenuItem)sourceItem).setState(true); } } } /** * Changes global status from selected item name. * @param itemName the item name that was selected. */ private void changeStatusFromName(String itemName) { OsDependentActivator.getGlobalStatusService().publishStatus( GlobalStatusEnum.getStatusByName(itemName)); } /** * Listens for <tt>ServiceEvent</tt>s indicating that a * <tt>ProtocolProviderService</tt> has been registered and completes the * account status menu. */ private class ProtocolProviderServiceListener implements ServiceListener { /** * When a service is registered or unregistered, we update * the provider tables and add/remove listeners (if it supports * BasicInstantMessaging implementation) * * @param event ServiceEvent */ public void serviceChanged(ServiceEvent event) { //if the event is caused by a bundle being stopped, we don't want to //know ServiceReference<?> serviceRef = event.getServiceReference(); if(serviceRef.getBundle().getState() == Bundle.STOPPING) return; Object service = OsDependentActivator.bundleContext.getService(serviceRef); if (! (service instanceof ProtocolProviderService)) return; ProtocolProviderService provider = (ProtocolProviderService)service; switch (event.getType()) { case ServiceEvent.REGISTERED: addAccount(provider); break; case ServiceEvent.UNREGISTERING: removeAccount(provider); break; } } } }