package floobits.windows; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.ui.JBColor; import com.intellij.ui.components.JBScrollPane; import floobits.actions.*; import floobits.common.interfaces.IContext; import floobits.common.protocol.FlooUser; import floobits.common.protocol.handlers.FlooHandler; import floobits.impl.ContextImpl; import floobits.impl.DocImpl; import floobits.utilities.Colors; import floobits.utilities.Flog; import floobits.utilities.IntelliUtils; import javax.swing.*; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.text.BadLocationException; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.text.SimpleDateFormat; import java.util.*; import java.util.List; public class FloobitsWindowForm { private JPanel chatPanel; private JScrollPane messagesScrollPane; private JButton chatButton; private JTextField chatInput; private JTextPane messages; private JScrollPane clientsScrollPane; private JPanel actionBarContainer; private JSplitPane splitPane; private JPanel clientsPane; private HTMLEditorKit kit; private HTMLDocument doc; private ContextImpl context; private boolean shouldScrollToBottom; private HashMap<String, ChatUserForm> userForms = new HashMap<String, ChatUserForm>(); public FloobitsWindowForm(final ContextImpl context) { super(); this.context = context; kit = new HTMLEditorKit(); doc = new HTMLDocument(); messages.setEditorKit(kit); messages.setDocument(doc); messages.setEditable(false); messages.addHyperlinkListener(new HyperlinkListener() { public void hyperlinkUpdate(HyperlinkEvent e) { IntelliUtils.handleHyperLink(e, context.project); } }); try { kit.insertHTML(doc, doc.getLength(), "<style>body { color: #7f7f7f;}</style>", 0, 0, null); } catch (Throwable e) { Flog.warn("Couldn't insert initial HTML into chat %s.", e.toString()); } messagesScrollPane.getVerticalScrollBar().addAdjustmentListener(new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent e) { if (shouldScrollToBottom) { e.getAdjustable().setValue(e.getAdjustable().getMaximum()); } shouldScrollToBottom = false; } }); chatInput.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { Flog.log("Got action in chat"); sendChatContents(); } }); chatButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { Flog.log("Got action from chat button"); sendChatContents(); } }); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { splitPane.setDividerLocation(300); } }); } private void createUIComponents() { clientsScrollPane = new JBScrollPane(); clientsPane = new JPanel(); clientsScrollPane.setViewportView(clientsPane); clientsPane.setLayout(new BoxLayout(clientsPane, BoxLayout.Y_AXIS)); actionBarContainer = new JPanel(); actionBarContainer.setPreferredSize(new Dimension(30, -1)); String connectLabel = "Connect to project's workspace"; String disconnectLabel = "Leave current workspace"; String openBrowserLabel = "Open Workspace in Browser"; String openSettingsLabel = "Open Workspace Settings in Browser"; String clearHighlightsLabel = "Clear Highlights"; String summonLabel = "Summon everyone in workspace to current cursor location"; String followLabel = "Follow all changes in workspace"; String unFollowLabel = "Stop following changes"; String helpLabel = "Get help with using Floobits"; ActionGroup group = new DefaultActionGroup( new AnAction(connectLabel, connectLabel, AllIcons.Actions.Execute) { @Override public void actionPerformed(AnActionEvent e) { new OpenProjectInWorkspace().actionPerformed(e); } @Override public void update(AnActionEvent e) { if (context == null) { return; } FlooHandler flooHandler = context.getFlooHandler(); e.getPresentation().setEnabled(flooHandler == null); } }, new AnAction(disconnectLabel, disconnectLabel, AllIcons.Actions.Suspend) { @Override public void actionPerformed(AnActionEvent e) { new LeaveWorkspace().actionPerformed(e); } @Override public void update(AnActionEvent e) { if (context == null) { return; } FlooHandler flooHandler = context.getFlooHandler(); e.getPresentation().setEnabled(flooHandler != null); } }, new AnAction(clearHighlightsLabel, clearHighlightsLabel, AllIcons.ObjectBrowser.ShowEditorHighlighting) { @Override public void actionPerformed(AnActionEvent e) { new ClearHighlights().actionPerformed(e); } @Override public void update(AnActionEvent e) { HashMap<Integer, HashMap<String, LinkedList<RangeHighlighter>>> highlights = DocImpl.highlights; e.getPresentation().setEnabled(highlights.size() > 0); } }, new AnAction(summonLabel, summonLabel, AllIcons.Ide.IncomingChangesOn) { @Override public void actionPerformed(AnActionEvent e) { new Summon().actionPerformed(e); } @Override public void update(AnActionEvent e) { if (context == null) { return; } FlooHandler flooHandler = context.getFlooHandler(); e.getPresentation().setEnabled(flooHandler != null && flooHandler.state.users.size() > 1); } }, new AnAction(followLabel, followLabel, AllIcons.Actions.Share) { @Override public void actionPerformed(AnActionEvent e) { if (context == null) { return; } FlooHandler handler = context.getFlooHandler(); if (handler == null) { return; } handler.state.setFollowing(true); handler.editorEventHandler.goToLastHighlight(); } @Override public void update(AnActionEvent e) { if (context == null) { return; } FlooHandler flooHandler = context.getFlooHandler(); e.getPresentation().setEnabled(flooHandler != null && !flooHandler.state.getFollowing()); } }, new AnAction(unFollowLabel, unFollowLabel, AllIcons.Actions.Unshare) { @Override public void actionPerformed(AnActionEvent e) { if (context == null) { return; } FlooHandler handler = context.getFlooHandler(); if (handler == null) { return; } handler.state.setFollowing(false); } @Override public void update(AnActionEvent e) { if (context == null) { return; } FlooHandler flooHandler = context.getFlooHandler(); e.getPresentation().setEnabled(flooHandler != null && flooHandler.state.getFollowing()); } }, new AnAction(openBrowserLabel, openBrowserLabel, AllIcons.Actions.Nextfile) { @Override public void actionPerformed(AnActionEvent e) { new OpenWorkspaceInBrowser().actionPerformed(e); } }, new AnAction(openSettingsLabel, openSettingsLabel, AllIcons.General.Settings) { @Override public void actionPerformed(AnActionEvent e) { new OpenSettingsInBrowser().actionPerformed(e); } }, new AnAction(helpLabel, helpLabel, AllIcons.Actions.Help) { @Override public void actionPerformed(AnActionEvent e) { new GoToHelp().actionPerformed(e); } } ); ActionManager actionManager = ActionManager.getInstance(); ActionToolbar tb = actionManager.createActionToolbar(ActionPlaces.UNKNOWN, group, false); actionBarContainer.add(tb.getComponent()); } public void clearClients() { clientsPane.removeAll(); userForms.clear(); } public void addUser(FlooUser user) { ChatUserForm userForm; userForm = userForms.get(user.username); if (userForm == null) { userForm = new ChatUserForm(context, user.username, user.gravatar); userForms.put(user.username, userForm); } userForm.addClient(user.client, user.platform, user.user_id); clientsPane.add(userForm.getContainerPanel()); chatPanel.validate(); } private void sendChatContents() { FlooHandler flooHandler = context.getFlooHandler(); if (flooHandler == null) { return; } String chatContents = chatInput.getText().trim(); if (chatContents.length() < 1) { return; } flooHandler.editorEventHandler.msg(chatContents); chatInput.setText(""); chatMessage(flooHandler.state.getUsername(flooHandler.state.getMyConnectionId()), chatContents, null); } public JPanel getChatPanel() { return chatPanel; } public void statusMessage(String message) { String messageFormat = "%s<b><i><span style=\"color:green\">%s</span></i></b><br/>"; addMessage(String.format(messageFormat, stampMessage("*status*", null), message)); } public void errorMessage(String message) { String messageFormat = "%s<b><i><span style=\"color:red\">%s</span></i></b><br/>"; addMessage(String.format(messageFormat, stampMessage("*error*", null), message)); } public void chatMessage(String username, String msg, Date messageDate) { FlooHandler flooHandler = context.getFlooHandler(); if (flooHandler == null) { return; } addMessage(String.format("%s%s<br/>", stampMessage(username, messageDate), msg)); } protected void addMessage(String msg) { shouldScrollToBottom = true; try { kit.insertHTML(doc, doc.getLength(), msg, 0, 0, null); } catch (IOException e) { Flog.warn("Was unable to add message to chat: %s at %d because %s", msg, doc.getLength(), e.toString()); } catch (BadLocationException e) { Flog.warn("Was unable to add message to chat: %s at %d because %s", msg, doc.getLength(), e.toString()); } } protected String stampMessage(String nick, Date when) { if (when == null) { when = new Date(); } String displayNick = nick; if (!nick.equals("*error*") && !nick.equals("*status*")) { JBColor color = Colors.getColorForUser(nick); String rgba = Colors.getHex(color); displayNick = String.format(" <span style=\"background-color:%s;color:white;\"> </span> %s ", rgba, nick); } return String.format("<span style=\"color:gray;\">[%s]</span> <b>%s</b>: ", new SimpleDateFormat("HH:mm:ss").format(when), displayNick); } public void removeUser(FlooUser user) { ChatUserForm userForm = userForms.get(user.username); if (userForm == null) { Flog.info("Could not find userForm for %d %s when trying to remove a user.", user.user_id, user.username); return; } userForm.removeClient(user.user_id); if (userForm.getNumClients() < 1) { clientsPane.remove(userForm.getContainerPanel()); userForms.remove(user.username); } chatPanel.validate(); } public void updateGravatars() { for (ChatUserForm userForm : userForms.values()) { userForm.updateGravatar(); } chatPanel.validate(); } public void updateFollowing(List<String> followedUsers) { for (Map.Entry<String, ChatUserForm> userFormSet: userForms.entrySet()) { userFormSet.getValue().setFollowing(followedUsers.contains(userFormSet.getKey())); } } }