001/* ---------------------------------------------------------------------------- 002 The Kiwi Toolkit - A Java Class Library 003 Copyright (C) 1998-2004 Mark A. Lindner 004 005 This library is free software; you can redistribute it and/or 006 modify it under the terms of the GNU General Public License as 007 published by the Free Software Foundation; either version 2 of the 008 License, or (at your option) any later version. 009 010 This library is distributed in the hope that it will be useful, 011 but WITHOUT ANY WARRANTY; without even the implied warranty of 012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 General Public License for more details. 014 015 You should have received a copy of the GNU General Public License 016 along with this library; if not, write to the Free Software 017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 018 02111-1307, USA. 019 020 The author may be contacted at: mark_a_lindner@yahoo.com 021 ---------------------------------------------------------------------------- 022 $Log: SimpleStyledEditor.java,v $ 023 Revision 1.5 2004/05/12 19:02:21 markl 024 comment block updates, updates to use renamed XML* classes 025 026 Revision 1.4 2003/01/19 09:50:53 markl 027 Javadoc & comment header updates. 028 029 Revision 1.3 2001/03/12 09:28:00 markl 030 Source code and Javadoc cleanup. 031 032 Revision 1.2 1999/01/10 03:00:07 markl 033 added GPL header & RCS tag 034 ---------------------------------------------------------------------------- 035*/ 036 037package kiwi.ui; 038 039import java.awt.*; 040import java.awt.event.*; 041import java.io.*; 042import java.util.Enumeration; 043import javax.swing.*; 044import javax.swing.text.*; 045 046import kiwi.text.*; 047 048/** A simple text editor that supports styles like bold, italic, and underline, 049 * and that is able to render embedded markup using proxies. 050 * 051 * <p><center> 052 * <img src="snapshot/SimpleStyledEditor.gif"><br> 053 * <i>An example SimpleStyledEditor.</i> 054 * </center> 055 * 056 * @see kiwi.ui.SimpleEditor 057 * 058 * @author Mark Lindner 059 */ 060 061public class SimpleStyledEditor extends JTextPane 062 { 063 private SimpleAttributeSet a_italic, a_plain, a_bold, a_underline; 064 private DefaultStyledDocument doc; 065 private StringBuffer buffer; 066 private boolean italicf, boldf, underlinef, plainf; 067 private MarkupProxyFactory proxyFactory; 068 069 /** Construct a new <code>SimpleStyledEditor</code>. 070 * 071 * @param proxyFactory The <code>MarkupProxyFactory</code> that will provide 072 * proxies for rendering markup content. 073 */ 074 075 public SimpleStyledEditor(MarkupProxyFactory proxyFactory) 076 { 077 super(); 078 079 StyleContext sc = new StyleContext(); 080 Style def = sc.getStyle(StyleContext.DEFAULT_STYLE); 081 StyleConstants.setFontFamily(def, "Serif"); 082 StyleConstants.setFontSize(def, 14); 083 doc = new DefaultStyledDocument(sc); 084 setDocument(doc); 085 086 a_italic = new SimpleAttributeSet(); 087 StyleConstants.setItalic(a_italic, true); 088 089 a_bold = new SimpleAttributeSet(); 090 StyleConstants.setBold(a_bold, true); 091 092 a_underline = new SimpleAttributeSet(); 093 StyleConstants.setUnderline(a_underline, true); 094 095 a_plain = new SimpleAttributeSet(); 096 097 this.proxyFactory = proxyFactory; 098 } 099 100 /** Set the text to be displayed in the editor. 101 * 102 * @param text The text to display. 103 * @see #getText 104 */ 105 106 public synchronized void setText(String text) 107 { 108 new TextReader(text); 109 } 110 111 /** Get the text currently displayed in the editor. 112 * 113 * @return The text currently displayed in the editor. 114 * @see #setText 115 **/ 116 117 public synchronized String getText() 118 { 119 buffer = new StringBuffer(); 120 italicf = boldf = underlinef = false; 121 plainf = true; 122 123 Element e[] = doc.getRootElements(); 124 for(int i = 0; i < e.length; i++) 125 decode(e[i]); 126 127 if(boldf) buffer.append("</b>"); 128 if(italicf) buffer.append("</i>"); 129 if(underlinef) buffer.append("</u>"); 130 131 return(buffer.toString()); 132 } 133 134 // --- decoder 135 136 private void decode(Element el) 137 { 138 // decode the element 139 140 AttributeSet attrs = el.getAttributes(); 141 Enumeration e = attrs.getAttributeNames(); 142 143 if(el.isLeaf() && el.getName().equals("component")) 144 { 145 while(e.hasMoreElements()) 146 { 147 Object name = e.nextElement(); 148 Object value = attrs.getAttribute(name); 149 150 if(name == StyleConstants.ComponentAttribute) 151 buffer.append(((MarkupProxy)(StyleConstants.getComponent(attrs))) 152 .getMarkup()); 153 } 154 } 155 156 else if(el.isLeaf() && el.getName().equals("content")) 157 { 158 boolean cboldf = false, citalicf = false, cunderlinef = false; 159 160 while(e.hasMoreElements()) 161 { 162 Object name = e.nextElement(); 163 Object value = attrs.getAttribute(name); 164 165 if((name == StyleConstants.Bold) && ((Boolean)value).booleanValue()) 166 cboldf = true; 167 else if((name == StyleConstants.Italic) 168 && ((Boolean)value).booleanValue()) 169 citalicf = true; 170 else if((name == StyleConstants.Underline) 171 && ((Boolean)value).booleanValue()) 172 cunderlinef = true; 173 } 174 175 if(boldf && !cboldf) 176 buffer.append("</b>"); 177 else if(!boldf && cboldf) 178 buffer.append("<b>"); 179 else if(italicf && !citalicf) 180 buffer.append("</i>"); 181 else if(!italicf && citalicf) 182 buffer.append("<i>"); 183 else if(underlinef && !cunderlinef) 184 buffer.append("</u>"); 185 else if(!underlinef && cunderlinef) 186 buffer.append("<u>"); 187 188 boldf = cboldf; 189 italicf = citalicf; 190 underlinef = cunderlinef; 191 192 int start = el.getStartOffset(); 193 int end = el.getEndOffset(); 194 try 195 { 196 buffer.append(escapeText(doc.getText(start, (end - start)))); 197 } 198 catch(Exception ex){} 199 } 200 else 201 { 202 if(el.getName().equals("paragraph")) 203 { 204 while(e.hasMoreElements()) 205 { 206 Object name = e.nextElement(); 207 Object value = attrs.getAttribute(name); 208 209 /* ignore these for the time being */ 210 } 211 } 212 213 // decode the element's children 214 215 int ct = el.getElementCount(); 216 for(int i = 0; i < ct; i++) 217 { 218 Element ee = el.getElement(i); 219 decode(ee); 220 } 221 } 222 } 223 224 // --- public style/content modification methods 225 226 /** Insert text at the current insertion point. 227 * 228 * @param text The text to insert. 229 */ 230 231 public synchronized void insertText(String text) 232 { 233 String t = escapeText(text); 234 replaceSelection(t); 235 } 236 237 /** Insert a markup proxy at the current insertion point. 238 * 239 * @param proxy The proxy to insert. 240 */ 241 242 public synchronized void insertMarkupProxy(MarkupProxy proxy) 243 { 244 insertComponent((Component)proxy); 245 } 246 247 /** Italicize the currently-selected content. */ 248 249 public synchronized void setItalic() 250 { 251 setCharacterAttributes(a_italic, false); 252 } 253 254 /** Boldify the currently-selected content. */ 255 256 public synchronized void setBold() 257 { 258 setCharacterAttributes(a_bold, false); 259 } 260 261 /** Underline the currently-selected content. */ 262 263 public synchronized void setUnderline() 264 { 265 setCharacterAttributes(a_underline, false); 266 } 267 268 /** Remove all style attributes from the currently-selected content. */ 269 270 public synchronized void setPlain() 271 { 272 setCharacterAttributes(a_plain, true); 273 } 274 275 /* escape < and > */ 276 277 private String escapeText(String s) 278 { 279 StringBuffer sb = new StringBuffer(); 280 281 for(int i = 0; i < s.length(); i++) 282 { 283 char c = s.charAt(i); 284 285 if(c == '<') 286 sb.append("<lt>"); 287 else if(c == '>') 288 sb.append("<gt>"); 289 else 290 sb.append(c); 291 } 292 return(sb.toString()); 293 } 294 295 // xml consumer 296 297 private class TextReader implements XMLConsumer 298 { 299 private String lastString = null; 300 private boolean italicf = false, boldf = false, underlinef = false; 301 302 TextReader(String text) 303 { 304 XMLParser parser = new XMLParser(new StringReader(text), this); 305 try 306 { 307 doc.remove(0, doc.getLength()); 308 parser.parse(); 309 310 if(lastString != null) 311 insertString(); 312 } 313 catch(Exception ex) {} 314 } 315 316 public void consumeText(String s) 317 { 318 if(lastString != null) 319 insertString(); 320 lastString = s; 321 } 322 323 private void insertString() 324 { 325 insertString(lastString); 326 } 327 328 private void insertString(String s) 329 { 330 SimpleAttributeSet attrs = new SimpleAttributeSet(); 331 332 if(italicf) 333 StyleConstants.setItalic(attrs, true); 334 335 if(boldf) 336 StyleConstants.setBold(attrs, true); 337 338 if(underlinef) 339 StyleConstants.setUnderline(attrs, true); 340 341 try 342 { 343 doc.insertString(doc.getLength(), s, attrs); 344 } 345 catch(/*BadLocation*/Exception ex) 346 { 347 ex.printStackTrace(); 348 } 349 } 350 351 public boolean consumeElement(XMLElement e) 352 { 353 // dump out last string, if any 354 355 if(lastString != null) 356 insertString(); 357 358 // adjust style state 359 360 String tag = e.getTag(); 361 if(tag.equals("b")) 362 boldf = !e.isEnd(); 363 364 else if(tag.equals("i")) 365 italicf = !e.isEnd(); 366 367 else if(tag.equals("u")) 368 underlinef = !e.isEnd(); 369 370 else if(tag.equals("lt")) 371 insertString("<"); 372 373 else if(tag.equals("gt")) 374 insertString(">"); 375 376 else 377 { 378 MarkupProxy pxy = proxyFactory.getMarkupProxy(e); 379 if(pxy != null) 380 { 381 SimpleAttributeSet attrs2 = new SimpleAttributeSet(); 382 StyleConstants.setComponent(attrs2, pxy); 383 try 384 { 385 doc.insertString(doc.getLength(), " ", attrs2); 386 } 387 catch(BadLocationException ex) {} 388 } 389 } 390 lastString = null; 391 return(false); 392 } 393 } 394 395 } 396 397/* end of source file */