// License: WTFPL. For details, see LICENSE file. package geochat; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextPane; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.gui.util.GuiHelper; /** * * @author zverik */ class ChatPaneManager { private static final String PUBLIC_PANE = "Public Pane"; private GeoChatPanel panel; private JTabbedPane tabs; private Map<String, ChatPane> chatPanes; private boolean collapsed; ChatPaneManager(GeoChatPanel panel, JTabbedPane tabs) { this.panel = panel; this.tabs = tabs; this.collapsed = panel.isDialogInCollapsedView(); chatPanes = new HashMap<>(); createChatPane(null); tabs.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { updateActiveTabStatus(); } }); } public void setCollapsed(boolean collapsed) { this.collapsed = collapsed; updateActiveTabStatus(); } public boolean hasUser(String user) { return chatPanes.containsKey(user == null ? PUBLIC_PANE : user); } public Component getPublicChatComponent() { return chatPanes.get(PUBLIC_PANE).component; } public int getNotifyLevel() { int alarm = 0; for (ChatPane entry : chatPanes.values()) { if (entry.notify > alarm) alarm = entry.notify; } return alarm; } public void updateActiveTabStatus() { if (tabs.getSelectedIndex() >= 0) ((ChatTabTitleComponent) tabs.getTabComponentAt(tabs.getSelectedIndex())).updateAlarm(); } public void notify(String user, int alarmLevel) { if (alarmLevel <= 0 || !hasUser(user)) return; ChatPane entry = chatPanes.get(user == null ? PUBLIC_PANE : user); entry.notify = alarmLevel; int idx = tabs.indexOfComponent(entry.component); if (idx >= 0) ((ChatTabTitleComponent) tabs.getTabComponentAt(idx)).updateAlarm(); } public static int MESSAGE_TYPE_DEFAULT = 0; public static int MESSAGE_TYPE_INFORMATION = 1; public static int MESSAGE_TYPE_ATTENTION = 2; private static Color COLOR_ATTENTION = new Color(0, 0, 192); private void addLineToChatPane(String userName, String line, final int messageType) { if (line.length() == 0) return; if (!chatPanes.containsKey(userName)) createChatPane(userName); final String nline = line.startsWith("\n") ? line : "\n" + line; final JTextPane thepane = chatPanes.get(userName).pane; GuiHelper.runInEDT(new Runnable() { @Override public void run() { Document doc = thepane.getDocument(); try { SimpleAttributeSet attrs = null; if (messageType != MESSAGE_TYPE_DEFAULT) { attrs = new SimpleAttributeSet(); if (messageType == MESSAGE_TYPE_INFORMATION) StyleConstants.setItalic(attrs, true); else if (messageType == MESSAGE_TYPE_ATTENTION) StyleConstants.setForeground(attrs, COLOR_ATTENTION); } doc.insertString(doc.getLength(), nline, attrs); } catch (BadLocationException ex) { Main.warn(ex); } thepane.setCaretPosition(doc.getLength()); } }); } public void addLineToChatPane(String userName, String line) { addLineToChatPane(userName, line, MESSAGE_TYPE_DEFAULT); } public void addLineToPublic(String line) { addLineToChatPane(PUBLIC_PANE, line); } public void addLineToPublic(String line, int messageType) { addLineToChatPane(PUBLIC_PANE, line, messageType); } public void clearPublicChatPane() { chatPanes.get(PUBLIC_PANE).pane.setText(""); } public void clearChatPane(String userName) { if (userName == null || userName.equals(PUBLIC_PANE)) clearPublicChatPane(); else chatPanes.get(userName).pane.setText(""); } public void clearActiveChatPane() { clearChatPane(getActiveChatPane()); } public ChatPane createChatPane(String userName) { JTextPane chatPane = new JTextPane(); chatPane.setEditable(false); Font font = chatPane.getFont(); float size = Main.pref.getInteger("geochat.fontsize", -1); if (size < 6) size += font.getSize2D(); chatPane.setFont(font.deriveFont(size)); // DefaultCaret caret = (DefaultCaret)chatPane.getCaret(); // does not work // caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); JScrollPane scrollPane = new JScrollPane(chatPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); chatPane.addMouseListener(new GeoChatPopupAdapter(panel)); ChatPane entry = new ChatPane(); entry.pane = chatPane; entry.component = scrollPane; entry.notify = 0; entry.userName = userName; entry.isPublic = userName == null; chatPanes.put(userName == null ? PUBLIC_PANE : userName, entry); tabs.addTab(null, scrollPane); tabs.setTabComponentAt(tabs.getTabCount() - 1, new ChatTabTitleComponent(entry)); tabs.setSelectedComponent(scrollPane); return entry; } /** * Returns key in chatPanes hash map for the currently active * chat pane, or null in case of an error. */ public String getActiveChatPane() { Component c = tabs.getSelectedComponent(); if (c == null) return null; for (String user : chatPanes.keySet()) { if (c.equals(chatPanes.get(user).component)) return user; } return null; } public String getRecipient() { String user = getActiveChatPane(); return user == null || user.equals(PUBLIC_PANE) ? null : user; } public void closeChatPane(String user) { if (user == null || user.equals(PUBLIC_PANE) || !chatPanes.containsKey(user)) return; tabs.remove(chatPanes.get(user).component); chatPanes.remove(user); } public void closeSelectedPrivatePane() { String pane = getRecipient(); if (pane != null) closeChatPane(pane); } public void closePrivateChatPanes() { List<String> entries = new ArrayList<>(chatPanes.keySet()); for (String user : entries) { if (!user.equals(PUBLIC_PANE)) closeChatPane(user); } } public boolean hasSelectedText() { String user = getActiveChatPane(); if (user != null) { JTextPane pane = chatPanes.get(user).pane; return pane.getSelectedText() != null; } return false; } public void copySelectedText() { String user = getActiveChatPane(); if (user != null) chatPanes.get(user).pane.copy(); } private class ChatTabTitleComponent extends JLabel { private ChatPane entry; ChatTabTitleComponent(ChatPane entry) { super(entry.isPublic ? tr("Public") : entry.userName); this.entry = entry; } private Font normalFont; private Font boldFont; public void updateAlarm() { if (normalFont == null) { // prepare cached fonts normalFont = getFont().deriveFont(Font.PLAIN); boldFont = getFont().deriveFont(Font.BOLD); } if (entry.notify > 0 && !collapsed && tabs.getSelectedIndex() == tabs.indexOfComponent(entry.component)) entry.notify = 0; setFont(entry.notify > 0 ? boldFont : normalFont); panel.updateTitleAlarm(); } } class ChatPane { public String userName; public boolean isPublic; public JTextPane pane; public JScrollPane component; public int notify; } }