1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.gui;
21
22 import java.awt.Color;
23 import java.awt.Font;
24 import java.beans.PropertyChangeEvent;
25 import java.beans.PropertyChangeListener;
26
27 import javax.swing.JScrollPane;
28 import javax.swing.JTextPane;
29 import javax.swing.SwingUtilities;
30 import javax.swing.border.EmptyBorder;
31 import javax.swing.text.BadLocationException;
32 import javax.swing.text.Document;
33 import javax.swing.text.Style;
34 import javax.swing.text.StyleConstants;
35 import javax.swing.text.StyleContext;
36
37 import org.xnap.gui.util.GUIHelper;
38 import org.xnap.util.Preferences;
39
40 /***
41 * Provides a {@link JTextPane} that displays chat messages.
42 */
43 public class ChatPane extends JScrollPane implements PropertyChangeListener {
44
45
46
47
48
49 private static Preferences prefs = Preferences.getInstance();
50
51 /***
52 * A common style context for all panels.
53 */
54 private static StyleContext styles = new StyleContext();
55 static {
56 updateStyles();
57
58 PropertyChangeListener l = new PropertyChangeListener()
59 {
60 public void propertyChange(PropertyChangeEvent e)
61 {
62 updateStyles();
63 }
64 };
65
66
67 prefs.addPropertyChangeListener("chatFont", l);
68 prefs.addPropertyChangeListener("chatMessageColor", l);
69 prefs.addPropertyChangeListener("chatUserColor", l);
70 prefs.addPropertyChangeListener("chatInfoColor", l);
71 prefs.addPropertyChangeListener("chatErrorColor", l);
72 }
73
74 private JTextPane jtp;
75
76
77
78 public ChatPane()
79 {
80 jtp = new JTextPane();
81
82 jtp.setBackground(prefs.getColor("chatBackgroundColor"));
83 jtp.setEditable(false);
84 jtp.setBorder(new EmptyBorder(5, 5, 5, 5));
85
86 setVerticalScrollBarPolicy
87 (JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
88 setHorizontalScrollBarPolicy
89 (JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
90
91 setViewportView(jtp);
92
93 prefs.addPropertyChangeListener("chatBackgroundColor", this);
94 }
95
96
97
98 public static Style getStyle(String key)
99 {
100 return styles.getStyle(key);
101 }
102
103 public static void setStyle(String key, Color color, Font font)
104 {
105 Style def = styles.getStyle(StyleContext.DEFAULT_STYLE);
106 Style s = styles.addStyle(key, def);
107 StyleConstants.setForeground(s, color);
108 StyleConstants.setFontFamily(s, font.getName());
109 StyleConstants.setFontSize(s, font.getSize());
110 StyleConstants.setItalic(s, font.isItalic());
111 StyleConstants.setBold(s, font.isBold());
112 }
113
114 public static void updateStyles()
115 {
116 Font font = prefs.getFont("chatFont");
117
118 setStyle("chatMessage", prefs.getColor("chatMessageColor"), font);
119 setStyle("chatUser", prefs.getColor("chatUserColor"), font);
120 setStyle("chatInfo", prefs.getColor("chatInfoColor"), font);
121 setStyle("chatError", prefs.getColor("chatErrorColor"), font);
122 }
123
124 public void appendLater(final String text, final String style)
125 {
126 Runnable r = new Runnable()
127 {
128 public void run()
129 {
130 append(text, style);
131 }
132 };
133
134 SwingUtilities.invokeLater(r);
135 }
136
137 public void appendLater(final String text)
138 {
139 appendLater(text, StyleContext.DEFAULT_STYLE);
140 }
141
142 public void append(String text, String style)
143 {
144 boolean autoScroll = GUIHelper.shouldScroll(getVerticalScrollBar());
145
146 try {
147 if (style.equals("chatMessage")) {
148 appendColored(text, style);
149 }
150 else {
151 Document doc = jtp.getDocument();
152
153 doc.insertString(doc.getLength(), text, getStyle(style));
154
155
156
157
158
159
160 }
161 }
162 catch (BadLocationException e) {
163 }
164
165 if (autoScroll) {
166 GUIHelper.scrollToEnd(jtp);
167 }
168 }
169
170 public void appendColored(String text, String style)
171 throws BadLocationException
172 {
173 Style s = styles.addStyle(null, getStyle(style));
174
175 Document doc = jtp.getDocument();
176 boolean escape = false;
177 for (int i = 0; i < text.length(); i++) {
178 if (escape) {
179 escape = false;
180
181 switch (text.charAt(i)) {
182 case 0:
183 case '0':
184 StyleConstants.setForeground(s, Color.white);
185 break;
186 case 1:
187 StyleConstants.setForeground(s, Color.black);
188 break;
189 case '1':
190 char peek = (i < text.length()) ? text.charAt(i + 1) : 255;
191 i++;
192 switch (peek) {
193 case '0':
194 StyleConstants.setForeground
195 (s, new Color(0, 204, 204));
196 break;
197 case '1':
198 StyleConstants.setForeground
199 (s, new Color(51, 238, 255));
200 break;
201 case '2':
202 StyleConstants.setForeground
203 (s, new Color(0, 0, 255));
204 break;
205 case '3':
206 StyleConstants.setForeground
207 (s, new Color(238, 34, 238));
208 break;
209 case '4':
210 StyleConstants.setForeground
211 (s, new Color(119, 119, 119));
212 break;
213 case '5':
214 StyleConstants.setForeground
215 (s, new Color(153, 153, 153));
216 break;
217 default:
218 StyleConstants.setForeground(s, Color.black);
219 i--;
220 }
221 break;
222 case 2:
223 case '2':
224
225 StyleConstants.setForeground(s, new Color(0, 0, 204));
226 break;
227 case 3:
228 case '3':
229
230 StyleConstants.setForeground(s, new Color(0, 204, 0));
231 break;
232 case 4:
233 case '4':
234
235 StyleConstants.setForeground(s, new Color(221, 0, 0));
236 break;
237 case 5:
238 case '5':
239
240 StyleConstants.setForeground(s, new Color(170, 0, 0));
241 break;
242 case 6:
243 case '6':
244
245 StyleConstants.setForeground(s, new Color(187, 0, 187));
246 break;
247 case 7:
248 case '7':
249
250 StyleConstants.setForeground(s, new Color(255, 170, 0));
251 break;
252 case 8:
253 case '8':
254
255 StyleConstants.setForeground(s, new Color(238, 221, 34));
256 break;
257 case 9:
258 case '9':
259
260 StyleConstants.setForeground(s, new Color(51, 222, 85));
261 break;
262 case 10:
263
264 StyleConstants.setForeground(s, new Color(0, 204, 204));
265 break;
266 case 11:
267
268 StyleConstants.setForeground(s, new Color(51, 238, 255));
269 break;
270 case 12:
271
272 StyleConstants.setForeground(s, new Color(0, 0, 255));
273 break;
274 case 13:
275
276 StyleConstants.setForeground(s, new Color(238, 34, 238));
277 break;
278 case 14:
279
280 StyleConstants.setForeground(s, new Color(119, 119, 119));
281 break;
282 case 15:
283
284 StyleConstants.setForeground(s, new Color(153, 153, 153));
285 break;
286 }
287 }
288 else {
289 switch (text.charAt(i)) {
290 case 2:
291 StyleConstants.setBold(s, true);
292 break;
293 case 3:
294 escape = true;
295 break;
296 case 7:
297 StyleConstants.setBold(s, false);
298 StyleConstants.setBold(s, false);
299 case 15:
300 StyleConstants.setBold(s, false);
301 break;
302 case 22:
303
304 break;
305 case 31:
306 StyleConstants.setUnderline(s, true);
307 default:
308 ensureIsReadable(s);
309 doc.insertString(doc.getLength(), text.charAt(i) + "", s);
310
311
312
313
314
315
316 }
317 }
318 }
319
320 }
321
322 private void ensureIsReadable(Style s)
323 {
324 Color c = jtp.getBackground();
325 double bLightness = getLightness(c);
326
327 c = StyleConstants.getForeground(s);
328 double fLightness = getLightness(c);
329
330 while (Math.abs(fLightness - bLightness) <= 0.2) {
331 if ((fLightness - bLightness) < 0) {
332 c = c.darker();
333 }
334 else if (fLightness == bLightness) {
335 if (fLightness > 0.5) {
336 c = c.darker();
337 }
338 else {
339 c = c.brighter();
340 }
341 }
342 else {
343 c = c.brighter();
344 }
345 fLightness = getLightness(c);
346 }
347 StyleConstants.setForeground(s, c);
348 }
349
350 private double getLightness(Color c)
351 {
352 return Math.pow((0.3 * c.getRed() + 0.59 * c.getGreen()
353 + 0.11 * c.getBlue()) / 255.0,
354 1.0 / 3.0);
355 }
356
357 public void propertyChange(PropertyChangeEvent e)
358 {
359 jtp.setBackground(prefs.getColor("chatBackgroundColor"));
360 }
361
362 public void setText(String newValue)
363 {
364 jtp.setText(newValue);
365 }
366
367 }