Index: /applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java
===================================================================
--- /applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java	(revision 29543)
+++ /applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java	(revision 29544)
@@ -17,4 +17,5 @@
 import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
 import static org.openstreetmap.josm.tools.I18n.tr;
+import static org.openstreetmap.josm.tools.I18n.trn;
 
 /**
@@ -31,4 +32,5 @@
     private JPanel gcPanel;
     private ChatServerConnection connection;
+    private Map<String, LatLon> users;
     
     public GeoChatPanel() {
@@ -43,34 +45,24 @@
 
         tabs = new JTabbedPane();
-        tabs.addTab(tr("Public"), chatPane);
-
-        input = new JTextField() {
+        tabs.addTab(tr("Public"), new JScrollPane(chatPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
+
+        input = new JPanelTextField() {
             @Override
-            protected void processKeyEvent( KeyEvent e ) {
-                if( e.getID() == KeyEvent.KEY_PRESSED ) {
-                    int code = e.getKeyCode();
-                    if( code == KeyEvent.VK_ENTER ) {
-                        String text = input.getText();
-                        if( text.length() > 0 ) {
-                            connection.postMessage(text);
-                            input.setText("");
-                        }
-                    } else if( code == KeyEvent.VK_TAB ) {
-                        // todo: autocomplete name
-                    } else if( code == KeyEvent.VK_ESCAPE ) {
-                        if( Main.map != null && Main.map.mapView != null )
-                            Main.map.mapView.requestFocus();
-                    }
-                    // Do not pass other events to JOSM
-                    if( code != KeyEvent.VK_LEFT && code != KeyEvent.VK_HOME && code != KeyEvent.VK_RIGHT
-                            && code != KeyEvent.VK_END && code != KeyEvent.VK_BACK_SPACE && code != KeyEvent.VK_DELETE )
-                        e.consume();
-                }
-                super.processKeyEvent(e);
-            }
-
+            protected void processEnter( String text ) {
+                connection.postMessage(text);
+            }
+
+            @Override
+            protected String autoComplete( String word ) {
+                return word;
+            }
         };
 
-        final JTextField nameField = new JTextField();
+        final JTextField nameField = new JPanelTextField() {
+            @Override
+            protected void processEnter( String text ) {
+                connection.login(text);
+            }
+        };
         String userName = JosmUserIdentityManager.getInstance().getUserName();
         if( userName == null )
@@ -97,8 +89,38 @@
         createLayout(gcPanel, false, null);
 
+        users = new HashMap<String, LatLon>();
         // Start threads
         connection = ChatServerConnection.getInstance();
         connection.addListener(this);
         connection.checkLogin();
+    }
+
+    private void addLineToPublic( String line ) {
+        Document doc = chatPane.getDocument();
+        try {
+            doc.insertString(doc.getLength(), line, null);
+        } catch( BadLocationException ex ) {
+            // whatever
+        }
+    }
+
+    private String cachedTitle = "";
+    private int cachedAlarm = 0;
+
+    public void setTitle( String title ) {
+        setTitle(title, -1);
+    }
+
+    private void setTitleAlarm( int alarmLevel ) {
+        setTitle(null, alarmLevel);
+    }
+
+    private void setTitle( String title, int alarmLevel ) {
+        if( title != null )
+            cachedTitle = title;
+        if( alarmLevel >= 0 )
+            cachedAlarm = alarmLevel;
+        String alarm = cachedAlarm <= 0 ? "" : cachedAlarm == 1 ? "* " : "[!] ";
+        super.setTitle(alarm + cachedTitle);
     }
 
@@ -124,4 +146,16 @@
 
     public void updateUsers( Map<String, LatLon> users ) {
+        for( String name : this.users.keySet() ) {
+            if( !users.containsKey(name) )
+                addLineToPublic(tr("User {0} has left", name));
+        }
+        for( String name : users.keySet() ) {
+            if( !this.users.containsKey(name) )
+                addLineToPublic(tr("User {0} is mapping nearby", name));
+        }
+        // todo: update header with user count
+        setTitle(trn("GeoChat ({0} user)", "GeoChat({0} users)", users.size(), users.size()));
+        // todo: update users location
+        this.users = users;
     }
 
@@ -137,14 +171,38 @@
             sb.append(msg.getAuthor()).append(": ").append(msg.getMessage());
         }
-
-        Document doc = chatPane.getDocument();
-        try {
-            doc.insertString(doc.getLength(), sb.toString(), null);
-        } catch( BadLocationException ex ) {
-            // whatever
-        }
+        addLineToPublic(sb.toString());
     }
 
     public void receivedPrivateMessages( boolean replace, List<ChatMessage> messages ) {
     }
+
+    private class JPanelTextField extends JTextField {
+        @Override
+        protected void processKeyEvent( KeyEvent e ) {
+            if( e.getID() == KeyEvent.KEY_PRESSED ) {
+                int code = e.getKeyCode();
+                if( code == KeyEvent.VK_ENTER ) {
+                    String text = input.getText();
+                    if( text.length() > 0 ) {
+                        processEnter(text);
+                        input.setText("");
+                    }
+                } else if( code == KeyEvent.VK_TAB ) {
+                    autoComplete(""); // todo
+                } else if( code == KeyEvent.VK_ESCAPE ) {
+                    if( Main.map != null && Main.map.mapView != null )
+                        Main.map.mapView.requestFocus();
+                }
+                // Do not pass other events to JOSM
+                if( code != KeyEvent.VK_LEFT && code != KeyEvent.VK_HOME && code != KeyEvent.VK_RIGHT
+                        && code != KeyEvent.VK_END && code != KeyEvent.VK_BACK_SPACE && code != KeyEvent.VK_DELETE )
+                    e.consume();
+            }
+            super.processKeyEvent(e);
+        }
+
+        protected void processEnter( String text ) { }
+
+        protected String autoComplete( String word ) { return word; }
+    }
 }
