View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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      //--- Constant(s) ---
46  
47      //--- Data field(s) ---
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      //--- Constructor(s) ---
77  
78      public ChatPane()
79      {
80  		jtp = new JTextPane();
81  		//  jtp.setContentType("text/rtf");
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      //--- Method(s) ---
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 				//  RTFEditorKit rtf = new RTFEditorKit();
155 //  				try {
156 //  					rtf.read(new StringReader(text), doc, doc.getLength());
157 //  				}
158 //  				catch (IOException ie){
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': // 10
194 						StyleConstants.setForeground
195 							(s, new Color(0, 204, 204));
196 						break;
197 					case '1': // 11
198 						StyleConstants.setForeground
199 							(s, new Color(51, 238, 255));
200 						break;
201 					case '2': // 12
202 						StyleConstants.setForeground
203 							(s, new Color(0, 0, 255));
204 						break;
205 					case '3': // 13
206 						StyleConstants.setForeground
207 							(s, new Color(238, 34, 238));
208 						break;
209 					case '4': // 14
210 						StyleConstants.setForeground
211 							(s, new Color(119, 119, 119));
212 						break;
213 					case '5': // 15 
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 					// blue
225 					StyleConstants.setForeground(s, new Color(0, 0, 204));
226 					break;
227 				case 3:
228 				case '3':
229 					// dark green
230 					StyleConstants.setForeground(s, new Color(0, 204, 0));
231 					break;
232 				case 4:
233 				case '4':
234 					// red
235 					StyleConstants.setForeground(s, new Color(221, 0, 0));
236 					break;
237 				case 5:
238 				case '5':
239 					// brown
240 					StyleConstants.setForeground(s, new Color(170, 0, 0));
241 					break;
242 				case 6:
243 				case '6':
244 					// purple
245 					StyleConstants.setForeground(s, new Color(187, 0, 187));
246 					break;
247 				case 7:
248 				case '7':
249 					// orange
250 					StyleConstants.setForeground(s, new Color(255, 170, 0));
251 					break;
252 				case 8:
253 				case '8':
254 					// yellow
255 					StyleConstants.setForeground(s, new Color(238, 221, 34));
256 					break;
257 				case 9:
258 				case '9':
259 					// green
260 					StyleConstants.setForeground(s, new Color(51, 222, 85));
261 					break;
262 				case 10:
263 					// teal
264 					StyleConstants.setForeground(s, new Color(0, 204, 204));
265 					break;
266 				case 11:
267 					// cyan
268 					StyleConstants.setForeground(s, new Color(51, 238, 255));
269 					break;
270 				case 12:
271 					// blue
272 					StyleConstants.setForeground(s, new Color(0, 0, 255));
273 					break;
274 				case 13:
275 					// magenta
276 					StyleConstants.setForeground(s, new Color(238, 34, 238));
277 					break;
278 				case 14:
279 					// dark gray
280 					StyleConstants.setForeground(s, new Color(119, 119, 119));
281 					break;
282 				case 15:
283 					// light gray
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 					// reversed: swap colors ?
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 					//  RTFEditorKit rtf = new RTFEditorKit();
311 //  					try {
312 //  						rtf.read(new StringReader(text), doc, doc.getLength());
313 //  					}
314 //  					catch (IOException ie) {
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 }