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 */