/** * $RCSfile: ,v $ * $Revision: $ * $Date: $ * * Copyright (C) 2004-2011 Jive Software. All rights reserved. * * 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 org.jivesoftware.spark.ui; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.UIManager; import org.jivesoftware.resource.Res; import org.jivesoftware.resource.SparkRes; import org.jivesoftware.smack.RosterEntry; import org.jivesoftware.smack.packet.DefaultPacketExtension; import org.jivesoftware.smack.packet.PacketExtension; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.RosterPacket; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.spark.ChatManager; import org.jivesoftware.spark.PresenceManager; import org.jivesoftware.spark.SparkManager; import org.jivesoftware.spark.util.GraphicUtils; import org.jivesoftware.spark.util.ModelUtil; import org.jivesoftware.spark.util.log.Log; import org.jivesoftware.sparkimpl.plugin.layout.LayoutSettings; import org.jivesoftware.sparkimpl.plugin.layout.LayoutSettingsManager; import org.jivesoftware.sparkimpl.settings.local.LocalPreferences; import org.jivesoftware.sparkimpl.settings.local.SettingsManager; /** * Represent a single contact within the <code>ContactList</code>. */ public class ContactItem extends JPanel { private static final long serialVersionUID = 1514044406550293152L; private JLabel imageLabel; private JLabel displayNameLabel; private JLabel descriptionLabel; private String nickname; private String alias; private final String fullyQualifiedJID; private JLabel specialImageLabel; private Icon icon; private String status; private String groupName; boolean available; private Presence presence; private String hash = ""; private File contactsDir; private JLabel sideIcon; private int fontSize; private int iconSize; private boolean avatarsShowing; public ContactItem(String alias, String nickname, String fullyQualifiedJID) { this(alias, nickname, fullyQualifiedJID, true); } /** * Creates a new instance of a contact. * * @param alias the alias of the contact * @param nickname the nickname of the contact. * @param fullyQualifiedJID the fully-qualified jid of the contact (ex. derek@jivesoftware.com) */ public ContactItem(String alias, String nickname, String fullyQualifiedJID, boolean initUi) { setLayout(new GridBagLayout()); // Set Default Font final LocalPreferences pref = SettingsManager.getLocalPreferences(); fontSize = pref.getContactListFontSize(); iconSize = pref.getContactListIconSize(); avatarsShowing = pref.areAvatarsVisible(); // Set default presence presence = new Presence(Presence.Type.unavailable); contactsDir = new File(SparkManager.getUserDirectory(), "contacts"); this.alias = alias; this.nickname = nickname; this.fullyQualifiedJID = fullyQualifiedJID; if (initUi) { displayNameLabel = new JLabel(); descriptionLabel = new JLabel(); imageLabel = new JLabel(); specialImageLabel = new JLabel(); sideIcon = new JLabel(); if (avatarsShowing) { sideIcon.setMinimumSize(new Dimension(iconSize, iconSize)); sideIcon.setMaximumSize(new Dimension(iconSize, iconSize)); sideIcon.setPreferredSize(new Dimension(iconSize, iconSize)); } displayNameLabel.setHorizontalTextPosition(JLabel.LEFT); displayNameLabel.setHorizontalAlignment(JLabel.LEFT); //displayNameLabel.setText(nickname); descriptionLabel.setFont(new Font("Dialog", Font.PLAIN, fontSize)); descriptionLabel.setForeground((Color)UIManager.get("ContactItemDescription.foreground")); descriptionLabel.setHorizontalTextPosition(JLabel.LEFT); descriptionLabel.setHorizontalAlignment(JLabel.LEFT); this.setOpaque(true); add(imageLabel, new GridBagConstraints(0, 0, 1, 2, 0.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 15, 0, 0), 0, 0)); add(displayNameLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0)); add(descriptionLabel, new GridBagConstraints(2, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 2, 0), 0, 0)); add(specialImageLabel, new GridBagConstraints(3, 0, 1, 2, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0)); add(sideIcon, new GridBagConstraints(4, 0, 1, 2, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0)); setDisplayName(); } } /** * Returns the name that should be displayed to represent the the contact. * If an alias has been set, this alias will be returned. If no alias has * been set, the nickname will be returned. If that hasn't been set either, * the JID will be returned. * * @return a name suitable to be displayed */ public String getDisplayName() { final String displayName; if (alias != null) { displayName = alias; } else if (nickname != null) { displayName = nickname; } else { displayName = getJID(); } if (displayName != null) { return displayName; } else { return ""; // weird, but happens. } } /** * Returns the nickname of the contact. Note that for typical user-interface * related tasks, you probably should use {@link #getDisplayName()} instead. * * @return the contact nickname. */ public String getNickname() { return nickname; } /** * Sets the nickname of the contact. * * @param nickname the contact nickname. */ public void setNickname(String nickname) { this.nickname = nickname; if (alias == null) { setDisplayName(); } } /** * Returns the alias of the contact. Note that for typical user-interface * related tasks, you probably should use {@link #getDisplayName()} instead. * * @return the contact alias. */ public String getAlias() { return alias; } /** * Sets the alias of the contact. * * @param alias the contact alias. */ public void setAlias(String alias) { this.alias = alias; setDisplayName(); } /** * Updates the displayed name for the contact. This method tries to use an * alias first. If that's not set, the nickname will be used instead. If * that's not set either, the JID of the user will be used. */ protected void setDisplayName() { final String displayName = getDisplayName(); int nickLength = displayName.length(); LayoutSettings settings = LayoutSettingsManager.getLayoutSettings(); int windowWidth = settings.getMainWindowWidth(); if (nickLength > windowWidth) { displayNameLabel.setText(StringUtils.unescapeNode(displayName).substring(0, windowWidth) + "..."); } else { displayNameLabel.setText(StringUtils.unescapeNode(displayName)); } } /** * Returns the fully qualified JID of the contact. (If available). Otherwise will * return the bare jid. * * @return the fully qualified jid (ex. derek@jivesoftware.com). */ public String getJID() { return fullyQualifiedJID; } /** * Returns the icon showing the contacts current state or presence. * * @return the icon. */ public Icon getIcon() { return icon; } /** * Sets the current icon to use. * * @param icon the current icon to use. */ public void setIcon(Icon icon) { this.icon = icon; imageLabel.setIcon(icon); } /** * Returns the contacts current status based on their presence. * * @return the contacts current status. */ public String getStatus() { return status; } /** * Sets the contacts current status. * * @param status the contacts current status. */ public void setStatus(String status) { this.status = status; } /** * Returns the name of the <code>ContactGroup</code> that this contact belongs to. * * @return the name of the <code>ContactGroup</code>. */ public String getGroupName() { return groupName; } /** * Sets the name of the <code>ContactGrouop</code> that this contact belongs to. * * @param groupName the name of the ContactGroup. */ public void setGroupName(String groupName) { this.groupName = groupName; } public boolean isAvailable() { return available; } public void setAvailable(boolean available) { this.available = available; } /** * Returns the <code>JLabel</code> showing the users nickname. * * @return the nickname label. */ public JLabel getNicknameLabel() { return displayNameLabel; } /** * Returns the <code>JLabel</code> representing the description. * * @return the description label. */ public JLabel getDescriptionLabel() { return descriptionLabel; } /** * Returns the current presence of the contact. * * @return the users current presence. */ public Presence getPresence() { return presence; } /** * Sets the current presence on this contact item. * * @param presence the presence. */ public void setPresence(Presence presence) { this.presence = presence; final PacketExtension packetExtension = presence.getExtension("x", "vcard-temp:x:update"); // Handle vCard update packet. if (packetExtension != null && packetExtension instanceof DefaultPacketExtension) { DefaultPacketExtension o = (DefaultPacketExtension)packetExtension; String hash = o.getValue("photo"); if (hash != null) { this.hash = hash; if (!hashExists(hash)) { updateAvatar(); updateAvatarInSideIcon(); } } } updatePresenceIcon(presence); } /** * Checks to see if the hash already exists. * * @param hash the hash. * @return true if the hash exists, otherwise false. */ private boolean hashExists(String hash) { contactsDir.mkdirs(); final File imageFile = new File(contactsDir, hash); return imageFile.exists(); } /** * Returns the url of the avatar belonging to this contact. * * @return the url of the avatar. * @throws MalformedURLException thrown if the address is invalid. */ public URL getAvatarURL() throws MalformedURLException { contactsDir.mkdirs(); if (ModelUtil.hasLength(hash)) { final File imageFile = new File(contactsDir, hash); if (imageFile.exists()) { return imageFile.toURI().toURL(); } } return SparkManager.getVCardManager().getAvatarURLIfAvailable(getJID()); } /** * Persists the avatar locally based on the new hash. */ private void updateAvatar() { SparkManager.getVCardManager().addToQueue(getJID()); } public String toString() { return displayNameLabel.getText(); } /** * Updates the icon of the user based on their presence. * * @param presence the users presence. */ public void updatePresenceIcon(Presence presence) { ChatManager chatManager = SparkManager.getChatManager(); boolean handled = chatManager.fireContactItemPresenceChanged(this, presence); if (handled) { return; } String status = presence.getStatus(); Icon statusIcon = SparkRes.getImageIcon(SparkRes.GREEN_BALL); boolean isAvailable = false; if (status == null && presence.isAvailable()) { Presence.Mode mode = presence.getMode(); if (mode == Presence.Mode.available) { status = Res.getString("status.online"); isAvailable = true; } else if (mode == Presence.Mode.away) { status = Res.getString("status.away"); statusIcon = SparkRes.getImageIcon(SparkRes.IM_AWAY); } else if (mode == Presence.Mode.chat) { status = Res.getString("status.free.to.chat"); } else if (mode == Presence.Mode.dnd) { status = Res.getString("status.do.not.disturb"); statusIcon = SparkRes.getImageIcon(SparkRes.IM_AWAY); } else if (mode == Presence.Mode.xa) { status = Res.getString("status.extended.away"); statusIcon = SparkRes.getImageIcon(SparkRes.IM_AWAY); } } if (presence.isAvailable() && (presence.getMode() == Presence.Mode.dnd || presence.getMode() == Presence.Mode.away || presence.getMode() == Presence.Mode.xa)) { statusIcon = SparkRes.getImageIcon(SparkRes.IM_AWAY); } else if (presence.isAvailable()) { isAvailable = true; } else if (!presence.isAvailable()) { getNicknameLabel().setFont(new Font("Dialog", Font.PLAIN, fontSize)); getNicknameLabel().setForeground((Color)UIManager.get("ContactItemOffline.color")); RosterEntry entry = SparkManager.getConnection().getRoster().getEntry(getJID()); if (entry != null && (entry.getType() == RosterPacket.ItemType.none || entry.getType() == RosterPacket.ItemType.from) && RosterPacket.ItemStatus.SUBSCRIPTION_PENDING == entry.getStatus()) { // Do not move out of group. setIcon(SparkRes.getImageIcon(SparkRes.SMALL_QUESTION)); getNicknameLabel().setFont(new Font("Dialog", Font.PLAIN, fontSize)); setStatusText(Res.getString("status.pending")); } else { setIcon(null); setFont(new Font("Dialog", Font.PLAIN, fontSize)); getNicknameLabel().setFont(new Font("Dialog", Font.PLAIN, fontSize)); setAvailable(false); if (ModelUtil.hasLength(status)) { setStatusText(status); } else { setStatusText(""); } } sideIcon.setIcon(null); setAvailable(false); return; } Icon sIcon = PresenceManager.getIconFromPresence(presence); if (sIcon != null) { setIcon(sIcon); } else { setIcon(statusIcon); } if (status != null) { setStatus(status); } if (PresenceManager.isOnPhone(presence)) { statusIcon = SparkRes.getImageIcon(SparkRes.ON_PHONE_IMAGE); setIcon(statusIcon); } // Always change nickname label to black. getNicknameLabel().setForeground((Color)UIManager.get("ContactItemNickname.foreground")); if (isAvailable) { getNicknameLabel().setFont(new Font("Dialog", Font.PLAIN, fontSize)); if (Res.getString("status.online").equals(status) || Res.getString("available").equalsIgnoreCase(status)) { setStatusText(""); } else { setStatusText(status); } } else if (presence.isAvailable()) { getNicknameLabel().setFont(new Font("Dialog", Font.ITALIC, fontSize)); getNicknameLabel().setForeground(Color.gray); if (status != null) { setStatusText(status); } } setAvailable(true); } /** * Sets the status label text based on the users status. * * @param status the users status. */ public void setStatusText(String status) { setStatus(status); if (ModelUtil.hasLength(status)) { getDescriptionLabel().setText(" - " + status); } else { getDescriptionLabel().setText(""); } } /** * The icon should only be used to display avatars in contact list. if you want to add an icon * to indicated that this contact is a transport e.g you should use setSpecialIcon() * * @param icon the icon to use. */ public void setSideIcon(Icon icon) { sideIcon.setIcon(icon); } /** * The icon to use to show extra information about this contact. An example would be to * represent that this user is from a 3rd party transport. * * @param icon the icon to use. */ public void setSpecialIcon(Icon icon) { specialImageLabel.setIcon(icon); } /** * Shows that the user is coming online. */ public void showUserComingOnline() { // Change Font getNicknameLabel().setFont(new Font("Dialog", Font.BOLD, fontSize)); getNicknameLabel().setForeground(new Color(255, 128, 0)); } /** * Shows that the user is going offline. */ public void showUserGoingOfflineOnline() { // Change Font getNicknameLabel().setFont(new Font("Dialog", Font.BOLD, fontSize)); getNicknameLabel().setForeground(Color.red); } /** * Update avatar icon. */ public void updateAvatarInSideIcon() { try { final URL url = getAvatarURL(); if (url != null) { if (!avatarsShowing) { setSideIcon(null); } else { ImageIcon icon = new ImageIcon(url); icon = GraphicUtils.scale(icon, iconSize, iconSize); setSideIcon(icon); } } } catch (MalformedURLException e) { Log.error(e); } } protected JLabel getDisplayNameLabel() { return displayNameLabel; } protected String getFullyQualifiedJID() { return fullyQualifiedJID; } protected void setDisplayNameLabel(JLabel displayNameLabel) { this.displayNameLabel = displayNameLabel; } protected void setDescriptionLabel(JLabel descriptionLabel) { this.descriptionLabel = descriptionLabel; } protected JLabel getSpecialImageLabel() { return specialImageLabel; } protected void setSpecialImageLabel(JLabel specialImageLabel) { this.specialImageLabel = specialImageLabel; } }