/** * $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.status; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.AffineTransform; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.TimerTask; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; import javax.swing.border.Border; import org.jivesoftware.resource.Default; import org.jivesoftware.resource.Res; import org.jivesoftware.resource.SparkRes; import org.jivesoftware.smack.packet.PacketExtension; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smackx.packet.VCard; import org.jivesoftware.spark.PresenceManager; import org.jivesoftware.spark.SparkManager; import org.jivesoftware.spark.ui.CommandPanel; import org.jivesoftware.spark.ui.PresenceListener; import org.jivesoftware.spark.util.GraphicUtils; import org.jivesoftware.spark.util.ImageCombiner; import org.jivesoftware.spark.util.ModelUtil; import org.jivesoftware.spark.util.StringUtils; import org.jivesoftware.spark.util.SwingTimerTask; import org.jivesoftware.spark.util.SwingWorker; import org.jivesoftware.spark.util.TaskEngine; import org.jivesoftware.spark.util.log.Log; import org.jivesoftware.sparkimpl.plugin.privacy.PrivacyManager; import org.jivesoftware.sparkimpl.plugin.privacy.list.SparkPrivacyList; import org.jivesoftware.sparkimpl.profile.VCardEditor; import org.jivesoftware.sparkimpl.profile.VCardListener; import org.jivesoftware.sparkimpl.profile.VCardManager; import org.jivesoftware.spark.util.UIComponentRegistry; //TODO: I need to remove the presence logic from this class. public class StatusBar extends JPanel implements VCardListener { private static final long serialVersionUID = -4322806442034868526L; private List<StatusItem> statusList = new ArrayList<StatusItem>(); private JLabel imageLabel = new JLabel(); private JLabel descriptiveLabel = new JLabel(); private JLabel nicknameLabel = new JLabel(); private StatusPanel statusPanel = new StatusPanel(); private CommandPanel commandPanel ; private Image backgroundImage; private Runnable changePresenceRunnable; private Presence currentPresence; public StatusBar() { this(true); } public StatusBar(boolean doLayout) { commandPanel = UIComponentRegistry.createCommandPanel(); if (doLayout) { setLayout(new GridBagLayout()); backgroundImage = Default.getImageIcon(Default.TOP_BOTTOM_BACKGROUND_IMAGE).getImage(); ImageIcon brandedImage = Default.getImageIcon(Default.BRANDED_IMAGE); if (brandedImage != null && brandedImage.getIconWidth() > 1) { final JLabel brandedLabel = new JLabel(brandedImage); add(brandedLabel, new GridBagConstraints(3, 0, 1, 3, 1.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(2, 2, 2, 2), 0, 0)); } add(imageLabel, new GridBagConstraints(0, 0, 1, 3, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.BOTH, new Insets(2, 8, 2, 2), 0, 0)); add(nicknameLabel, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(2, 12, 0, 0), 0, 0)); add(statusPanel, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 7, 0, 0), 0, 0)); add(commandPanel, new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0, GridBagConstraints.SOUTHWEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0)); nicknameLabel.setToolTipText(SparkManager.getConnection().getUser()); nicknameLabel.setFont(new Font(Font.DIALOG, Font.BOLD, 12)); setStatus(Res.getString("status.online")); } buildStatusItemList(); currentPresence = new Presence(Presence.Type.available, Res.getString("status.online"), 1, Presence.Mode.available); //setBorder(BorderFactory.createLineBorder(new Color(197, 213, 230), 1)); SparkManager.getSessionManager().addPresenceListener(new PresenceListener() { public void presenceChanged(Presence presence) { presence.setStatus(StringUtils.modifyWildcards(presence.getStatus())); changeAvailability(presence); } }); // Show profile on double click of image label imageLabel.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent mouseEvent) { if (mouseEvent.getClickCount() == 1) { VCardManager vcardManager = SparkManager.getVCardManager(); VCardEditor editor = new VCardEditor(); editor.editProfile(vcardManager.getVCard(), SparkManager.getWorkspace()); } } public void mouseEntered(MouseEvent e) { imageLabel.setCursor(GraphicUtils.HAND_CURSOR); } public void mouseExited(MouseEvent e) { imageLabel.setCursor(GraphicUtils.DEFAULT_CURSOR); } }); final TimerTask task = new SwingTimerTask() { public void doRun() { SparkManager.getVCardManager().addVCardListener(SparkManager.getWorkspace().getStatusBar()); } }; TaskEngine.getInstance().schedule(task, 3000); changePresenceRunnable = new Runnable() { @Override public void run() { updatePresence(); } }; } public void setAvatar(Icon icon) { Image image = ImageCombiner.iconToImage(icon); if (icon.getIconHeight() > 64 || icon.getIconWidth() > 64) { imageLabel.setIcon(new ImageIcon(image.getScaledInstance(-1, 64, Image.SCALE_SMOOTH))); } else { imageLabel.setIcon(icon); } imageLabel.setBorder(null); revalidate(); } public CommandPanel getCommandPanel() { return commandPanel; } public void setNickname(String nickname) { nicknameLabel.setText(nickname); } /** * Sets the current status text in the Status Manager. * * @param status the status to set. */ public void setStatus(String status) { statusPanel.setStatus(status); } protected void updatePresence() { setStatus(currentPresence.getStatus()); final Icon icon = PresenceManager.getIconFromPresence(currentPresence); if (icon != null) { statusPanel.setIcon(icon); } } public void showPopup(MouseEvent e) { final JPopupMenu popup = new JPopupMenu(); List<CustomStatusItem> custom = CustomMessages.load(); if (custom == null) { custom = new ArrayList<CustomStatusItem>(); } // Sort Custom Messages Collections.sort( custom, new Comparator<CustomStatusItem>() { public int compare( final CustomStatusItem a, final CustomStatusItem b ) { return( a.getStatus().compareToIgnoreCase( b.getStatus() ) ); } } ); // Build menu from StatusList for (final StatusItem statusItem : statusList) { final Action statusAction = new AbstractAction() { private static final long serialVersionUID = -192865863435381702L; public void actionPerformed(ActionEvent actionEvent) { final String text = statusItem.getText(); final StatusItem si = getStatusItem(text); if (si == null) { // Custom status Log.error("Unable to find status item for status - " + text); return; } SwingWorker worker = new SwingWorker() { public Object construct() { SparkManager.getSessionManager().changePresence(si.getPresence()); return "ok"; } public void finished() { setStatus(text); } }; worker.start(); } }; statusAction.putValue(Action.NAME, statusItem.getText()); statusAction.putValue(Action.SMALL_ICON, statusItem.getIcon()); // Has Children boolean hasChildren = false; for (Object aCustom : custom) { final CustomStatusItem cItem = (CustomStatusItem) aCustom; String type = cItem.getType(); if (type.equals(statusItem.getText())) { hasChildren = true; } } if (!hasChildren) { // Add as Menu Item popup.add(statusAction); } else { final JMenu mainStatusItem = new JMenu(statusAction); popup.add(mainStatusItem); // Add Custom Messages for (Object aCustom : custom) { final CustomStatusItem customItem = (CustomStatusItem) aCustom; String type = customItem.getType(); if (type.equals(statusItem.getText())) { // Add Child Menu Action action = new AbstractAction() { private static final long serialVersionUID = -1264239704492879742L; public void actionPerformed(ActionEvent actionEvent) { final String text = mainStatusItem.getText(); final StatusItem si = getStatusItem(text); if (si == null) { // Custom status Log.error("Unable to find status item for status - " + text); return; } SwingWorker worker = new SwingWorker() { public Object construct() { Presence oldPresence = si.getPresence(); Presence presence = copyPresence(oldPresence); presence.setStatus(customItem.getStatus()); presence.setPriority(customItem.getPriority()); SparkManager.getSessionManager().changePresence(presence); return "ok"; } public void finished() { String status = customItem.getType() + " - " + customItem.getStatus(); setStatus(status); } }; worker.start(); } }; action.putValue(Action.NAME, customItem.getStatus()); action.putValue(Action.SMALL_ICON, statusItem.getIcon()); mainStatusItem.add(action); } } // If menu has children, allow it to still be clickable. mainStatusItem.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent mouseEvent) { statusAction.actionPerformed(null); popup.setVisible(false); } }); } } //Add privacy menu if (PrivacyManager.getInstance().isPrivacyActive()) { JMenu privMenu = new JMenu(Res.getString("privacy.status.menu.entry")); privMenu.setIcon(SparkRes.getImageIcon("PRIVACY_ICON_SMALL")); final PrivacyManager pmanager = PrivacyManager.getInstance(); for (SparkPrivacyList plist : pmanager.getPrivacyLists()) { JMenuItem it = new JMenuItem(plist.getListName()); privMenu.add(it); if (plist.isActive()) { it.setIcon(SparkRes.getImageIcon("PRIVACY_LIGHTNING")); } else { it.setIcon(null); } final SparkPrivacyList finalList = plist; it.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { PrivacyManager.getInstance().setListAsActive(finalList.getListName()); } }); } if (pmanager.hasActiveList()) { JMenuItem remMenu = new JMenuItem(Res.getString("privacy.menuitem.deactivate.current.list", pmanager.getActiveList().getListName()), SparkRes.getImageIcon("PRIVACY_DEACTIVATE_LIST")); remMenu.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { pmanager.declineActiveList(); } }); privMenu.addSeparator(); privMenu.add(remMenu); } popup.add(privMenu); } // Add change message final JMenuItem changeStatusMenu = new JMenuItem(Res.getString("menuitem.set.status.message"), SparkRes.getImageIcon(SparkRes.BLANK_IMAGE)); popup.addSeparator(); popup.add(changeStatusMenu); changeStatusMenu.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { CustomMessages.addCustomMessage(); } }); Action editMessagesAction = new AbstractAction() { private static final long serialVersionUID = 7148051050075679995L; public void actionPerformed(ActionEvent actionEvent) { CustomMessages.editCustomMessages(); } }; editMessagesAction.putValue(Action.NAME, Res.getString("menuitem.edit.status.message")); popup.add(editMessagesAction); final JPanel panel = getStatusPanel(); popup.show(panel, 0, panel.getHeight()); } protected JPanel getStatusPanel() { return statusPanel; } public void changeAvailability(final Presence presence) { if (!presence.isAvailable()) { return; } if ((presence.getMode() == currentPresence.getMode()) && (presence.getType() == currentPresence.getType()) && (presence.getStatus().equals(currentPresence.getStatus()))) { PacketExtension pe = presence.getExtension("x", "vcard-temp:x:update"); if (pe != null) { // Update VCard loadVCard(); } return; } currentPresence = presence; SwingUtilities.invokeLater(changePresenceRunnable); } /** * Populates the current Dnd List. */ private void buildStatusItemList() { for (Presence presence : PresenceManager.getPresences()) { Icon icon = PresenceManager.getIconFromPresence(presence); StatusItem item = new StatusItem(presence, icon); statusList.add(item); } final Icon availableIcon = PresenceManager.getIconFromPresence(new Presence(Presence.Type.available)); // Set default presence icon (Avaialble) statusPanel.setIcon(availableIcon); } public Collection<StatusItem> getStatusList() { return statusList; } public Collection<CustomStatusItem> getCustomStatusList() { List<CustomStatusItem> custom = CustomMessages.load(); if (custom == null) custom = new ArrayList<CustomStatusItem>(); // Sort Custom Messages Collections.sort( custom, new Comparator<CustomStatusItem>() { public int compare( final CustomStatusItem a, final CustomStatusItem b ) { return( a.getStatus().compareToIgnoreCase( b.getStatus() ) ); } } ); return custom; } public Presence getPresence() { return currentPresence; } public StatusItem getStatusItem(String label) { for (StatusItem aStatusList : statusList) { if (aStatusList.getText().equals(label)) { return aStatusList; } } return null; } public void paintComponent(Graphics g) { super.paintComponent(g); if (backgroundImage != null) { double scaleX = getWidth() / (double)backgroundImage.getWidth(null); double scaleY = getHeight() / (double)backgroundImage.getHeight(null); AffineTransform xform = AffineTransform.getScaleInstance(scaleX, scaleY); ((Graphics2D)g).drawImage(backgroundImage, xform, this); } } public void loadVCard() { final Runnable loadVCard = new Runnable() { public void run() { VCard vcard = SparkManager.getVCardManager().getVCard(); updateVCardInformation(vcard); } }; TaskEngine.getInstance().submit(loadVCard); } protected void updateVCardInformation(final VCard vCard) { SwingUtilities.invokeLater(new Runnable() { public void run() { if (vCard.getError() == null) { String firstName = vCard.getFirstName(); String lastName = vCard.getLastName(); String nickname = vCard.getNickName(); if (ModelUtil.hasLength(firstName) && ModelUtil.hasLength(lastName)) { setNickname(firstName + " " + lastName); } else if (ModelUtil.hasLength(firstName)) { setNickname(firstName); } else if (ModelUtil.hasLength(nickname)) { setNickname(nickname); } else { nickname = SparkManager.getSessionManager().getUsername(); setNickname(nickname); } } else { String nickname = SparkManager.getSessionManager().getUsername(); setNickname(nickname); return; } byte[] avatarBytes = null; try { avatarBytes = vCard.getAvatar(); } catch (Exception e) { Log.error("Cannot retrieve avatar bytes.", e); } if (avatarBytes != null && avatarBytes.length > 0) { try { ImageIcon avatarIcon = new ImageIcon(avatarBytes); avatarIcon = VCardManager.scale(avatarIcon); setAvatar(avatarIcon); imageLabel.invalidate(); imageLabel.validate(); imageLabel.repaint(); } catch (Exception e) { // no issue } } else { imageLabel.setIcon(null); imageLabel.setBorder(null); imageLabel.invalidate(); imageLabel.validate(); imageLabel.repaint(); } } }); } public static Presence copyPresence(Presence presence) { return new Presence(presence.getType(), presence.getStatus(), presence.getPriority(), presence.getMode()); } /** * Return the nickname Component used to display the users profile name. * * @return the label. */ public JLabel getNicknameLabel() { return nicknameLabel; } private class StatusPanel extends JPanel { private static final long serialVersionUID = -5086334443225239032L; private JLabel iconLabel; private JLabel statusLabel; public StatusPanel() { super(); setOpaque(false); iconLabel = new JLabel(); statusLabel = new JLabel(); setLayout(new GridBagLayout()); // Remove padding from icon label iconLabel.setIconTextGap(0); add(iconLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); add(statusLabel, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 2, 0, 0), 0, 0)); statusLabel.setFont(new Font(Font.DIALOG, Font.PLAIN, 11)); statusLabel.setIcon(SparkRes.getImageIcon(SparkRes.DOWN_ARROW_IMAGE)); statusLabel.setHorizontalTextPosition(JLabel.LEFT); setOpaque(false); final Border border = BorderFactory.createEmptyBorder(2, 2, 2, 2); setBorder(border); statusLabel.addMouseListener(new MouseAdapter() { public void mouseReleased(MouseEvent e) { showPopup(e); } public void mouseEntered(MouseEvent e) { setCursor(GraphicUtils.HAND_CURSOR); setBorder(BorderFactory.createBevelBorder(0)); } public void mouseExited(MouseEvent e) { setCursor(GraphicUtils.DEFAULT_CURSOR); setBorder(border); } public void mousePressed(MouseEvent e) { setBorder(BorderFactory.createBevelBorder(1)); } }); } public void setStatus(String status) { int length = status.length(); String visualStatus = status; if (length > 30) { visualStatus = status.substring(0, 27) + "..."; } statusLabel.setText(visualStatus); statusLabel.setToolTipText(status); } public void setIcon(Icon icon) { iconLabel.setIcon(icon); } } public void setBackgroundImage(Image image) { this.backgroundImage = image; } public void setDescriptiveText(String text) { descriptiveLabel.setText(text); } public Dimension getPreferredSize() { Dimension dim = super.getPreferredSize(); dim.width = 0; return dim; } public void vcardChanged(VCard vcard) { updateVCardInformation(vcard); } protected Runnable getChangePresenceRunnable() { return changePresenceRunnable; } protected Presence getCurrentPresence() { return currentPresence; } }