Index: /applications/editors/josm/plugins/geochat/build.xml
===================================================================
--- /applications/editors/josm/plugins/geochat/build.xml	(revision 29570)
+++ /applications/editors/josm/plugins/geochat/build.xml	(revision 29571)
@@ -22,6 +22,6 @@
     <property name="plugin.class" value="geochat.GeoChatPlugin"/>
     <property name="plugin.description" value="Talk with users editing the map nearby, be notified when someone comes close."/>
-    <property name="plugin.icon" value="images/geochat.png"/>
-    <property name="plugin.link" value="http://wiki.openstreetmap.org/wiki/GeoChat"/>
+    <property name="plugin.icon" value="images/dialogs/geochat.png"/>
+    <property name="plugin.link" value="http://wiki.openstreetmap.org/wiki/JOSM/Plugins/GeoChat"/>
 
     <target name="additional-manifest">
@@ -29,4 +29,5 @@
             <attribute name="ru_Author" value="Илья Зверев"/>
             <attribute name="ru_Plugin-Description" value="Панель для общения с редактирующими карту поблизости, а также способ узнать, когда кто-то собирается править окрестности."/>
+            <attribute name="ru_Plugin_Link" value="http://wiki.openstreetmap.org/wiki/RU:JOSM/Plugins/GeoChat"/>
         </manifest>
     </target>
Index: /applications/editors/josm/plugins/geochat/src/geochat/ChatPaneManager.java
===================================================================
--- /applications/editors/josm/plugins/geochat/src/geochat/ChatPaneManager.java	(revision 29570)
+++ /applications/editors/josm/plugins/geochat/src/geochat/ChatPaneManager.java	(revision 29571)
@@ -7,7 +7,5 @@
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
-import javax.swing.text.BadLocationException;
-import javax.swing.text.DefaultCaret;
-import javax.swing.text.Document;
+import javax.swing.text.*;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
@@ -53,10 +51,6 @@
         int alarm = 0;
         for( ChatPane entry : chatPanes.values() ) {
-            if( entry.notify ) {
-                if( entry.isPublic && alarm < 1 )
-                    alarm = 1;
-                else if( !entry.isPublic )
-                    alarm = 2;
-            }
+            if( entry.notify > alarm )
+                alarm = entry.notify;
         }
         return alarm;
@@ -68,12 +62,9 @@
     }
 
-    public void notify( String user, boolean really ) {
-//        if( user == null && !really && !collapsed )
-//            return;
-        if( !hasUser(user) )
+    public void notify( String user, int alarmLevel ) {
+        if( alarmLevel <= 0 || !hasUser(user) )
             return;
         ChatPane entry = chatPanes.get(user == null ? PUBLIC_PANE : user);
-        System.out.println("Notifying " + user);
-        entry.notify = true;
+        entry.notify = alarmLevel;
         int idx = tabs.indexOfComponent(entry.component);
         if( idx >= 0 )
@@ -82,4 +73,6 @@
 
     public void addLineToChatPane( String userName, String line ) {
+        if( line.length() == 0 )
+            return;
         if( !chatPanes.containsKey(userName) )
             createChatPane(userName);
@@ -98,4 +91,20 @@
     }
 
+    /**
+     * Special case: the line contains username, so it must be highlighted.
+     */
+    public void addLineToPublicEm( String line ) {
+        if( !line.startsWith("\n") )
+            line = "\n" + line;
+        Document doc = chatPanes.get(PUBLIC_PANE).pane.getDocument();
+        try {
+            SimpleAttributeSet attrs = new SimpleAttributeSet();
+            StyleConstants.setItalic(attrs, true);
+            doc.insertString(doc.getLength(), line, attrs);
+        } catch( BadLocationException ex ) {
+            // whatever
+        }
+    }
+
     public void clearPublicChatPane() {
         chatPanes.get(PUBLIC_PANE).pane.setText("");
@@ -139,5 +148,5 @@
         entry.pane = chatPane;
         entry.component = scrollPane;
-        entry.notify = false;
+        entry.notify = 0;
         entry.userName = userName;
         entry.isPublic = userName == null;
@@ -207,8 +216,7 @@
                 boldFont = getFont().deriveFont(Font.BOLD);
             }
-            System.out.println("clauses: collapsed=" + collapsed + ", tabs:" + tabs.getSelectedIndex() + "=" + tabs.indexOfComponent(entry.component));
-            if( entry.notify && !collapsed && tabs.getSelectedIndex() == tabs.indexOfComponent(entry.component) )
-                entry.notify = false;
-            setFont(entry.notify ? boldFont : normalFont);
+            if( entry.notify > 0 && !collapsed && tabs.getSelectedIndex() == tabs.indexOfComponent(entry.component) )
+                entry.notify = 0;
+            setFont(entry.notify > 0 ? boldFont : normalFont);
             panel.updateTitleAlarm();
         }
@@ -220,5 +228,5 @@
         public JTextPane pane;
         public JScrollPane component;
-        public boolean notify;
+        public int notify;
 
     }
Index: /applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java
===================================================================
--- /applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java	(revision 29570)
+++ /applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java	(revision 29571)
@@ -104,5 +104,19 @@
 
     private String autoCompleteUser( String word ) {
-        return word; // todo: write autocomplete
+        String result = null;
+        for( String user : users.keySet() ) {
+            if( user.startsWith(word) ) {
+                if( result == null )
+                    result = user;
+                else {
+                    int i = word.length();
+                    while( i < result.length() && i < user.length() && result.charAt(i) == user.charAt(i) )
+                        i++;
+                    if( i < result.length() )
+                        result = result.substring(0, i);
+                }
+            }
+        }
+        return result;
     }
 
@@ -245,14 +259,35 @@
             StringBuilder sb = new StringBuilder();
             for( ChatMessage msg : messages ) {
-                formatMessage(sb, msg);
-                if( msg.isIncoming() ) {
-                    // todo: alarm=2 for private messages
-                    alarm = 1;
+                boolean important = msg.isIncoming() && containsName(msg.getMessage());
+                if( msg.isIncoming() && alarm < 2 ) {
+                    alarm = important ? 2 : 1;
                 }
+                if( important ) {
+                    // add buffer, then add current line with italic, then clear buffer
+                    chatPanes.addLineToPublic(sb.toString());
+                    sb.setLength(0);
+                    formatMessage(sb, msg);
+                    chatPanes.addLineToPublicEm(sb.toString());
+                    sb.setLength(0);
+                } else
+                    formatMessage(sb, msg);
             }
             chatPanes.addLineToPublic(sb.toString());
             if( alarm > 0 )
-                chatPanes.notify(null, alarm > 1);
-        }
+                chatPanes.notify(null, alarm);
+        }
+    }
+
+    private boolean containsName( String message ) {
+        String userName = connection.getUserName();
+        int length = userName.length();
+        int i = message.indexOf(userName);
+        while( i >= 0 ) {
+            if( (i == 0 || !Character.isJavaIdentifierPart(message.charAt(i - 1)))
+                    && (i + length >= message.length() || !Character.isJavaIdentifierPart(message.charAt(i + length))) )
+                return true;
+            i = message.indexOf(userName, i + 1);
+        }
+        return false;
     }
 
@@ -265,5 +300,5 @@
             chatPanes.addLineToChatPane(msg.isIncoming() ? msg.getAuthor() : msg.getRecipient(), sb.toString());
             if( msg.isIncoming() )
-                chatPanes.notify(msg.getAuthor(), true);
+                chatPanes.notify(msg.getAuthor(), 2);
         }
     }
Index: /applications/editors/josm/plugins/geochat/src/geochat/JPanelTextField.java
===================================================================
--- /applications/editors/josm/plugins/geochat/src/geochat/JPanelTextField.java	(revision 29570)
+++ /applications/editors/josm/plugins/geochat/src/geochat/JPanelTextField.java	(revision 29571)
@@ -1,6 +1,9 @@
 package geochat;
 
+import java.awt.KeyboardFocusManager;
 import java.awt.event.KeyEvent;
+import java.util.*;
 import javax.swing.JTextField;
+import javax.swing.KeyStroke;
 import org.openstreetmap.josm.Main;
 
@@ -12,4 +15,8 @@
  */
 public class JPanelTextField extends JTextField {
+
+    public JPanelTextField() {
+        setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, new HashSet<KeyStroke>());
+    }
 
     @Override
@@ -24,8 +31,24 @@
                 }
             } else if( code == KeyEvent.VK_TAB ) {
-                String word = ""; // todo: get the word
-                String complete = word == null ? null : autoComplete(word);
-                if( complete != null && !complete.equals(word) ) {
-                    // todo: replace the word
+                String text = getText();
+                int caret = getCaretPosition();
+                int start = caret - 1;
+                while( start >= 0 && Character.isJavaIdentifierPart(text.charAt(start)) )
+                    start--;
+                start++;
+                System.out.println("Autocomplete! Word " + start + "-" + caret + " (not inclusive)");
+                if( start < caret ) {
+                    String word = text.substring(start, caret);
+                    String complete = word == null ? null : autoComplete(word);
+                    if( complete != null && !complete.equals(word) ) {
+                        StringBuilder sb = new StringBuilder();
+                        if( start > 0 )
+                            sb.append(text.substring(0, start));
+                        sb.append(complete);
+                        if( caret < text.length() )
+                            sb.append(text.substring(caret));
+                        setText(sb.toString());
+                        setCaretPosition(start + complete.length());
+                    }
                 }
             } else if( code == KeyEvent.VK_ESCAPE ) {
