Ignore:
Timestamp:
2017-09-14T23:12:25+02:00 (7 years ago)
Author:
donvip
Message:

fix #josm15307 - update to JOSM 12743

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

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/geochat/build.xml

    r33545 r33602  
    55    <property name="commit.message" value="[josm_geochat] copypaste from keyboard, font size advanced parameters"/>
    66    <!-- enter the *lowest* JOSM version this plugin is currently compatible with -->
    7     <property name="plugin.main.version" value="12643"/>
     7    <property name="plugin.main.version" value="12743"/>
    88
    99    <property name="plugin.author" value="Ilya Zverev"/>
  • applications/editors/josm/plugins/geochat/src/geochat/ChatPaneManager.java

    r33545 r33602  
    3232 */
    3333class 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             @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     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        }
    268268}
  • applications/editors/josm/plugins/geochat/src/geochat/GeoChatPanel.java

    r33545 r33602  
    3636import org.openstreetmap.josm.Main;
    3737import org.openstreetmap.josm.data.Bounds;
     38import org.openstreetmap.josm.data.UserIdentityManager;
    3839import org.openstreetmap.josm.data.coor.LatLon;
    39 import org.openstreetmap.josm.gui.*;
     40import org.openstreetmap.josm.gui.MainApplication;
     41import org.openstreetmap.josm.gui.MapView;
     42import org.openstreetmap.josm.gui.Notification;
    4043import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
    4144import org.openstreetmap.josm.gui.layer.MapViewPaintable;
     
    5053 */
    5154public 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 actions
    59     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             @Override
    74             protected void processEnter(String text) {
    75                 connection.postMessage(text, chatPanes.getRecipient());
    76             }
    77 
    78             @Override
    79             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 threads
    93         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 null
    102         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             @Override
    115             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             @Override
    124             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             @Override
    133             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     @Override
    150     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 circles
    185     * for all users nearby.
    186     */
    187     @Override
    188     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             @Override
    236             public void run() {
    237                 setTitle(alarm);
    238             }
    239         });
    240     }
    241 
    242     /**
    243     * Track panel collapse events.
    244     */
    245     @Override
    246     protected void setIsCollapsed(boolean val) {
    247         super.setIsCollapsed(val);
    248         chatPanes.setCollapsed(val);
    249         updateTitleAlarm();
    250     }
    251 
    252     /* ============ ChatServerConnectionListener methods ============= */
    253 
    254     @Override
    255     public void loggedIn(String userName) {
    256         Main.pref.put("geochat.username", userName);
    257         if (gcPanel.getComponentCount() == 1) {
    258             GuiHelper.runInEDTAndWait(new Runnable() {
    259                 @Override
    260                 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     @Override
    271     public void notLoggedIn(final String reason) {
    272         if (reason != null) {
    273             GuiHelper.runInEDT(new Runnable() {
    274                 @Override
    275                 public void run() {
    276                     new Notification(tr("Failed to log in to GeoChat:") + "\n" + reason).show();
    277                 }
    278             });
    279         } else {
    280             // regular logout
    281             if (gcPanel.getComponentCount() > 1) {
    282                 gcPanel.removeAll();
    283                 gcPanel.add(loginPanel, BorderLayout.CENTER);
    284             }
    285         }
    286         updateTitleAlarm();
    287     }
    288 
    289     @Override
    290     public void messageSendFailed(final String reason) {
    291         GuiHelper.runInEDT(new Runnable() {
    292             @Override
    293             public void run() {
    294                 new Notification(tr("Failed to send message:") + "\n" + reason).show();
    295             }
    296         });
    297     }
    298 
    299     @Override
    300     public void statusChanged(boolean active) {
    301         // only the public tab, because private chats don't rely on coordinates
    302         tabs.setComponentAt(0, active ? chatPanes.getPublicChatComponent() : noData);
    303         repaint();
    304     }
    305 
    306     @Override
    307     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     @Override
    331     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 buffer
    344                     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                 } else
    350                     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     @Override
    386     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        }
    397400}
Note: See TracChangeset for help on using the changeset viewer.