001 /* JTextPane.java -- A powerful text widget supporting styled text
002 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package javax.swing;
040
041 import java.awt.Component;
042
043 import javax.swing.text.AbstractDocument;
044 import javax.swing.text.AttributeSet;
045 import javax.swing.text.BadLocationException;
046 import javax.swing.text.Caret;
047 import javax.swing.text.Document;
048 import javax.swing.text.EditorKit;
049 import javax.swing.text.Element;
050 import javax.swing.text.MutableAttributeSet;
051 import javax.swing.text.SimpleAttributeSet;
052 import javax.swing.text.Style;
053 import javax.swing.text.StyleConstants;
054 import javax.swing.text.StyledDocument;
055 import javax.swing.text.StyledEditorKit;
056
057 /**
058 * A powerful text component that supports styled content as well as
059 * embedding images and components. It is entirely based on a
060 * {@link StyledDocument} content model and a {@link StyledEditorKit}.
061 *
062 * @author Roman Kennke (roman@kennke.org)
063 * @author Andrew Selkirk
064 */
065 public class JTextPane
066 extends JEditorPane
067 {
068 /**
069 * Creates a new <code>JTextPane</code> with a <code>null</code> document.
070 */
071 public JTextPane()
072 {
073 super();
074 }
075
076 /**
077 * Creates a new <code>JTextPane</code> and sets the specified
078 * <code>document</code>.
079 *
080 * @param document the content model to use
081 */
082 public JTextPane(StyledDocument document)
083 {
084 this();
085 setStyledDocument(document);
086 }
087
088 /**
089 * Returns the UI class ID. This is <code>TextPaneUI</code>.
090 *
091 * @return <code>TextPaneUI</code>
092 */
093 public String getUIClassID()
094 {
095 return "TextPaneUI";
096 }
097
098 /**
099 * Sets the content model for this <code>JTextPane</code>.
100 * <code>JTextPane</code> can only be used with {@link StyledDocument}s,
101 * if you try to set a different type of <code>Document</code>, an
102 * <code>IllegalArgumentException</code> is thrown.
103 *
104 * @param document the content model to set
105 *
106 * @throws IllegalArgumentException if <code>document</code> is not an
107 * instance of <code>StyledDocument</code>
108 *
109 * @see #setStyledDocument
110 */
111 public void setDocument(Document document)
112 {
113 if (document != null && !(document instanceof StyledDocument))
114 throw new IllegalArgumentException
115 ("JTextPane can only handle StyledDocuments");
116
117 setStyledDocument((StyledDocument) document);
118 }
119
120 /**
121 * Returns the {@link StyledDocument} that is the content model for
122 * this <code>JTextPane</code>. This is a typed wrapper for
123 * {@link #getDocument()}.
124 *
125 * @return the content model of this <code>JTextPane</code>
126 */
127 public StyledDocument getStyledDocument()
128 {
129 return (StyledDocument) super.getDocument();
130 }
131
132 /**
133 * Sets the content model for this <code>JTextPane</code>.
134 *
135 * @param document the content model to set
136 */
137 public void setStyledDocument(StyledDocument document)
138 {
139 super.setDocument(document);
140 }
141
142 /**
143 * Replaces the currently selected text with the specified
144 * <code>content</code>. If there is no selected text, this results
145 * in a simple insertion at the current caret position. If there is
146 * no <code>content</code> specified, this results in the selection
147 * beeing deleted.
148 *
149 * @param content the text with which the selection is replaced
150 */
151 public void replaceSelection(String content)
152 {
153 Caret caret = getCaret();
154 StyledDocument doc = getStyledDocument();
155 AttributeSet a = getInputAttributes().copyAttributes();
156 if (doc == null)
157 return;
158
159 int dot = caret.getDot();
160 int mark = caret.getMark();
161
162 int p0 = Math.min (dot, mark);
163 int p1 = Math.max (dot, mark);
164
165 try
166 {
167 if (doc instanceof AbstractDocument)
168 ((AbstractDocument)doc).replace(p0, p1 - p0, content, a);
169 else
170 {
171 // Remove selected text.
172 if (dot != mark)
173 doc.remove(p0, p1 - p0);
174 // Insert new text.
175 if (content != null && content.length() > 0)
176 doc.insertString(p0, content, a);
177 }
178 }
179 catch (BadLocationException e)
180 {
181 throw new AssertionError
182 ("No BadLocationException should be thrown here");
183 }
184 }
185
186 /**
187 * Inserts an AWT or Swing component into the text at the current caret
188 * position.
189 *
190 * @param component the component to be inserted
191 */
192 public void insertComponent(Component component)
193 {
194 SimpleAttributeSet atts = new SimpleAttributeSet();
195 atts.addAttribute(StyleConstants.ComponentAttribute, component);
196 atts.addAttribute(StyleConstants.NameAttribute,
197 StyleConstants.ComponentElementName);
198 try
199 {
200 getDocument().insertString(getCaret().getDot(), " ", atts);
201 }
202 catch (BadLocationException ex)
203 {
204 AssertionError err = new AssertionError("Unexpected bad location");
205 err.initCause(ex);
206 throw err;
207 }
208 }
209
210 /**
211 * Inserts an <code>Icon</code> into the text at the current caret position.
212 *
213 * @param icon the <code>Icon</code> to be inserted
214 */
215 public void insertIcon(Icon icon)
216 {
217 MutableAttributeSet inputAtts = getInputAttributes();
218 inputAtts.removeAttributes(inputAtts);
219 StyleConstants.setIcon(inputAtts, icon);
220 replaceSelection(" ");
221 inputAtts.removeAttributes(inputAtts);
222 }
223
224 /**
225 * Adds a style into the style hierarchy. Unspecified style attributes
226 * can be resolved in the <code>parent</code> style, if one is specified.
227 *
228 * While it is legal to add nameless styles (<code>nm == null</code),
229 * you must be aware that the client application is then responsible
230 * for managing the style hierarchy, since unnamed styles cannot be
231 * looked up by their name.
232 *
233 * @param nm the name of the style or <code>null</code> if the style should
234 * be unnamed
235 * @param parent the parent in which unspecified style attributes are
236 * resolved, or <code>null</code> if that is not necessary
237 *
238 * @return the newly created <code>Style</code>
239 */
240 public Style addStyle(String nm, Style parent)
241 {
242 return getStyledDocument().addStyle(nm, parent);
243 }
244
245 /**
246 * Removes a named <code>Style</code> from the style hierarchy.
247 *
248 * @param nm the name of the <code>Style</code> to be removed
249 */
250 public void removeStyle(String nm)
251 {
252 getStyledDocument().removeStyle(nm);
253 }
254
255 /**
256 * Looks up and returns a named <code>Style</code>.
257 *
258 * @param nm the name of the <code>Style</code>
259 *
260 * @return the found <code>Style</code> of <code>null</code> if no such
261 * <code>Style</code> exists
262 */
263 public Style getStyle(String nm)
264 {
265 return getStyledDocument().getStyle(nm);
266 }
267
268 /**
269 * Returns the logical style of the paragraph at the current caret position.
270 *
271 * @return the logical style of the paragraph at the current caret position
272 */
273 public Style getLogicalStyle()
274 {
275 return getStyledDocument().getLogicalStyle(getCaretPosition());
276 }
277
278 /**
279 * Sets the logical style for the paragraph at the current caret position.
280 *
281 * @param style the style to set for the current paragraph
282 */
283 public void setLogicalStyle(Style style)
284 {
285 getStyledDocument().setLogicalStyle(getCaretPosition(), style);
286 }
287
288 /**
289 * Returns the text attributes for the character at the current caret
290 * position.
291 *
292 * @return the text attributes for the character at the current caret
293 * position
294 */
295 public AttributeSet getCharacterAttributes()
296 {
297 StyledDocument doc = getStyledDocument();
298 Element el = doc.getCharacterElement(getCaretPosition());
299 return el.getAttributes();
300 }
301
302 /**
303 * Sets text attributes for the current selection. If there is no selection
304 * the text attributes are applied to newly inserted text
305 *
306 * @param attribute the text attributes to set
307 * @param replace if <code>true</code>, the attributes of the current
308 * selection are overridden, otherwise they are merged
309 *
310 * @see #getInputAttributes
311 */
312 public void setCharacterAttributes(AttributeSet attribute,
313 boolean replace)
314 {
315 int dot = getCaret().getDot();
316 int start = getSelectionStart();
317 int end = getSelectionEnd();
318 if (start == dot && end == dot)
319 // There is no selection, update insertAttributes instead
320 {
321 MutableAttributeSet inputAttributes =
322 getStyledEditorKit().getInputAttributes();
323 if (replace)
324 inputAttributes.removeAttributes(inputAttributes);
325 inputAttributes.addAttributes(attribute);
326 }
327 else
328 getStyledDocument().setCharacterAttributes(start, end - start, attribute,
329 replace);
330 }
331
332 /**
333 * Returns the text attributes of the paragraph at the current caret
334 * position.
335 *
336 * @return the attributes of the paragraph at the current caret position
337 */
338 public AttributeSet getParagraphAttributes()
339 {
340 StyledDocument doc = getStyledDocument();
341 Element el = doc.getParagraphElement(getCaretPosition());
342 return el.getAttributes();
343 }
344
345 /**
346 * Sets text attributes for the paragraph at the current selection.
347 * If there is no selection the text attributes are applied to
348 * the paragraph at the current caret position.
349 *
350 * @param attribute the text attributes to set
351 * @param replace if <code>true</code>, the attributes of the current
352 * selection are overridden, otherwise they are merged
353 */
354 public void setParagraphAttributes(AttributeSet attribute,
355 boolean replace)
356 {
357 // TODO
358 }
359
360 /**
361 * Returns the attributes that are applied to newly inserted text.
362 * This is a {@link MutableAttributeSet}, so you can easily modify these
363 * attributes.
364 *
365 * @return the attributes that are applied to newly inserted text
366 */
367 public MutableAttributeSet getInputAttributes()
368 {
369 return getStyledEditorKit().getInputAttributes();
370 }
371
372 /**
373 * Returns the {@link StyledEditorKit} that is currently used by this
374 * <code>JTextPane</code>.
375 *
376 * @return the current <code>StyledEditorKit</code> of this
377 * <code>JTextPane</code>
378 */
379 protected final StyledEditorKit getStyledEditorKit()
380 {
381 return (StyledEditorKit) getEditorKit();
382 }
383
384 /**
385 * Creates the default {@link EditorKit} that is used in
386 * <code>JTextPane</code>s. This is an instance of {@link StyledEditorKit}.
387 *
388 * @return the default {@link EditorKit} that is used in
389 * <code>JTextPane</code>s
390 */
391 protected EditorKit createDefaultEditorKit()
392 {
393 return new StyledEditorKit();
394 }
395
396 /**
397 * Sets the {@link EditorKit} to use for this <code>JTextPane</code>.
398 * <code>JTextPane</code>s can only handle {@link StyledEditorKit}s,
399 * if client programs try to set a different type of <code>EditorKit</code>
400 * then an IllegalArgumentException is thrown
401 *
402 * @param editor the <code>EditorKit</code> to set
403 *
404 * @throws IllegalArgumentException if <code>editor</code> is no
405 * <code>StyledEditorKit</code>
406 */
407 public final void setEditorKit(EditorKit editor)
408 {
409 if (!(editor instanceof StyledEditorKit))
410 throw new IllegalArgumentException
411 ("JTextPanes can only handle StyledEditorKits");
412 super.setEditorKit(editor);
413 }
414
415 /**
416 * Returns a param string that can be used for debugging.
417 *
418 * @return a param string that can be used for debugging.
419 */
420 protected String paramString()
421 {
422 return super.paramString(); // TODO
423 }
424 }