/*
* 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.presence;
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import net.java.sip.communicator.impl.gui.*;
import net.java.sip.communicator.impl.gui.event.*;
import net.java.sip.communicator.impl.gui.lookandfeel.*;
import net.java.sip.communicator.impl.gui.utils.*;
import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.plugin.desktoputil.presence.avatar.*;
import net.java.sip.communicator.service.globaldisplaydetails.*;
import net.java.sip.communicator.service.globaldisplaydetails.event.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.gui.Container;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.util.skin.*;
import org.jitsi.util.*;
/**
* The panel shown on the top of the contact list. It contains user name,
* current status menu and the avatar of the user.
*
* @author Yana Stamcheva
* @author Adam Netocny
*/
public class AccountStatusPanel
extends TransparentPanel
implements RegistrationStateChangeListener,
PluginComponentListener,
GlobalDisplayDetailsListener,
Skinnable
{
/**
* Class id key used in UIDefaults.
*/
private static final String uiClassID =
AccountStatusPanel.class.getName() + "OpaquePanelUI";
/**
* Property name to disable the avatar change menu.
*/
private static final String PNAME_DISABLE_AVATAR_MENU
= "net.java.sip.communicator.gui.DISABLE_AVATAR_MENU";
/**
* Adds the ui class to UIDefaults.
*/
static
{
UIManager.getDefaults().put(uiClassID,
SIPCommOpaquePanelUI.class.getName());
}
/**
* The desired height of the avatar.
*/
private static final int AVATAR_ICON_HEIGHT = 50;
/**
* The desired width of the avatar.
*/
private static final int AVATAR_ICON_WIDTH = 50;
/**
* The image object storing the avatar.
*/
private final FramedImage accountImageLabel;
/**
* The label showing the name of the user.
*/
private final JLabel accountNameLabel
= new EmphasizedLabel(
GuiActivator
.getResources().getI18NString("service.gui.ACCOUNT_ME"));
/**
* The background color property.
*/
private Color bgColor;
/**
* The background image property.
*/
private Image logoBgImage;
/**
* The combo box containing status menu.
*/
private final GlobalStatusSelectorBox statusComboBox;
/**
* TexturePaint used to paint background image.
*/
private TexturePaint texture;
/**
* The tool bar plug-in container.
*/
private final TransparentPanel toolbarPluginPanel;
/**
* The south plug-in container.
*/
private final TransparentPanel southPluginPanel;
/**
* Keep reference to plugin container or it will loose its
* listener.
*/
private final PluginContainer southPluginContainer;
/**
* Keep reference to plugin container or it will loose its
* listener.
*/
private final PluginContainer mainToolbarPluginContainer;
/**
* When setting global status message, the message will be displayed in this
* label.
*/
private final JLabel statusMessageLabel = new JLabel();
/**
* This is the panel that contains display name, status message, status
* selector box and plugins.
*/
private final TransparentPanel rightPanel = new TransparentPanel();
/**
* Creates an instance of <tt>AccountStatusPanel</tt> by specifying the
* main window, where this panel is added.
*/
public AccountStatusPanel()
{
super(new BorderLayout(10, 0));
FramedImageWithMenu imageWithMenu
= new FramedImageWithMenu(
new ImageIcon(
ImageLoader
.getImage(ImageLoader.DEFAULT_USER_PHOTO)),
AVATAR_ICON_WIDTH,
AVATAR_ICON_HEIGHT);
if (!GuiActivator.getConfigurationService().getBoolean(
PNAME_DISABLE_AVATAR_MENU, false))
{
imageWithMenu.setPopupMenu(new SelectAvatarMenu(imageWithMenu));
}
this.accountImageLabel = imageWithMenu;
accountNameLabel.setFont(
accountNameLabel.getFont().deriveFont(12f));
accountNameLabel.setOpaque(false);
statusComboBox = new GlobalStatusSelectorBox(this);
// Align status combo box with account name field.
statusComboBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
TransparentPanel statusToolsPanel
= new TransparentPanel(new BorderLayout(0, 0));
SIPCommMenuBar statusMenuBar = new SIPCommMenuBar();
statusMenuBar.add(statusComboBox);
statusToolsPanel.add(statusMenuBar, BorderLayout.WEST);
toolbarPluginPanel
= new TransparentPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0));
mainToolbarPluginContainer = new PluginContainer(toolbarPluginPanel,
Container.CONTAINER_MAIN_TOOL_BAR);
statusToolsPanel.add(toolbarPluginPanel, BorderLayout.EAST);
statusMessageLabel.setFont(
statusMessageLabel.getFont().deriveFont(9f));
statusMessageLabel.setForeground(Color.GRAY);
rightPanel.setLayout(new BorderLayout(0, 0));
rightPanel.add(accountNameLabel, BorderLayout.NORTH);
rightPanel.add(statusMessageLabel, BorderLayout.CENTER);
rightPanel.add(statusToolsPanel, BorderLayout.SOUTH);
this.add(accountImageLabel, BorderLayout.WEST);
this.add(rightPanel, BorderLayout.CENTER);
southPluginPanel = new TransparentPanel(new BorderLayout());
southPluginContainer = new PluginContainer(
southPluginPanel,
Container.CONTAINER_ACCOUNT_SOUTH);
this.add(southPluginPanel, BorderLayout.SOUTH);
loadSkin();
GuiActivator.getUIService().addPluginComponentListener(this);
GlobalDisplayDetailsService gDService
= GuiActivator.getGlobalDisplayDetailsService();
if(gDService != null)
{
gDService.addGlobalDisplayDetailsListener(this);
String globalDisplayName = gDService.getGlobalDisplayName();
if(!StringUtils.isNullOrEmpty(globalDisplayName))
accountNameLabel.setText(globalDisplayName);
}
}
/**
* Updates status message.
* @param text
*/
void setStatusMessage(String text)
{
if(text == null)
{
statusMessageLabel.setText("");
rightPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
}
else
{
rightPanel.setBorder(null);
statusMessageLabel.setText(text);
}
}
/**
* Adds the account given by <tt>protocolProvider</tt> in the contained
* status combo box.
* @param protocolProvider the <tt>ProtocolProviderService</tt>
* corresponding to the account to add
*/
public void addAccount(final ProtocolProviderService protocolProvider)
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
addAccount(protocolProvider);
}
});
return;
}
statusComboBox.addAccount(protocolProvider);
protocolProvider.addRegistrationStateChangeListener(this);
}
/**
* Removes the account given by <tt>protocolProvider</tt> from the contained
* status combo box.
* @param protocolProvider the <tt>ProtocolProviderService</tt>
* corresponding to the account to remove
*/
public void removeAccount(final ProtocolProviderService protocolProvider)
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
removeAccount(protocolProvider);
}
});
return;
}
if (containsAccount(protocolProvider))
{
statusComboBox.removeAccount(protocolProvider);
protocolProvider.removeRegistrationStateChangeListener(this);
}
}
/**
* Checks if an account corresponding to the given <tt>protocolProvider</tt>
* is contained in the contained status combo box.
* @param protocolProvider the <tt>ProtocolProviderService</tt>
* corresponding to the account to check for
* @return <tt>true</tt> to indicate that an account corresponding to the
* given <tt>protocolProvider</tt> is contained in the status box,
* <tt>false</tt> - otherwise
*/
public boolean containsAccount(ProtocolProviderService protocolProvider)
{
return statusComboBox.containsAccount(protocolProvider);
}
/**
* Updates the current status of the <tt>protocolProvider</tt> with the
* <tt>newStatus</tt>. If status is null uses the current status.
* @param protocolProvider the <tt>ProtocolProviderService</tt> to update
* @param newStatus the new status to set
*/
public void updateStatus(final ProtocolProviderService protocolProvider,
final PresenceStatus newStatus)
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
updateStatus(protocolProvider, newStatus);
}
});
return;
}
if(newStatus != null)
statusComboBox.updateStatus(protocolProvider, newStatus);
else
statusComboBox.updateStatus(protocolProvider);
}
/**
* Updates the current status of the <tt>protocolProvider</tt>.
* @param protocolProvider the <tt>ProtocolProviderService</tt> to update
*/
public void updateStatus( ProtocolProviderService protocolProvider)
{
updateStatus(protocolProvider, null);
}
/**
* Updates the image that is shown.
* @param img the new image.
*/
public void updateImage(ImageIcon img)
{
accountImageLabel.setImageIcon(img.getImage());
accountImageLabel.setMaximumSize(
new Dimension(AVATAR_ICON_WIDTH, AVATAR_ICON_HEIGHT));
revalidate();
repaint();
}
/**
* Starts connecting user interface for the given <tt>protocolProvider</tt>.
* @param protocolProvider the <tt>ProtocolProviderService</tt> to start
* connecting for
*/
public void startConnecting(final ProtocolProviderService protocolProvider)
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
startConnecting(protocolProvider);
}
});
return;
}
statusComboBox.startConnecting(protocolProvider);
}
/**
* Stops connecting user interface for the given <tt>protocolProvider</tt>.
* @param protocolProvider the <tt>ProtocolProviderService</tt> to stop
* connecting for
*/
public void stopConnecting(final ProtocolProviderService protocolProvider)
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
stopConnecting(protocolProvider);
}
});
return;
}
statusComboBox.stopConnecting(protocolProvider);
}
/**
* Returns <tt>true</tt> if there are selected status selector boxes,
* otherwise returns <tt>false</tt>.
* @return <tt>true</tt> if there are selected status selector boxes,
* otherwise returns <tt>false</tt>
*/
public boolean hasSelectedMenus()
{
return statusComboBox.hasSelectedMenus();
}
/**
* Paints this component.
* @param g the <tt>Graphics</tt> object used for painting
*/
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (logoBgImage != null)
{
Graphics2D g2 = (Graphics2D) g;
g.setColor(bgColor);
g2.setPaint(texture);
g2.fillRect(0, 0, this.getWidth(), this.getHeight());
g.drawImage(
logoBgImage,
this.getWidth() - logoBgImage.getWidth(null),
0,
null);
}
}
/**
* Indicates that a plug-in component is registered to be added in a
* container. If the plug-in component in the given event is registered for
* this container then we add it.
* @param event <tt>PluginComponentEvent</tt> that notified us
*/
public void pluginComponentAdded(PluginComponentEvent event)
{
PluginComponentFactory pluginComponent =
event.getPluginComponentFactory();
Container containerID = pluginComponent.getContainer();
/*
// avoid early creating of components by calling getComponent
Object component = pluginComponent.getComponent();
if (!(component instanceof Component))
return;
*/
if (containerID.equals(Container.CONTAINER_MAIN_TOOL_BAR)
|| containerID.equals(Container.CONTAINER_ACCOUNT_SOUTH))
{
this.revalidate();
this.repaint();
}
}
/**
* Indicates that a plug-in component is registered to be removed from a
* container. If the plug-in component in the given event is registered for
* this container then we remove it.
* @param event <tt>PluginComponentEvent</tt> that notified us
*/
public void pluginComponentRemoved(PluginComponentEvent event)
{
PluginComponentFactory pluginComponent =
event.getPluginComponentFactory();
Container pluginContainer = pluginComponent.getContainer();
/*Object component = pluginComponent.getComponent();
if (!(component instanceof Component))
return;
*/
if (pluginContainer.equals(Container.CONTAINER_MAIN_TOOL_BAR)
|| pluginContainer.equals(Container.CONTAINER_ACCOUNT_SOUTH))
{
this.revalidate();
this.repaint();
}
}
/**
* Called whenever a new avatar is defined for one of the protocols that we
* have subscribed for.
*
* @param event the event containing the new image
*/
public void globalDisplayNameChanged(
final GlobalDisplayNameChangeEvent event)
{
if (!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
globalDisplayNameChanged(event);
return;
}
});
return;
}
String displayName = event.getNewDisplayName();
if(!StringUtils.isNullOrEmpty(displayName))
accountNameLabel.setText(displayName);
}
/**
* Called whenever a new avatar is defined for one of the protocols that we
* have subscribed for.
*
* @param event the event containing the new image
*/
public void globalDisplayAvatarChanged(
final GlobalAvatarChangeEvent event)
{
if (!SwingUtilities.isEventDispatchThread())
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
globalDisplayAvatarChanged(event);
return;
}
});
byte[] avatarImage = event.getNewAvatar();
// If there is no avatar image set, then displays the default one.
if(avatarImage != null)
{
accountImageLabel.setImageIcon(avatarImage);
}
}
/**
* Updates account information when a protocol provider is registered.
* @param evt the <tt>RegistrationStateChangeEvent</tt> that notified us
* of the change
*/
public void registrationStateChanged(RegistrationStateChangeEvent evt)
{
ProtocolProviderService protocolProvider = evt.getProvider();
// There is nothing we can do when account is registering,
// will set only connecting state later.
// While dispatching the registering if the state of the provider
// changes to registered we may end with client logged off
// this may happen if registered is coming too quickly after registered
// Dispatching registering is doing some swing stuff which
// is scheduled in EDT and so can be executing when already registered
if (evt.getNewState().equals(RegistrationState.REGISTERING))
{
startConnecting(protocolProvider);
}
else
this.updateStatus(protocolProvider);
}
/**
* Loads images for the account status panel.
*/
public void loadSkin()
{
bgColor
= new Color(GuiActivator.getResources()
.getColor("service.gui.LOGO_BAR_BACKGROUND"));
logoBgImage
= ImageLoader.getImage(ImageLoader.WINDOW_TITLE_BAR);
// texture
BufferedImage bgImage
= ImageLoader.getImage(ImageLoader.WINDOW_TITLE_BAR_BG);
texture
= new TexturePaint(
bgImage,
new Rectangle(
0,
0,
bgImage.getWidth(null),
bgImage.getHeight(null)));
GuiActivator.getUIService().addPluginComponentListener(this);
byte[] avatar = null;
if(GuiActivator.getGlobalDisplayDetailsService() != null)
{
avatar = GuiActivator.getGlobalDisplayDetailsService()
.getGlobalDisplayAvatar();
}
if (avatar == null || avatar.length <= 0)
accountImageLabel.setImageIcon(ImageLoader
.getImage(ImageLoader.DEFAULT_USER_PHOTO));
else
accountImageLabel.setImageIcon(avatar);
}
/**
* Returns the name of the L&F class that renders this component.
*
* @return the string "TreeUI"
* @see JComponent#getUIClassID
* @see UIDefaults#getUI
*/
@Override
public String getUIClassID()
{
if(ConfigurationUtils.isTransparentWindowEnabled())
return uiClassID;
else
return super.getUIClassID();
}
}