package org.chartsy.chatsy; import java.awt.Dimension; import org.chartsy.chatsy.chat.component.ChatCheckBox; import org.chartsy.chatsy.chat.component.ChatButton; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GradientPaint; 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.io.IOException; import java.util.prefs.PreferenceChangeEvent; import java.util.prefs.PreferenceChangeListener; import java.util.prefs.Preferences; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.packet.XMPPError; import org.jivesoftware.smack.util.StringUtils; import org.chartsy.chatsy.chat.SessionManager; import org.chartsy.chatsy.chat.ChatsyManager; import org.chartsy.chatsy.chat.Workspace; import org.chartsy.chatsy.chat.util.SwingWorker; import org.chartsy.chatsy.chat.util.log.Log; import org.chartsy.chatsy.chat.util.log.NotifyUtil; import org.openide.util.ImageUtilities; import org.openide.util.NbBundle; import org.openide.util.NbPreferences; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; /** * * @author Viorel */ public class ChatTopComponent extends TopComponent implements PreferenceChangeListener { private static ChatTopComponent instance; static final String ICON_PATH = "org/chartsy/chatsy/resources/chat.png"; private static final String PREFERRED_ID = "ChatTopComponent"; private Preferences preferences = NbPreferences.root().node("/org/chartsy/register"); private Preferences chatPref = NbPreferences.root().node("/org/chartsy/chat"); private RegisterPanel registerPanel = null; private LoginPanel loginPanel = null; private ChatTopComponent() { putClientProperty("TopComponentAllowDockAnywhere", Boolean.TRUE); setName(NbBundle.getMessage(ChatTopComponent.class, "CTL_ChatFrame")); setToolTipText(NbBundle.getMessage(ChatTopComponent.class, "HINT_ChatFrame")); setIcon(ImageUtilities.loadImage(ICON_PATH, true)); initComponents(); } public static synchronized ChatTopComponent getDefault() { if (instance == null) instance = new ChatTopComponent(); return instance; } public static synchronized ChatTopComponent findInstance() { TopComponent tc = WindowManager.getDefault().findTopComponent(PREFERRED_ID); if (tc == null) { Log.warning( "Cannot find " + PREFERRED_ID + " component. It will not be " + "located properly in the window system."); return getDefault(); } if (tc instanceof ChatTopComponent) return (ChatTopComponent) tc; Log.warning( "There seem to be multiple components with the '" + PREFERRED_ID + "' ID. That is a potential source of errors and unexpected behavior."); return getDefault(); } private void initComponents() { setLayout(new BorderLayout()); setOpaque(true); chatPref.putBoolean("loggedin", false); if (preferences.getBoolean("registred", false)) { if (loginPanel == null) loginPanel = new LoginPanel(); add(loginPanel, BorderLayout.CENTER); } else { if (registerPanel == null) registerPanel = new RegisterPanel(); add(registerPanel, BorderLayout.CENTER); } preferences.addPreferenceChangeListener((PreferenceChangeListener) this); } public void putLoginScreen() { removeAll(); if (loginPanel == null) loginPanel = new LoginPanel(); add(loginPanel, BorderLayout.CENTER); validate(); repaint(); } public void putChatsScreen() { removeAll(); add(MainWindow.getInstance()); validate(); repaint(); } @Override protected void paintComponent(Graphics g) { if (!isOpaque()) { super.paintComponent(g); return; } Graphics2D g2 = (Graphics2D) g; int w = getWidth(); int h = getHeight(); GradientPaint gradientPaint = new GradientPaint( 0, h, Color.decode("0x065a9d"), w, 0, Color.decode("0x0298db")); g2.setPaint(gradientPaint); g2.fillRect(0, 0, w, h); setOpaque(false); super.paintComponent(g); setOpaque(true); } @Override public int getPersistenceType() { return TopComponent.PERSISTENCE_ALWAYS; } @Override protected String preferredID() { return PREFERRED_ID; } @Override protected void componentClosed() { if (chatPref.getBoolean("loggedin", false)) { MainWindow.getInstance().logout(); chatPref.putBoolean("loggedin", false); } super.componentClosed(); } @Override public void preferenceChange(PreferenceChangeEvent evt) { preferences = evt.getNode(); if (preferences.getBoolean("registred", false)) { if (loginPanel == null) loginPanel = new LoginPanel(); removeAll(); add(loginPanel, BorderLayout.CENTER); validate(); repaint(); } else { if (registerPanel == null) registerPanel = new RegisterPanel(); removeAll(); add(registerPanel, BorderLayout.CENTER); validate(); repaint(); } } private final class RegisterPanel extends JPanel { private JLabel registerLabel; public RegisterPanel() { setLayout(new BorderLayout()); setOpaque(false); registerLabel = new JLabel("<html>To start the chat you need to be registred.</html>"); registerLabel.setOpaque(false); registerLabel.setFont(new Font("Dialog", Font.BOLD, 14)); registerLabel.setForeground(Color.white); registerLabel.setHorizontalTextPosition(JLabel.CENTER); registerLabel.setVerticalTextPosition(JLabel.CENTER); add(registerLabel, BorderLayout.CENTER); } } private final class LoginPanel extends JPanel implements ActionListener, CallbackHandler { private static final String ICON_PATH = "org/chartsy/chatsy/resources/login_logo.png"; private final JLabel logoLabel = new JLabel(); private final ChatCheckBox autoLoginBox = new ChatCheckBox("Auto Login"); private final ChatButton loginButton = new ChatButton("Login"); private final LoadingLabel loadingLabel; private final CardLayout cardLayout = new CardLayout(0, 5); final JPanel cardPanel = new JPanel(cardLayout); final JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); private XMPPConnection connection = null; public LoginPanel() { setLayout(new GridBagLayout()); setOpaque(false); autoLoginBox.setOpaque(false); logoLabel.setOpaque(false); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(20,0,15,0); logoLabel.setIcon(ImageUtilities.loadImageIcon(ICON_PATH, true)); add(logoLabel, gbc); autoLoginBox.setSelected(chatPref.getBoolean("autologin", false)); add(autoLoginBox, gbc); buttonPanel.add(loginButton); buttonPanel.setOpaque(false); gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(5,0,5,0); add(buttonPanel, gbc); ImageIcon icon = new ImageIcon(ChatTopComponent.class.getResource("resources/loading.gif")); loadingLabel = new LoadingLabel(icon); add(loadingLabel, gbc); loginButton.addActionListener((ActionListener)this); autoLoginBox.addActionListener((ActionListener)this); if (preferences.getBoolean("registred", false)) if (chatPref.getBoolean("autologin", false)) loginButton.doClick(); } private String getUsername() { return StringUtils.escapeNode(preferences.get("username", "")); } private String getPassword() { return preferences.get("password", ""); } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == loginButton) validateLogin(); else if (e.getSource() == autoLoginBox) chatPref.putBoolean("autologin", autoLoginBox.isSelected()); } @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback callback : callbacks) { if (callback instanceof NameCallback) { NameCallback ncb = (NameCallback) callback; ncb.setName(getUsername()); } else if (callback instanceof PasswordCallback) { PasswordCallback pcb = (PasswordCallback) callback; pcb.setPassword(getPassword().toCharArray()); } else { String message = "Unknown callback requested: " + callback.getClass().getSimpleName(); Log.error(message); } } } private void validateLogin() { final SwingWorker loginValidationThread = new SwingWorker() { @Override public Object construct() { boolean loginSuccessfull = login(); if (loginSuccessfull) { chatPref.putBoolean("loggedin", true); startChat(); } else { loadingLabel.setVisible(false); } return loginSuccessfull; } }; loadingLabel.setVisible(true); loginValidationThread.start(); } private boolean login() { final SessionManager sessionManager = ChatsyManager.getSessionManager(); boolean hasErrors = false; String errorMessage = null; String serverName = "chat.mrswing.com"; if (!hasErrors) { SmackConfiguration.setPacketReplyTimeout(10000); try { ConnectionConfiguration config = new ConnectionConfiguration(serverName); config.setReconnectionAllowed(true); config.setRosterLoadedAtLogin(true); config.setSendPresence(true); connection = new XMPPConnection(config, this); connection.connect(); connection.login(getUsername(), getPassword()); sessionManager.setServerAddress(connection.getServiceName()); sessionManager.initializeSession(connection, getUsername(), getPassword()); sessionManager.setJID(connection.getUser()); } catch (XMPPException ex) { final XMPPError error = ex.getXMPPError(); int errorCode = 0; if (error != null) errorCode = error.getCode(); if (errorCode == 401) errorMessage = "Invalid username or password"; else if (errorCode == 502 || errorCode == 504) errorMessage = "Server unavailable"; else if (errorCode == 409) errorMessage = "Conflict error"; else errorMessage = "Can't connect to server"; Log.error(errorMessage, ex); hasErrors = true; } if (hasErrors) { final String finalErrorMessage = errorMessage; EventQueue.invokeLater(new Runnable() { @Override public void run() { chatPref.putBoolean("loggedin", false); loadingLabel.setVisible(false); NotifyUtil.error("Login Error", finalErrorMessage, false); } }); return false; } } connection.addConnectionListener(ChatsyManager.getSessionManager()); return !hasErrors; } private void startChat() { try { EventQueue.invokeLater(new Runnable() { @Override public void run() { chatPref.putBoolean("loggedin", true); Presence presence = new Presence(Presence.Type.available); connection.sendPacket(presence); final MainWindow mainWindow = MainWindow.getInstance(); Workspace workspace = Workspace.getInstance(); mainWindow.add(workspace.getCardPanel(), BorderLayout.CENTER); putChatsScreen(); workspace.buildLayout(); } }); } catch (Exception e) { Log.error(e); } } } private final class LoadingLabel extends JLabel { private final Image background; private LoadingLabel(ImageIcon icon) { super(icon); setOpaque(false); setHorizontalAlignment(CENTER); setVerticalAlignment(CENTER); setVisible(false); background = ImageUtilities.loadImage("org/chartsy/chatsy/resources/loading-bg.png", true); } @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.drawImage(background, 0, 0, this); super.paintComponent(g); } @Override public Dimension getPreferredSize() { Dimension dimension = new Dimension(46, 48); return dimension; } } }