Changeset 33602 in osm for applications/editors/josm/plugins
- Timestamp:
- 2017-09-14T23:12:25+02:00 (8 years ago)
- Location:
- applications/editors/josm/plugins/geochat
- Files:
-
- 3 edited
-
build.xml (modified) (1 diff)
-
src/geochat/ChatPaneManager.java (modified) (1 diff)
-
src/geochat/GeoChatPanel.java (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/geochat/build.xml
r33545 r33602 5 5 <property name="commit.message" value="[josm_geochat] copypaste from keyboard, font size advanced parameters"/> 6 6 <!-- enter the *lowest* JOSM version this plugin is currently compatible with --> 7 <property name="plugin.main.version" value="12 643"/>7 <property name="plugin.main.version" value="12743"/> 8 8 9 9 <property name="plugin.author" value="Ilya Zverev"/> -
applications/editors/josm/plugins/geochat/src/geochat/ChatPaneManager.java
r33545 r33602 32 32 */ 33 33 class ChatPaneManager { 34 private static final String PUBLIC_PANE = "Public Pane";35 36 private GeoChatPanel panel;37 private JTabbedPane tabs;38 private Map<String, ChatPane> chatPanes;39 private boolean collapsed;40 41 ChatPaneManager(GeoChatPanel panel, JTabbedPane tabs) {42 this.panel = panel;43 this.tabs = tabs;44 this.collapsed = panel.isDialogInCollapsedView();45 chatPanes = new HashMap<>();46 createChatPane(null);47 tabs.addChangeListener(new ChangeListener() {48 @Override49 public void stateChanged(ChangeEvent e) {50 updateActiveTabStatus();51 }52 });53 }54 55 public void setCollapsed(boolean collapsed) {56 this.collapsed = collapsed;57 updateActiveTabStatus();58 }59 60 public boolean hasUser(String user) {61 return chatPanes.containsKey(user == null ? PUBLIC_PANE : user);62 }63 64 public Component getPublicChatComponent() {65 return chatPanes.get(PUBLIC_PANE).component;66 }67 68 public int getNotifyLevel() {69 int alarm = 0;70 for (ChatPane entry : chatPanes.values()) {71 if (entry.notify > alarm)72 alarm = entry.notify;73 }74 return alarm;75 }76 77 public void updateActiveTabStatus() {78 if (tabs.getSelectedIndex() >= 0)79 ((ChatTabTitleComponent) tabs.getTabComponentAt(tabs.getSelectedIndex())).updateAlarm();80 }81 82 public void notify(String user, int alarmLevel) {83 if (alarmLevel <= 0 || !hasUser(user))84 return;85 ChatPane entry = chatPanes.get(user == null ? PUBLIC_PANE : user);86 entry.notify = alarmLevel;87 int idx = tabs.indexOfComponent(entry.component);88 if (idx >= 0)89 ((ChatTabTitleComponent) tabs.getTabComponentAt(idx)).updateAlarm();90 }91 92 public static int MESSAGE_TYPE_DEFAULT = 0;93 public static int MESSAGE_TYPE_INFORMATION = 1;94 public static int MESSAGE_TYPE_ATTENTION = 2;95 private static Color COLOR_ATTENTION = new Color(0, 0, 192);96 97 private void addLineToChatPane(String userName, String line, final int messageType) {98 if (line.length() == 0)99 return;100 if (!chatPanes.containsKey(userName))101 createChatPane(userName);102 final String nline = line.startsWith("\n") ? line : "\n" + line;103 final JTextPane thepane = chatPanes.get(userName).pane;104 GuiHelper.runInEDT(new Runnable() {105 @Override106 public void run() {107 Document doc = thepane.getDocument();108 try {109 SimpleAttributeSet attrs = null;110 if (messageType != MESSAGE_TYPE_DEFAULT) {111 attrs = new SimpleAttributeSet();112 if (messageType == MESSAGE_TYPE_INFORMATION)113 StyleConstants.setItalic(attrs, true);114 else if (messageType == MESSAGE_TYPE_ATTENTION)115 StyleConstants.setForeground(attrs, COLOR_ATTENTION);116 }117 doc.insertString(doc.getLength(), nline, attrs);118 } catch (BadLocationException ex) {119 Logging.warn(ex);120 }121 thepane.setCaretPosition(doc.getLength());122 }123 });124 }125 126 public void addLineToChatPane(String userName, String line) {127 addLineToChatPane(userName, line, MESSAGE_TYPE_DEFAULT);128 }129 130 public void addLineToPublic(String line) {131 addLineToChatPane(PUBLIC_PANE, line);132 }133 134 public void addLineToPublic(String line, int messageType) {135 addLineToChatPane(PUBLIC_PANE, line, messageType);136 }137 138 public void clearPublicChatPane() {139 chatPanes.get(PUBLIC_PANE).pane.setText("");140 }141 142 public void clearChatPane(String userName) {143 if (userName == null || userName.equals(PUBLIC_PANE))144 clearPublicChatPane();145 else146 chatPanes.get(userName).pane.setText("");147 }148 149 public void clearActiveChatPane() {150 clearChatPane(getActiveChatPane());151 }152 153 public ChatPane createChatPane(String userName) {154 JTextPane chatPane = new JTextPane();155 chatPane.setEditable(false);156 Font font = chatPane.getFont();157 float size = Main.pref.getInteger("geochat.fontsize", -1);158 if (size < 6)159 size += font.getSize2D();160 chatPane.setFont(font.deriveFont(size));161 // DefaultCaret caret = (DefaultCaret)chatPane.getCaret(); // does not work162 // caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);163 JScrollPane scrollPane = new JScrollPane(chatPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);164 chatPane.addMouseListener(new GeoChatPopupAdapter(panel));165 166 ChatPane entry = new ChatPane();167 entry.pane = chatPane;168 entry.component = scrollPane;169 entry.notify = 0;170 entry.userName = userName;171 entry.isPublic = userName == null;172 chatPanes.put(userName == null ? PUBLIC_PANE : userName, entry);173 174 tabs.addTab(null, scrollPane);175 tabs.setTabComponentAt(tabs.getTabCount() - 1, new ChatTabTitleComponent(entry));176 tabs.setSelectedComponent(scrollPane);177 return entry;178 }179 180 /**181 * Returns key in chatPanes hash map for the currently active182 * chat pane, or null in case of an error.183 */184 public String getActiveChatPane() {185 Component c = tabs.getSelectedComponent();186 if (c == null)187 return null;188 for (String user : chatPanes.keySet()) {189 if (c.equals(chatPanes.get(user).component))190 return user;191 }192 return null;193 }194 195 public String getRecipient() {196 String user = getActiveChatPane();197 return user == null || user.equals(PUBLIC_PANE) ? null : user;198 }199 200 public void closeChatPane(String user) {201 if (user == null || user.equals(PUBLIC_PANE) || !chatPanes.containsKey(user))202 return;203 tabs.remove(chatPanes.get(user).component);204 chatPanes.remove(user);205 }206 207 public void closeSelectedPrivatePane() {208 String pane = getRecipient();209 if (pane != null)210 closeChatPane(pane);211 }212 213 public void closePrivateChatPanes() {214 List<String> entries = new ArrayList<>(chatPanes.keySet());215 for (String user : entries) {216 if (!user.equals(PUBLIC_PANE))217 closeChatPane(user);218 }219 }220 221 public boolean hasSelectedText() {222 String user = getActiveChatPane();223 if (user != null) {224 JTextPane pane = chatPanes.get(user).pane;225 return pane.getSelectedText() != null;226 }227 return false;228 }229 230 public void copySelectedText() {231 String user = getActiveChatPane();232 if (user != null)233 chatPanes.get(user).pane.copy();234 }235 236 private class ChatTabTitleComponent extends JLabel {237 private ChatPane entry;238 239 ChatTabTitleComponent(ChatPane entry) {240 super(entry.isPublic ? tr("Public") : entry.userName);241 this.entry = entry;242 }243 244 private Font normalFont;245 private Font boldFont;246 247 public void updateAlarm() {248 if (normalFont == null) {249 // prepare cached fonts250 normalFont = getFont().deriveFont(Font.PLAIN);251 boldFont = getFont().deriveFont(Font.BOLD);252 }253 if (entry.notify > 0 && !collapsed && tabs.getSelectedIndex() == tabs.indexOfComponent(entry.component))254 entry.notify = 0;255 setFont(entry.notify > 0 ? boldFont : normalFont);256 panel.updateTitleAlarm();257 }258 }259 260 class ChatPane {261 public String userName;262 public boolean isPublic;263 public JTextPane pane;264 public JScrollPane component;265 public int notify;266 267 }34 private static final String PUBLIC_PANE = "Public Pane"; 35 36 private GeoChatPanel panel; 37 private JTabbedPane tabs; 38 private Map<String, ChatPane> chatPanes; 39 private boolean collapsed; 40 41 ChatPaneManager(GeoChatPanel panel, JTabbedPane tabs) { 42 this.panel = panel; 43 this.tabs = tabs; 44 this.collapsed = panel.isDialogInCollapsedView(); 45 chatPanes = new HashMap<>(); 46 createChatPane(null); 47 tabs.addChangeListener(new ChangeListener() { 48 @Override 49 public void stateChanged(ChangeEvent e) { 50 updateActiveTabStatus(); 51 } 52 }); 53 } 54 55 public void setCollapsed(boolean collapsed) { 56 this.collapsed = collapsed; 57 updateActiveTabStatus(); 58 } 59 60 public boolean hasUser(String user) { 61 return chatPanes.containsKey(user == null ? PUBLIC_PANE : user); 62 } 63 64 public Component getPublicChatComponent() { 65 return chatPanes.get(PUBLIC_PANE).component; 66 } 67 68 public int getNotifyLevel() { 69 int alarm = 0; 70 for (ChatPane entry : chatPanes.values()) { 71 if (entry.notify > alarm) 72 alarm = entry.notify; 73 } 74 return alarm; 75 } 76 77 public void updateActiveTabStatus() { 78 if (tabs.getSelectedIndex() >= 0) 79 ((ChatTabTitleComponent) tabs.getTabComponentAt(tabs.getSelectedIndex())).updateAlarm(); 80 } 81 82 public void notify(String user, int alarmLevel) { 83 if (alarmLevel <= 0 || !hasUser(user)) 84 return; 85 ChatPane entry = chatPanes.get(user == null ? PUBLIC_PANE : user); 86 entry.notify = alarmLevel; 87 int idx = tabs.indexOfComponent(entry.component); 88 if (idx >= 0) 89 ((ChatTabTitleComponent) tabs.getTabComponentAt(idx)).updateAlarm(); 90 } 91 92 public static int MESSAGE_TYPE_DEFAULT = 0; 93 public static int MESSAGE_TYPE_INFORMATION = 1; 94 public static int MESSAGE_TYPE_ATTENTION = 2; 95 private static Color COLOR_ATTENTION = new Color(0, 0, 192); 96 97 private void addLineToChatPane(String userName, String line, final int messageType) { 98 if (line.length() == 0) 99 return; 100 if (!chatPanes.containsKey(userName)) 101 createChatPane(userName); 102 final String nline = line.startsWith("\n") ? line : "\n" + line; 103 final JTextPane thepane = chatPanes.get(userName).pane; 104 GuiHelper.runInEDT(new Runnable() { 105 @Override 106 public void run() { 107 Document doc = thepane.getDocument(); 108 try { 109 SimpleAttributeSet attrs = null; 110 if (messageType != MESSAGE_TYPE_DEFAULT) { 111 attrs = new SimpleAttributeSet(); 112 if (messageType == MESSAGE_TYPE_INFORMATION) 113 StyleConstants.setItalic(attrs, true); 114 else if (messageType == MESSAGE_TYPE_ATTENTION) 115 StyleConstants.setForeground(attrs, COLOR_ATTENTION); 116 } 117 doc.insertString(doc.getLength(), nline, attrs); 118 } catch (BadLocationException ex) { 119 Logging.warn(ex); 120 } 121 thepane.setCaretPosition(doc.getLength()); 122 } 123 }); 124 } 125 126 public void addLineToChatPane(String userName, String line) { 127 addLineToChatPane(userName, line, MESSAGE_TYPE_DEFAULT); 128 } 129 130 public void addLineToPublic(String line) { 131 addLineToChatPane(PUBLIC_PANE, line); 132 } 133 134 public void addLineToPublic(String line, int messageType) { 135 addLineToChatPane(PUBLIC_PANE, line, messageType); 136 } 137 138 public void clearPublicChatPane() { 139 chatPanes.get(PUBLIC_PANE).pane.setText(""); 140 } 141 142 public void clearChatPane(String userName) { 143 if (userName == null || userName.equals(PUBLIC_PANE)) 144 clearPublicChatPane(); 145 else 146 chatPanes.get(userName).pane.setText(""); 147 } 148 149 public void clearActiveChatPane() { 150 clearChatPane(getActiveChatPane()); 151 } 152 153 public ChatPane createChatPane(String userName) { 154 JTextPane chatPane = new JTextPane(); 155 chatPane.setEditable(false); 156 Font font = chatPane.getFont(); 157 float size = Main.pref.getInteger("geochat.fontsize", -1); 158 if (size < 6) 159 size += font.getSize2D(); 160 chatPane.setFont(font.deriveFont(size)); 161 // DefaultCaret caret = (DefaultCaret)chatPane.getCaret(); // does not work 162 // caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); 163 JScrollPane scrollPane = new JScrollPane(chatPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 164 chatPane.addMouseListener(new GeoChatPopupAdapter(panel)); 165 166 ChatPane entry = new ChatPane(); 167 entry.pane = chatPane; 168 entry.component = scrollPane; 169 entry.notify = 0; 170 entry.userName = userName; 171 entry.isPublic = userName == null; 172 chatPanes.put(userName == null ? PUBLIC_PANE : userName, entry); 173 174 tabs.addTab(null, scrollPane); 175 tabs.setTabComponentAt(tabs.getTabCount() - 1, new ChatTabTitleComponent(entry)); 176 tabs.setSelectedComponent(scrollPane); 177 return entry; 178 } 179 180 /** 181 * Returns key in chatPanes hash map for the currently active 182 * chat pane, or null in case of an error. 183 */ 184 public String getActiveChatPane() { 185 Component c = tabs.getSelectedComponent(); 186 if (c == null) 187 return null; 188 for (String user : chatPanes.keySet()) { 189 if (c.equals(chatPanes.get(user).component)) 190 return user; 191 } 192 return null; 193 } 194 195 public String getRecipient() { 196 String user = getActiveChatPane(); 197 return user == null || user.equals(PUBLIC_PANE) ? null : user; 198 } 199 200 public void closeChatPane(String user) { 201 if (user == null || user.equals(PUBLIC_PANE) || !chatPanes.containsKey(user)) 202 return; 203 tabs.remove(chatPanes.get(user).component); 204 chatPanes.remove(user); 205 } 206 207 public void closeSelectedPrivatePane() { 208 String pane = getRecipient(); 209 if (pane != null) 210 closeChatPane(pane); 211 } 212 213 public void closePrivateChatPanes() { 214 List<String> entries = new ArrayList<>(chatPanes.keySet()); 215 for (String user : entries) { 216 if (!user.equals(PUBLIC_PANE)) 217 closeChatPane(user); 218 } 219 } 220 221 public boolean hasSelectedText() { 222 String user = getActiveChatPane(); 223 if (user != null) { 224 JTextPane pane = chatPanes.get(user).pane; 225 return pane.getSelectedText() != null; 226 } 227 return false; 228 } 229 230 public void copySelectedText() { 231 String user = getActiveChatPane(); 232 if (user != null) 233 chatPanes.get(user).pane.copy(); 234 } 235 236 private class ChatTabTitleComponent extends JLabel { 237 private ChatPane entry; 238 239 ChatTabTitleComponent(ChatPane entry) { 240 super(entry.isPublic ? tr("Public") : entry.userName); 241 this.entry = entry; 242 } 243 244 private Font normalFont; 245 private Font boldFont; 246 247 public void updateAlarm() { 248 if (normalFont == null) { 249 // prepare cached fonts 250 normalFont = getFont().deriveFont(Font.PLAIN); 251 boldFont = getFont().deriveFont(Font.BOLD); 252 } 253 if (entry.notify > 0 && !collapsed && tabs.getSelectedIndex() == tabs.indexOfComponent(entry.component)) 254 entry.notify = 0; 255 setFont(entry.notify > 0 ? boldFont : normalFont); 256 panel.updateTitleAlarm(); 257 } 258 } 259 260 static class ChatPane { 261 public String userName; 262 public boolean isPublic; 263 public JTextPane pane; 264 public JScrollPane component; 265 public int notify; 266 267 } 268 268 } -
applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java
r33545 r33602 36 36 import org.openstreetmap.josm.Main; 37 37 import org.openstreetmap.josm.data.Bounds; 38 import org.openstreetmap.josm.data.UserIdentityManager; 38 39 import org.openstreetmap.josm.data.coor.LatLon; 39 import org.openstreetmap.josm.gui.*; 40 import org.openstreetmap.josm.gui.MainApplication; 41 import org.openstreetmap.josm.gui.MapView; 42 import org.openstreetmap.josm.gui.Notification; 40 43 import org.openstreetmap.josm.gui.dialogs.ToggleDialog; 41 44 import org.openstreetmap.josm.gui.layer.MapViewPaintable; … … 50 53 */ 51 54 public class GeoChatPanel extends ToggleDialog implements ChatServerConnectionListener, MapViewPaintable { 52 private JTextField input;53 private JTabbedPane tabs;54 private JComponent noData;55 private JPanel loginPanel;56 private JPanel gcPanel;57 private ChatServerConnection connection;58 // those fields should be visible to popup menu actions59 Map<String, LatLon> users;60 ChatPaneManager chatPanes;61 boolean userLayerActive;62 63 public GeoChatPanel() {64 super(tr("GeoChat"), "geochat", tr("Open GeoChat panel"), null, 200, true);65 66 noData = new JLabel(tr("Zoom in to see messages"), SwingConstants.CENTER);67 68 tabs = new JTabbedPane();69 tabs.addMouseListener(new GeoChatPopupAdapter(this));70 chatPanes = new ChatPaneManager(this, tabs);71 72 input = new JPanelTextField() {73 @Override74 protected void processEnter(String text) {75 connection.postMessage(text, chatPanes.getRecipient());76 }77 78 @Override79 protected String autoComplete(String word, boolean atStart) {80 return autoCompleteUser(word, atStart);81 }82 };83 84 String defaultUserName = constructUserName();85 loginPanel = createLoginPanel(defaultUserName);86 87 gcPanel = new JPanel(new BorderLayout());88 gcPanel.add(loginPanel, BorderLayout.CENTER);89 createLayout(gcPanel, false, null);90 91 users = new TreeMap<>();92 // Start threads93 connection = ChatServerConnection.getInstance();94 connection.addListener(this);95 boolean autoLogin = Main.pref.get("geochat.username", null) == null ? false : Main.pref.getBoolean("geochat.autologin", true);96 connection.autoLoginWithDelay(autoLogin ? defaultUserName : null);97 updateTitleAlarm();98 }99 100 private String constructUserName() {101 String userName = Main.pref.get("geochat.username", null); // so the default is null102 if (userName == null)103 userName =JosmUserIdentityManager.getInstance().getUserName();104 if (userName == null)105 userName = "";106 if (userName.contains("@"))107 userName = userName.substring(0, userName.indexOf('@'));108 userName = userName.replace(' ', '_');109 return userName;110 }111 112 private JPanel createLoginPanel(String defaultUserName) {113 final JTextField nameField = new JPanelTextField() {114 @Override115 protected void processEnter(String text) {116 connection.login(text);117 }118 };119 nameField.setText(defaultUserName);120 121 JButton loginButton = new JButton(tr("Login"));122 loginButton.addActionListener(new ActionListener() {123 @Override124 public void actionPerformed(ActionEvent e) {125 connection.login(nameField.getText());126 }127 });128 nameField.setPreferredSize(new Dimension(nameField.getPreferredSize().width, loginButton.getPreferredSize().height));129 130 final JCheckBox autoLoginBox = new JCheckBox(tr("Enable autologin"), Main.pref.getBoolean("geochat.autologin", true));131 autoLoginBox.addActionListener(new ActionListener() {132 @Override133 public void actionPerformed(ActionEvent e) {134 Main.pref.put("geochat.autologin", autoLoginBox.isSelected());135 }136 });137 138 JPanel panel = new JPanel(new GridBagLayout());139 panel.add(nameField, GBC.std().fill(GridBagConstraints.HORIZONTAL).insets(15, 0, 5, 0));140 panel.add(loginButton, GBC.eol().fill(GridBagConstraints.NONE).insets(0, 0, 15, 0));141 panel.add(autoLoginBox, GBC.std().insets(15, 0, 15, 0));142 return panel;143 }144 145 protected void logout() {146 connection.logout();147 }148 149 @Override150 public void destroy() {151 try {152 if (Main.pref.getBoolean("geochat.logout.on.close", true)) {153 connection.removeListener(this);154 connection.bruteLogout();155 }156 } catch (IOException e) {157 Logging.warn("Failed to logout from geochat server: " + e.getMessage());158 }159 super.destroy();160 }161 162 private String autoCompleteUser(String word, boolean atStart) {163 String result = null;164 boolean singleUser = true;165 for (String user : users.keySet()) {166 if (user.startsWith(word)) {167 if (result == null)168 result = user;169 else {170 singleUser = false;171 int i = word.length();172 while (i < result.length() && i < user.length() && result.charAt(i) == user.charAt(i)) {173 i++;174 }175 if (i < result.length())176 result = result.substring(0, i);177 }178 }179 }180 return result == null ? null : !singleUser ? result : atStart ? result + ": " : result + " ";181 }182 183 /**184 * This is implementation of a "temporary layer". It paints circles185 * for all users nearby.186 */187 @Override188 public void paint(Graphics2D g, MapView mv, Bounds bbox) {189 Graphics2D g2d = (Graphics2D) g.create();190 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);191 Composite ac04 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f);192 Composite ac10 = g2d.getComposite();193 194 Font font = g2d.getFont().deriveFont(Font.BOLD, g2d.getFont().getSize2D() + 2.0f);195 g2d.setFont(font);196 FontMetrics fm = g2d.getFontMetrics();197 198 for (String user : users.keySet()) {199 int stringWidth = fm.stringWidth(user);200 int radius = stringWidth / 2 + 10;201 Point p = mv.getPoint(users.get(user));202 203 g2d.setComposite(ac04);204 g2d.setColor(Color.white);205 g2d.fillOval(p.x - radius, p.y - radius, radius * 2 + 1, radius * 2 + 1);206 207 g2d.setComposite(ac10);208 g2d.setColor(Color.black);209 g2d.drawString(user, p.x - stringWidth / 2, p.y + fm.getDescent());210 }211 }212 213 /* ================== Notifications in the title ======================= */214 215 /**216 * Display number of users and notifications in the panel title.217 */218 protected void updateTitleAlarm() {219 int alarmLevel = connection.isLoggedIn() ? chatPanes.getNotifyLevel() : 0;220 if (!isDialogInCollapsedView() && alarmLevel > 1)221 alarmLevel = 1;222 223 String comment;224 if (connection.isLoggedIn()) {225 comment = trn("{0} user", "{0} users", users.size() + 1, users.size() + 1);226 } else {227 comment = tr("not logged in");228 }229 230 String title = tr("GeoChat");231 if (comment != null)232 title = title + " (" + comment + ")";233 final String alarm = (alarmLevel <= 0 ? "" : alarmLevel == 1 ? "* " : "!!! ") + title;234 GuiHelper.runInEDT(new Runnable() {235 @Override236 public void run() {237 setTitle(alarm);238 }239 });240 }241 242 /**243 * Track panel collapse events.244 */245 @Override246 protected void setIsCollapsed(boolean val) {247 super.setIsCollapsed(val);248 chatPanes.setCollapsed(val);249 updateTitleAlarm();250 }251 252 /* ============ ChatServerConnectionListener methods ============= */253 254 @Override255 public void loggedIn(String userName) {256 Main.pref.put("geochat.username", userName);257 if (gcPanel.getComponentCount() == 1) {258 GuiHelper.runInEDTAndWait(new Runnable() {259 @Override260 public void run() {261 gcPanel.remove(0);262 gcPanel.add(tabs, BorderLayout.CENTER);263 gcPanel.add(input, BorderLayout.SOUTH);264 }265 });266 }267 updateTitleAlarm();268 }269 270 @Override271 public void notLoggedIn(final String reason) {272 if (reason != null) {273 GuiHelper.runInEDT(new Runnable() {274 @Override275 public void run() {276 new Notification(tr("Failed to log in to GeoChat:") + "\n" + reason).show();277 }278 });279 } else {280 // regular logout281 if (gcPanel.getComponentCount() > 1) {282 gcPanel.removeAll();283 gcPanel.add(loginPanel, BorderLayout.CENTER);284 }285 }286 updateTitleAlarm();287 }288 289 @Override290 public void messageSendFailed(final String reason) {291 GuiHelper.runInEDT(new Runnable() {292 @Override293 public void run() {294 new Notification(tr("Failed to send message:") + "\n" + reason).show();295 }296 });297 }298 299 @Override300 public void statusChanged(boolean active) {301 // only the public tab, because private chats don't rely on coordinates302 tabs.setComponentAt(0, active ? chatPanes.getPublicChatComponent() : noData);303 repaint();304 }305 306 @Override307 public void updateUsers(Map<String, LatLon> newUsers) {308 for (String uname : this.users.keySet()) {309 if (!newUsers.containsKey(uname))310 chatPanes.addLineToPublic(tr("User {0} has left", uname), ChatPaneManager.MESSAGE_TYPE_INFORMATION);311 }312 for (String uname : newUsers.keySet()) {313 if (!this.users.containsKey(uname))314 chatPanes.addLineToPublic(tr("User {0} is mapping nearby", uname), ChatPaneManager.MESSAGE_TYPE_INFORMATION);315 }316 this.users = newUsers;317 updateTitleAlarm();318 if (userLayerActive && MainApplication.isDisplayingMapView())319 MainApplication.getMap().mapView.repaint();320 }321 322 private final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm");323 324 private void formatMessage(StringBuilder sb, ChatMessage msg) {325 sb.append("\n");326 sb.append('[').append(TIME_FORMAT.format(msg.getTime())).append("] ");327 sb.append(msg.getAuthor()).append(": ").append(msg.getMessage());328 }329 330 @Override331 public void receivedMessages(boolean replace, List<ChatMessage> messages) {332 if (replace)333 chatPanes.clearPublicChatPane();334 if (!messages.isEmpty()) {335 int alarm = 0;336 StringBuilder sb = new StringBuilder();337 for (ChatMessage msg : messages) {338 boolean important = msg.isIncoming() && containsName(msg.getMessage());339 if (msg.isIncoming() && alarm < 2) {340 alarm = important ? 2 : 1;341 }342 if (important) {343 // add buffer, then add current line with italic, then clear buffer344 chatPanes.addLineToPublic(sb.toString());345 sb.setLength(0);346 formatMessage(sb, msg);347 chatPanes.addLineToPublic(sb.toString(), ChatPaneManager.MESSAGE_TYPE_ATTENTION);348 sb.setLength(0);349 } else350 formatMessage(sb, msg);351 }352 chatPanes.addLineToPublic(sb.toString());353 if (alarm > 0)354 chatPanes.notify(null, alarm);355 }356 if (replace)357 showNearbyUsers();358 }359 360 private void showNearbyUsers() {361 if (!users.isEmpty()) {362 StringBuilder sb = new StringBuilder(tr("Users mapping nearby:"));363 boolean first = true;364 for (String user : users.keySet()) {365 sb.append(first ? " " : ", ");366 sb.append(user);367 }368 chatPanes.addLineToPublic(sb.toString(), ChatPaneManager.MESSAGE_TYPE_INFORMATION);369 }370 }371 372 private boolean containsName(String message) {373 String userName = connection.getUserName();374 int length = userName.length();375 int i = message.indexOf(userName);376 while (i >= 0) {377 if ((i == 0 || !Character.isJavaIdentifierPart(message.charAt(i - 1)))378 && (i + length >= message.length() || !Character.isJavaIdentifierPart(message.charAt(i + length))))379 return true;380 i = message.indexOf(userName, i + 1);381 }382 return false;383 }384 385 @Override386 public void receivedPrivateMessages(boolean replace, List<ChatMessage> messages) {387 if (replace)388 chatPanes.closePrivateChatPanes();389 for (ChatMessage msg : messages) {390 StringBuilder sb = new StringBuilder();391 formatMessage(sb, msg);392 chatPanes.addLineToChatPane(msg.isIncoming() ? msg.getAuthor() : msg.getRecipient(), sb.toString());393 if (msg.isIncoming())394 chatPanes.notify(msg.getAuthor(), 2);395 }396 }55 private JTextField input; 56 private JTabbedPane tabs; 57 private JComponent noData; 58 private JPanel loginPanel; 59 private JPanel gcPanel; 60 private ChatServerConnection connection; 61 // those fields should be visible to popup menu actions 62 Map<String, LatLon> users; 63 ChatPaneManager chatPanes; 64 boolean userLayerActive; 65 66 public GeoChatPanel() { 67 super(tr("GeoChat"), "geochat", tr("Open GeoChat panel"), null, 200, true); 68 69 noData = new JLabel(tr("Zoom in to see messages"), SwingConstants.CENTER); 70 71 tabs = new JTabbedPane(); 72 tabs.addMouseListener(new GeoChatPopupAdapter(this)); 73 chatPanes = new ChatPaneManager(this, tabs); 74 75 input = new JPanelTextField() { 76 @Override 77 protected void processEnter(String text) { 78 connection.postMessage(text, chatPanes.getRecipient()); 79 } 80 81 @Override 82 protected String autoComplete(String word, boolean atStart) { 83 return autoCompleteUser(word, atStart); 84 } 85 }; 86 87 String defaultUserName = constructUserName(); 88 loginPanel = createLoginPanel(defaultUserName); 89 90 gcPanel = new JPanel(new BorderLayout()); 91 gcPanel.add(loginPanel, BorderLayout.CENTER); 92 createLayout(gcPanel, false, null); 93 94 users = new TreeMap<>(); 95 // Start threads 96 connection = ChatServerConnection.getInstance(); 97 connection.addListener(this); 98 boolean autoLogin = Main.pref.get("geochat.username", null) == null ? false : Main.pref.getBoolean("geochat.autologin", true); 99 connection.autoLoginWithDelay(autoLogin ? defaultUserName : null); 100 updateTitleAlarm(); 101 } 102 103 private String constructUserName() { 104 String userName = Main.pref.get("geochat.username", null); // so the default is null 105 if (userName == null) 106 userName = UserIdentityManager.getInstance().getUserName(); 107 if (userName == null) 108 userName = ""; 109 if (userName.contains("@")) 110 userName = userName.substring(0, userName.indexOf('@')); 111 userName = userName.replace(' ', '_'); 112 return userName; 113 } 114 115 private JPanel createLoginPanel(String defaultUserName) { 116 final JTextField nameField = new JPanelTextField() { 117 @Override 118 protected void processEnter(String text) { 119 connection.login(text); 120 } 121 }; 122 nameField.setText(defaultUserName); 123 124 JButton loginButton = new JButton(tr("Login")); 125 loginButton.addActionListener(new ActionListener() { 126 @Override 127 public void actionPerformed(ActionEvent e) { 128 connection.login(nameField.getText()); 129 } 130 }); 131 nameField.setPreferredSize(new Dimension(nameField.getPreferredSize().width, loginButton.getPreferredSize().height)); 132 133 final JCheckBox autoLoginBox = new JCheckBox(tr("Enable autologin"), Main.pref.getBoolean("geochat.autologin", true)); 134 autoLoginBox.addActionListener(new ActionListener() { 135 @Override 136 public void actionPerformed(ActionEvent e) { 137 Main.pref.put("geochat.autologin", autoLoginBox.isSelected()); 138 } 139 }); 140 141 JPanel panel = new JPanel(new GridBagLayout()); 142 panel.add(nameField, GBC.std().fill(GridBagConstraints.HORIZONTAL).insets(15, 0, 5, 0)); 143 panel.add(loginButton, GBC.eol().fill(GridBagConstraints.NONE).insets(0, 0, 15, 0)); 144 panel.add(autoLoginBox, GBC.std().insets(15, 0, 15, 0)); 145 return panel; 146 } 147 148 protected void logout() { 149 connection.logout(); 150 } 151 152 @Override 153 public void destroy() { 154 try { 155 if (Main.pref.getBoolean("geochat.logout.on.close", true)) { 156 connection.removeListener(this); 157 connection.bruteLogout(); 158 } 159 } catch (IOException e) { 160 Logging.warn("Failed to logout from geochat server: " + e.getMessage()); 161 } 162 super.destroy(); 163 } 164 165 private String autoCompleteUser(String word, boolean atStart) { 166 String result = null; 167 boolean singleUser = true; 168 for (String user : users.keySet()) { 169 if (user.startsWith(word)) { 170 if (result == null) 171 result = user; 172 else { 173 singleUser = false; 174 int i = word.length(); 175 while (i < result.length() && i < user.length() && result.charAt(i) == user.charAt(i)) { 176 i++; 177 } 178 if (i < result.length()) 179 result = result.substring(0, i); 180 } 181 } 182 } 183 return result == null ? null : !singleUser ? result : atStart ? result + ": " : result + " "; 184 } 185 186 /** 187 * This is implementation of a "temporary layer". It paints circles 188 * for all users nearby. 189 */ 190 @Override 191 public void paint(Graphics2D g, MapView mv, Bounds bbox) { 192 Graphics2D g2d = (Graphics2D) g.create(); 193 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 194 Composite ac04 = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f); 195 Composite ac10 = g2d.getComposite(); 196 197 Font font = g2d.getFont().deriveFont(Font.BOLD, g2d.getFont().getSize2D() + 2.0f); 198 g2d.setFont(font); 199 FontMetrics fm = g2d.getFontMetrics(); 200 201 for (String user : users.keySet()) { 202 int stringWidth = fm.stringWidth(user); 203 int radius = stringWidth / 2 + 10; 204 Point p = mv.getPoint(users.get(user)); 205 206 g2d.setComposite(ac04); 207 g2d.setColor(Color.white); 208 g2d.fillOval(p.x - radius, p.y - radius, radius * 2 + 1, radius * 2 + 1); 209 210 g2d.setComposite(ac10); 211 g2d.setColor(Color.black); 212 g2d.drawString(user, p.x - stringWidth / 2, p.y + fm.getDescent()); 213 } 214 } 215 216 /* ================== Notifications in the title ======================= */ 217 218 /** 219 * Display number of users and notifications in the panel title. 220 */ 221 protected void updateTitleAlarm() { 222 int alarmLevel = connection.isLoggedIn() ? chatPanes.getNotifyLevel() : 0; 223 if (!isDialogInCollapsedView() && alarmLevel > 1) 224 alarmLevel = 1; 225 226 String comment; 227 if (connection.isLoggedIn()) { 228 comment = trn("{0} user", "{0} users", users.size() + 1, users.size() + 1); 229 } else { 230 comment = tr("not logged in"); 231 } 232 233 String title = tr("GeoChat"); 234 if (comment != null) 235 title = title + " (" + comment + ")"; 236 final String alarm = (alarmLevel <= 0 ? "" : alarmLevel == 1 ? "* " : "!!! ") + title; 237 GuiHelper.runInEDT(new Runnable() { 238 @Override 239 public void run() { 240 setTitle(alarm); 241 } 242 }); 243 } 244 245 /** 246 * Track panel collapse events. 247 */ 248 @Override 249 protected void setIsCollapsed(boolean val) { 250 super.setIsCollapsed(val); 251 chatPanes.setCollapsed(val); 252 updateTitleAlarm(); 253 } 254 255 /* ============ ChatServerConnectionListener methods ============= */ 256 257 @Override 258 public void loggedIn(String userName) { 259 Main.pref.put("geochat.username", userName); 260 if (gcPanel.getComponentCount() == 1) { 261 GuiHelper.runInEDTAndWait(new Runnable() { 262 @Override 263 public void run() { 264 gcPanel.remove(0); 265 gcPanel.add(tabs, BorderLayout.CENTER); 266 gcPanel.add(input, BorderLayout.SOUTH); 267 } 268 }); 269 } 270 updateTitleAlarm(); 271 } 272 273 @Override 274 public void notLoggedIn(final String reason) { 275 if (reason != null) { 276 GuiHelper.runInEDT(new Runnable() { 277 @Override 278 public void run() { 279 new Notification(tr("Failed to log in to GeoChat:") + "\n" + reason).show(); 280 } 281 }); 282 } else { 283 // regular logout 284 if (gcPanel.getComponentCount() > 1) { 285 gcPanel.removeAll(); 286 gcPanel.add(loginPanel, BorderLayout.CENTER); 287 } 288 } 289 updateTitleAlarm(); 290 } 291 292 @Override 293 public void messageSendFailed(final String reason) { 294 GuiHelper.runInEDT(new Runnable() { 295 @Override 296 public void run() { 297 new Notification(tr("Failed to send message:") + "\n" + reason).show(); 298 } 299 }); 300 } 301 302 @Override 303 public void statusChanged(boolean active) { 304 // only the public tab, because private chats don't rely on coordinates 305 tabs.setComponentAt(0, active ? chatPanes.getPublicChatComponent() : noData); 306 repaint(); 307 } 308 309 @Override 310 public void updateUsers(Map<String, LatLon> newUsers) { 311 for (String uname : this.users.keySet()) { 312 if (!newUsers.containsKey(uname)) 313 chatPanes.addLineToPublic(tr("User {0} has left", uname), ChatPaneManager.MESSAGE_TYPE_INFORMATION); 314 } 315 for (String uname : newUsers.keySet()) { 316 if (!this.users.containsKey(uname)) 317 chatPanes.addLineToPublic(tr("User {0} is mapping nearby", uname), ChatPaneManager.MESSAGE_TYPE_INFORMATION); 318 } 319 this.users = newUsers; 320 updateTitleAlarm(); 321 if (userLayerActive && MainApplication.isDisplayingMapView()) 322 MainApplication.getMap().mapView.repaint(); 323 } 324 325 private final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm"); 326 327 private void formatMessage(StringBuilder sb, ChatMessage msg) { 328 sb.append("\n"); 329 sb.append('[').append(TIME_FORMAT.format(msg.getTime())).append("] "); 330 sb.append(msg.getAuthor()).append(": ").append(msg.getMessage()); 331 } 332 333 @Override 334 public void receivedMessages(boolean replace, List<ChatMessage> messages) { 335 if (replace) 336 chatPanes.clearPublicChatPane(); 337 if (!messages.isEmpty()) { 338 int alarm = 0; 339 StringBuilder sb = new StringBuilder(); 340 for (ChatMessage msg : messages) { 341 boolean important = msg.isIncoming() && containsName(msg.getMessage()); 342 if (msg.isIncoming() && alarm < 2) { 343 alarm = important ? 2 : 1; 344 } 345 if (important) { 346 // add buffer, then add current line with italic, then clear buffer 347 chatPanes.addLineToPublic(sb.toString()); 348 sb.setLength(0); 349 formatMessage(sb, msg); 350 chatPanes.addLineToPublic(sb.toString(), ChatPaneManager.MESSAGE_TYPE_ATTENTION); 351 sb.setLength(0); 352 } else 353 formatMessage(sb, msg); 354 } 355 chatPanes.addLineToPublic(sb.toString()); 356 if (alarm > 0) 357 chatPanes.notify(null, alarm); 358 } 359 if (replace) 360 showNearbyUsers(); 361 } 362 363 private void showNearbyUsers() { 364 if (!users.isEmpty()) { 365 StringBuilder sb = new StringBuilder(tr("Users mapping nearby:")); 366 boolean first = true; 367 for (String user : users.keySet()) { 368 sb.append(first ? " " : ", "); 369 sb.append(user); 370 } 371 chatPanes.addLineToPublic(sb.toString(), ChatPaneManager.MESSAGE_TYPE_INFORMATION); 372 } 373 } 374 375 private boolean containsName(String message) { 376 String userName = connection.getUserName(); 377 int length = userName.length(); 378 int i = message.indexOf(userName); 379 while (i >= 0) { 380 if ((i == 0 || !Character.isJavaIdentifierPart(message.charAt(i - 1))) 381 && (i + length >= message.length() || !Character.isJavaIdentifierPart(message.charAt(i + length)))) 382 return true; 383 i = message.indexOf(userName, i + 1); 384 } 385 return false; 386 } 387 388 @Override 389 public void receivedPrivateMessages(boolean replace, List<ChatMessage> messages) { 390 if (replace) 391 chatPanes.closePrivateChatPanes(); 392 for (ChatMessage msg : messages) { 393 StringBuilder sb = new StringBuilder(); 394 formatMessage(sb, msg); 395 chatPanes.addLineToChatPane(msg.isIncoming() ? msg.getAuthor() : msg.getRecipient(), sb.toString()); 396 if (msg.isIncoming()) 397 chatPanes.notify(msg.getAuthor(), 2); 398 } 399 } 397 400 }
Note:
See TracChangeset
for help on using the changeset viewer.
