Ignore:
Timestamp:
2013-05-09T11:03:28+02:00 (11 years ago)
Author:
zverik
Message:

[josm_geochat] refactored; notifications work

Location:
applications/editors/josm/plugins/geochat/src/geochat
Files:
3 added
3 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/geochat/src/geochat/ChatMessage.java

    r29558 r29568  
    1717    private long id;
    1818    private boolean priv;
     19    private boolean incoming;
    1920
    20     public ChatMessage( long id, LatLon pos, String author, String message, Date time ) {
     21    public ChatMessage( long id, LatLon pos, String author, boolean incoming, String message, Date time ) {
    2122        this.id = id;
    2223        this.author = author;
     
    2425        this.pos = pos;
    2526        this.time = time;
     27        this.incoming = incoming;
    2628        this.priv = false;
    2729        this.recipient = null;
     
    6365    }
    6466
     67    public boolean isIncoming() {
     68        return incoming;
     69    }
     70
    6571    public Date getTime() {
    6672        return time;
  • applications/editors/josm/plugins/geochat/src/geochat/ChatServerConnection.java

    r29558 r29568  
    337337                    String author = msg.getString("author");
    338338                    String message = msg.getString("message");
    339                     ChatMessage cm = new ChatMessage(id, new LatLon(lat, lon), author, message, new Date(timeStamp * 1000));
     339                    boolean incoming = msg.getBoolean("incoming");
     340                    ChatMessage cm = new ChatMessage(id, new LatLon(lat, lon), author,
     341                            incoming, message, new Date(timeStamp * 1000));
    340342                    cm.setPrivate(priv);
    341                     if( msg.has("recipient") && !msg.getBoolean("incoming") )
     343                    if( msg.has("recipient") && !incoming )
    342344                        cm.setRecipient(msg.getString("recipient"));
    343345                    result.add(cm);
  • applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java

    r29563 r29568  
    88import java.util.List;
    99import javax.swing.*;
    10 import javax.swing.text.BadLocationException;
    11 import javax.swing.text.DefaultCaret;
    12 import javax.swing.text.Document;
    1310import org.openstreetmap.josm.Main;
    1411import org.openstreetmap.josm.data.Bounds;
     
    2118import static org.openstreetmap.josm.tools.I18n.tr;
    2219import static org.openstreetmap.josm.tools.I18n.trn;
    23 import org.openstreetmap.josm.tools.ImageProvider;
    2420
    2521/**
     22 * Chat Panel. Contains of one public chat pane and multiple private ones.
    2623 *
    2724 * @author zverik
    2825 */
    2926public class GeoChatPanel extends ToggleDialog implements ChatServerConnectionListener, MapViewPaintable {
    30     private static final String PUBLIC_PANE = "Public Pane";
    31 
    3227    private JTextField input;
    3328    private JTabbedPane tabs;
     
    3631    private JPanel gcPanel;
    3732    private ChatServerConnection connection;
    38     private Map<String, LatLon> users;
    39     private Map<String, ChatLogEntry> chatPanes;
     33    // those fields should be visible to popup menu actions
     34    protected Map<String, LatLon> users;
     35    protected ChatPaneManager chatPanes;
     36    protected boolean userLayerActive;
    4037   
    4138    public GeoChatPanel() {
     
    4441        noData = new JLabel(tr("Zoom in to see messages"), SwingConstants.CENTER);
    4542
    46         chatPanes = new HashMap<String, ChatLogEntry>();
    4743        tabs = new JTabbedPane();
    48         createChatPane(null);
    49 
    50         tabs.addMouseListener(new PopupAdapter());
     44        tabs.addMouseListener(new GeoChatPopupAdapter(this));
     45        chatPanes = new ChatPaneManager(this, tabs);
    5146
    5247        input = new JPanelTextField() {
    5348            @Override
    5449            protected void processEnter( String text ) {
    55                 connection.postMessage(text, getRecipient());
     50                connection.postMessage(text, chatPanes.getRecipient());
    5651            }
    5752
    5853            @Override
    5954            protected String autoComplete( String word ) {
    60                 return word;
     55                return autoCompleteUser(word);
    6156            }
    6257        };
    6358
     59        loginPanel = createLoginPanel();
     60
     61        gcPanel = new JPanel(new BorderLayout());
     62        gcPanel.add(loginPanel, BorderLayout.CENTER);
     63        createLayout(gcPanel, false, null);
     64
     65        users = new TreeMap<String, LatLon>();
     66        // Start threads
     67        connection = ChatServerConnection.getInstance();
     68        connection.addListener(this);
     69        connection.checkLogin();
     70    }
     71
     72    private JPanel createLoginPanel() {
    6473        final JTextField nameField = new JPanelTextField() {
    6574            @Override
     
    7382        if( userName.contains("@") )
    7483            userName = userName.substring(0, userName.indexOf('@'));
     84        userName = userName.replace(' ', '_');
    7585        nameField.setText(userName);
    7686
     
    8393        nameField.setPreferredSize(new Dimension(nameField.getPreferredSize().width, loginButton.getPreferredSize().height));
    8494
    85         loginPanel = new JPanel(new GridBagLayout());
    86         loginPanel.add(nameField, GBC.std().fill(GridBagConstraints.HORIZONTAL).insets(15, 0, 5, 0));
    87         loginPanel.add(loginButton, GBC.std().fill(GridBagConstraints.NONE).insets(0, 0, 15, 0));
    88 
    89         gcPanel = new JPanel(new BorderLayout());
    90         gcPanel.add(loginPanel, BorderLayout.CENTER);
    91         createLayout(gcPanel, false, null);
    92 
    93         users = new TreeMap<String, LatLon>();
    94         // Start threads
    95         connection = ChatServerConnection.getInstance();
    96         connection.addListener(this);
    97         connection.checkLogin();
    98     }
    99 
    100     private void addLineToChatPane( String userName, String line ) {
    101         if( !chatPanes.containsKey(userName) )
    102             createChatPane(userName);
    103         if( !line.startsWith("\n") )
    104             line = "\n" + line;
    105         Document doc = chatPanes.get(userName).pane.getDocument();
    106         try {
    107             doc.insertString(doc.getLength(), line, null);
    108         } catch( BadLocationException ex ) {
    109             // whatever
    110         }
    111     }
    112 
    113     private void addLineToPublic( String line ) {
    114         addLineToChatPane(PUBLIC_PANE, line);
    115     }
    116 
    117     private void clearPublicChatPane() {
    118         chatPanes.get(PUBLIC_PANE).pane.setText("");
    119         showNearbyUsers();
    120     }
    121 
    122     private void clearChatPane( String userName) {
    123         if( userName == null || userName.equals(PUBLIC_PANE) )
    124             clearPublicChatPane();
    125         else
    126             chatPanes.get(userName).pane.setText("");
    127     }
    128 
    129     private ChatLogEntry createChatPane( String userName ) {
    130         JTextPane chatPane = new JTextPane();
    131         chatPane.setEditable(false);
    132         Font font = chatPane.getFont();
    133         chatPane.setFont(font.deriveFont(font.getSize2D() - 2));
    134         DefaultCaret caret = (DefaultCaret)chatPane.getCaret();
    135         caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
    136         JScrollPane scrollPane = new JScrollPane(chatPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
    137         chatPane.addMouseListener(new PopupAdapter());
    138 
    139         ChatLogEntry entry = new ChatLogEntry();
    140         entry.pane = chatPane;
    141         entry.component = scrollPane;
    142         entry.notify = false;
    143         entry.userName = userName;
    144         entry.isPublic = userName == null;
    145         chatPanes.put(userName == null ? PUBLIC_PANE : userName, entry);
    146 
    147         tabs.addTab(userName == null ? tr("Public") : userName, scrollPane);
    148         tabs.setSelectedComponent(scrollPane);
    149         return entry;
     95        JPanel panel = new JPanel(new GridBagLayout());
     96        panel.add(nameField, GBC.std().fill(GridBagConstraints.HORIZONTAL).insets(15, 0, 5, 0));
     97        panel.add(loginButton, GBC.std().fill(GridBagConstraints.NONE).insets(0, 0, 15, 0));
     98        return panel;
     99    }
     100
     101    protected void logout() {
     102        connection.logout();
     103    }
     104
     105    private String autoCompleteUser( String word ) {
     106        return word; // todo: write autocomplete
    150107    }
    151108
    152109    /**
    153      * Returns key in chatPanes hash map for the currently active
    154      * chat pane, or null in case of an error.
     110     * This is implementation of a "temporary layer". It paints circles
     111     * for all users nearby.
    155112     */
    156     private String getActiveChatPane() {
    157         Component c = tabs.getSelectedComponent();
    158         if( c == null )
    159             return null;
    160         for( String user : chatPanes.keySet() )
    161             if( c.equals(chatPanes.get(user).component) )
    162                 return user;
    163         return null;
    164     }
    165 
    166     private String getRecipient() {
    167         String user = getActiveChatPane();
    168         return user == null || user.equals(PUBLIC_PANE) ? null : user;
    169     }
    170 
    171     private void closeChatPane( String user ) {
    172         if( user == null || user.equals(PUBLIC_PANE) || !chatPanes.containsKey(user) )
    173             return;
    174         tabs.remove(chatPanes.get(user).component);
    175         chatPanes.remove(user);
    176     }
    177 
    178     private void closePrivateChatPanes() {
    179         List<String> entries = new ArrayList<String>(chatPanes.keySet());
    180         for( String user : entries )
    181             if( !user.equals(PUBLIC_PANE) )
    182                 closeChatPane(user);
    183     }
    184 
    185     private String cachedTitle = "";
    186     private int cachedAlarm = 0;
    187 
    188     @Override
    189     public void setTitle( String title ) {
    190         setTitle(title, -1);
    191     }
    192 
    193     private void setTitleAlarm( int alarmLevel ) {
    194         setTitle(null, alarmLevel);
    195     }
    196 
    197     private void setTitle( String title, int alarmLevel ) {
    198         if( title != null )
    199             cachedTitle = title;
    200         if( alarmLevel >= 0 )
    201             cachedAlarm = alarmLevel;
    202         String alarm = cachedAlarm <= 0 ? "" : cachedAlarm == 1 ? "* " : "[!] ";
    203         super.setTitle(alarm + cachedTitle);
    204     }
    205 
    206113    public void paint( Graphics2D g, MapView mv, Bounds bbox ) {
    207114        Graphics2D g2d = (Graphics2D)g.create();
     
    227134            g2d.drawString(user, p.x - Math.round(rect.getWidth() / 2), p.y);
    228135        }
     136    }
     137
     138    /* ================== Notifications in the title ======================= */
     139
     140    private String cachedTitle = null;
     141    private int cachedAlarm = 0;
     142
     143    @Override
     144    public void setTitle( String title ) {
     145        setTitle(title, -1);
     146    }
     147
     148    private void setTitle( String title, int alarmLevel ) {
     149        boolean changed = false;
     150        if( title != null && (cachedTitle == null || !cachedTitle.equals(title)) ) {
     151            cachedTitle = title;
     152            changed = true;
     153        }
     154        if( alarmLevel >= 0 && cachedAlarm != alarmLevel ) {
     155            cachedAlarm = alarmLevel;
     156            changed = true;
     157        }
     158        if( changed ) {
     159            String alarm = cachedAlarm <= 0 ? "" : cachedAlarm == 1 ? "* " : "!!! ";
     160            super.setTitle(alarm + cachedTitle);
     161            // todo: title becomes cut off
     162        }
     163    }
     164
     165    protected void updateTitleAlarm() {
     166        int notifyLevel = chatPanes.getNotifyLevel();
     167        setTitle(null, isDialogInCollapsedView() ? notifyLevel : Math.min(1, notifyLevel));
     168    }
     169
     170    @Override
     171    protected void setIsCollapsed( boolean val ) {
     172        super.setIsCollapsed(val);
     173        chatPanes.setCollapsed(val);
     174        updateTitleAlarm();
    229175    }
    230176
     
    265211    public void statusChanged( boolean active ) {
    266212        // only the public tab, because private chats don't rely on coordinates
    267         tabs.setComponentAt(0, active ? chatPanes.get(PUBLIC_PANE).component : noData);
     213        tabs.setComponentAt(0, active ? chatPanes.getPublicChatComponent() : noData);
    268214        repaint();
    269215    }
     
    272218        for( String uname : this.users.keySet() ) {
    273219            if( !newUsers.containsKey(uname) )
    274                 addLineToPublic(tr("User {0} has left", uname));
     220                chatPanes.addLineToPublic(tr("User {0} has left", uname));
    275221        }
    276222        for( String uname : newUsers.keySet() ) {
    277223            if( !this.users.containsKey(uname) )
    278                 addLineToPublic(tr("User {0} is mapping nearby", uname));
     224                chatPanes.addLineToPublic(tr("User {0} is mapping nearby", uname));
    279225        }
    280226        setTitle(trn("GeoChat ({0} user)", "GeoChat ({0} users)", newUsers.size(), newUsers.size()));
    281         // todo: update users location
    282227        this.users = newUsers;
    283     }
    284 
    285     private void showNearbyUsers() {
    286         if( !users.isEmpty() ) {
    287             StringBuilder sb = new StringBuilder(tr("Users mapping nearby:"));
    288             boolean first = true;
    289             for( String user : users.keySet() ) {
    290                 sb.append(first ? " " : ", ");
    291                 sb.append(user);
    292             }
    293             addLineToPublic(sb.toString());
    294         }
     228        if( userLayerActive && Main.map.mapView != null )
     229            Main.map.mapView.repaint();
    295230    }
    296231
     
    305240    public void receivedMessages( boolean replace, List<ChatMessage> messages ) {
    306241        if( replace )
    307             clearPublicChatPane();
     242            chatPanes.clearPublicChatPane();
    308243        if( !messages.isEmpty() ) {
     244            int alarm = 0;
    309245            StringBuilder sb = new StringBuilder();
    310             for( ChatMessage msg : messages )
     246            for( ChatMessage msg : messages ) {
    311247                formatMessage(sb, msg);
    312             addLineToPublic(sb.toString());
     248                if( msg.isIncoming() ) {
     249                    // todo: alarm=2 for private messages
     250                    alarm = 1;
     251                }
     252            }
     253            chatPanes.addLineToPublic(sb.toString());
     254            if( alarm > 0 )
     255                chatPanes.notify(null, alarm > 1);
    313256        }
    314257    }
     
    316259    public void receivedPrivateMessages( boolean replace, List<ChatMessage> messages ) {
    317260        if( replace )
    318             closePrivateChatPanes();
     261            chatPanes.closePrivateChatPanes();
    319262        for( ChatMessage msg : messages ) {
    320263            StringBuilder sb = new StringBuilder();
    321264            formatMessage(sb, msg);
    322             addLineToChatPane(msg.getRecipient() != null ? msg.getRecipient() : msg.getAuthor(), sb.toString());
    323         }
    324     }
    325 
    326     /* =================== Service classes ==================== */
    327 
    328     private class JPanelTextField extends JTextField {
    329         @Override
    330         protected void processKeyEvent( KeyEvent e ) {
    331             if( e.getID() == KeyEvent.KEY_PRESSED ) {
    332                 int code = e.getKeyCode();
    333                 if( code == KeyEvent.VK_ENTER ) {
    334                     String text = input.getText();
    335                     if( text.length() > 0 ) {
    336                         processEnter(text);
    337                         input.setText("");
    338                     }
    339                 } else if( code == KeyEvent.VK_TAB ) {
    340                     autoComplete(""); // todo
    341                 } else if( code == KeyEvent.VK_ESCAPE ) {
    342                     if( Main.map != null && Main.map.mapView != null )
    343                         Main.map.mapView.requestFocus();
    344                 }
    345                 // Do not pass other events to JOSM
    346                 if( code != KeyEvent.VK_LEFT && code != KeyEvent.VK_HOME && code != KeyEvent.VK_RIGHT
    347                         && code != KeyEvent.VK_END && code != KeyEvent.VK_BACK_SPACE && code != KeyEvent.VK_DELETE )
    348                     e.consume();
    349             }
    350             super.processKeyEvent(e);
    351         }
    352 
    353         protected void processEnter( String text ) { }
    354 
    355         protected String autoComplete( String word ) { return word; }
    356     }
    357 
    358     private class ChatLogEntry {
    359         public String userName;
    360         public boolean isPublic;
    361         public JTextPane pane;
    362         public JScrollPane component;
    363         public boolean notify;
    364     }
    365 
    366     /* ================= Actions for popup menu ==================== */
    367 
    368 
    369     private JPopupMenu createPopupMenu() {
    370         JMenu userMenu = new JMenu(tr("Private chat"));
    371         for( String user : users.keySet() ) {
    372             if( !chatPanes.containsKey(user) )
    373                 userMenu.add(new PrivateChatAction(user));
    374         }
    375 
    376         JPopupMenu menu = new JPopupMenu();
    377         menu.add(new JCheckBoxMenuItem(new ToggleUserLayerAction()));
    378         if( userMenu.getItemCount() > 0 )
    379             menu.add(userMenu);
    380         if( getRecipient() != null )
    381             menu.add(new CloseTabAction());
    382 //        menu.add(new ClearPaneAction());
    383 //        menu.add(new LogoutAction());
    384         return menu;
    385     }
    386 
    387     private class PopupAdapter extends MouseAdapter {
    388         @Override public void mousePressed( MouseEvent e ) { check(e); }
    389         @Override public void mouseReleased( MouseEvent e ) { check(e); }
    390 
    391         private void check( MouseEvent e ) {
    392             if( e.isPopupTrigger() ) {
    393                 createPopupMenu().show(tabs, e.getX(), e.getY());
    394             }
    395         }
    396     }
    397 
    398     private class PrivateChatAction extends AbstractAction {
    399         private String userName;
    400 
    401         public PrivateChatAction( String userName ) {
    402             super(userName);
    403             this.userName = userName;
    404         }
    405 
    406         public void actionPerformed( ActionEvent e ) {
    407             if( !chatPanes.containsKey(userName) ) {
    408                 ChatLogEntry entry = createChatPane(userName);
    409             }
    410         }
    411     }
    412 
    413     private class CloseTabAction extends AbstractAction {
    414         public CloseTabAction() {
    415             super(tr("Close tab"));
    416 //            putValue(SMALL_ICON, ImageProvider.get("help"));
    417         }
    418 
    419         public void actionPerformed( ActionEvent e ) {
    420             String pane = getActiveChatPane();
    421             if( pane != null && !pane.equals(PUBLIC_PANE) )
    422                 closeChatPane(pane);
    423         }
    424     }
    425 
    426     private class LogoutAction extends AbstractAction {
    427         public LogoutAction() {
    428             super(tr("Logout"));
    429 //            putValue(SMALL_ICON, ImageProvider.get("help"));
    430         }
    431 
    432         public void actionPerformed( ActionEvent e ) {
    433             connection.logout();
    434         }
    435     }
    436 
    437     private class ClearPaneAction extends AbstractAction {
    438         public ClearPaneAction() {
    439             super(tr("Clear log"));
    440 //            putValue(SMALL_ICON, ImageProvider.get("help"));
    441         }
    442 
    443         public void actionPerformed( ActionEvent e ) {
    444             clearChatPane(getActiveChatPane());
    445         }
    446     }
    447 
    448     private class ToggleUserLayerAction extends AbstractAction {
    449         public ToggleUserLayerAction() {
    450             super(tr("Show users on map"));
    451 //            putValue(SMALL_ICON, ImageProvider.get("help"));
    452         }
    453 
    454         public void actionPerformed( ActionEvent e ) {
    455             if( Main.map == null || Main.map.mapView == null )
    456                 return;
    457             boolean wasAdded = Main.map.mapView.addTemporaryLayer(GeoChatPanel.this);
    458             if( !wasAdded )
    459                 Main.map.mapView.removeTemporaryLayer(GeoChatPanel.this);
    460             Main.map.mapView.repaint();
    461             if( e.getSource() != null )
    462                 System.out.println("toggle source: " + e.getSource().getClass().getName());
    463             if( e.getSource() instanceof JCheckBoxMenuItem )
    464                 ((JCheckBoxMenuItem)e.getSource()).setSelected(wasAdded);
     265            chatPanes.addLineToChatPane(msg.isIncoming() ? msg.getAuthor() : msg.getRecipient(), sb.toString());
     266            if( msg.isIncoming() )
     267                chatPanes.notify(msg.getAuthor(), true);
    465268        }
    466269    }
Note: See TracChangeset for help on using the changeset viewer.