2
|
1 |
/*
|
715
|
2 |
* Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
|
2
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
4 |
*
|
|
5 |
* This code is free software; you can redistribute it and/or modify it
|
|
6 |
* under the terms of the GNU General Public License version 2 only, as
|
|
7 |
* published by the Free Software Foundation. Sun designates this
|
|
8 |
* particular file as subject to the "Classpath" exception as provided
|
|
9 |
* by Sun in the LICENSE file that accompanied this code.
|
|
10 |
*
|
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that
|
|
15 |
* accompanied this code).
|
|
16 |
*
|
|
17 |
* You should have received a copy of the GNU General Public License version
|
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
20 |
*
|
|
21 |
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
22 |
* CA 95054 USA or visit www.sun.com if you need additional information or
|
|
23 |
* have any questions.
|
|
24 |
*/
|
|
25 |
package javax.swing;
|
|
26 |
|
|
27 |
import java.awt.*;
|
|
28 |
import java.awt.event.ActionEvent;
|
|
29 |
|
|
30 |
import java.io.ObjectOutputStream;
|
|
31 |
import java.io.ObjectInputStream;
|
|
32 |
import java.io.IOException;
|
|
33 |
|
|
34 |
import javax.swing.text.*;
|
|
35 |
import javax.swing.event.*;
|
|
36 |
import javax.swing.plaf.*;
|
|
37 |
|
|
38 |
/**
|
|
39 |
* A text component that can be marked up with attributes that are
|
|
40 |
* represented graphically.
|
|
41 |
* You can find how-to information and examples of using text panes in
|
|
42 |
* <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/text.html">Using Text Components</a>,
|
|
43 |
* a section in <em>The Java Tutorial.</em>
|
|
44 |
*
|
|
45 |
* <p>
|
|
46 |
* This component models paragraphs
|
|
47 |
* that are composed of runs of character level attributes. Each
|
|
48 |
* paragraph may have a logical style attached to it which contains
|
|
49 |
* the default attributes to use if not overridden by attributes set
|
|
50 |
* on the paragraph or character run. Components and images may
|
|
51 |
* be embedded in the flow of text.
|
|
52 |
* <p>
|
|
53 |
* <dl>
|
|
54 |
* <dt><b><font size=+1>Newlines</font></b>
|
|
55 |
* <dd>
|
|
56 |
* For a discussion on how newlines are handled, see
|
|
57 |
* <a href="text/DefaultEditorKit.html">DefaultEditorKit</a>.
|
|
58 |
* </dl>
|
|
59 |
*
|
|
60 |
* <p>
|
|
61 |
* <strong>Warning:</strong> Swing is not thread safe. For more
|
|
62 |
* information see <a
|
|
63 |
* href="package-summary.html#threading">Swing's Threading
|
|
64 |
* Policy</a>.
|
|
65 |
* <p>
|
|
66 |
* <strong>Warning:</strong>
|
|
67 |
* Serialized objects of this class will not be compatible with
|
|
68 |
* future Swing releases. The current serialization support is
|
|
69 |
* appropriate for short term storage or RMI between applications running
|
|
70 |
* the same version of Swing. As of 1.4, support for long term storage
|
|
71 |
* of all JavaBeans<sup><font size="-2">TM</font></sup>
|
|
72 |
* has been added to the <code>java.beans</code> package.
|
|
73 |
* Please see {@link java.beans.XMLEncoder}.
|
|
74 |
*
|
|
75 |
* @beaninfo
|
|
76 |
* attribute: isContainer true
|
|
77 |
* description: A text component that can be marked up with attributes that are graphically represented.
|
|
78 |
*
|
|
79 |
* @author Timothy Prinzing
|
|
80 |
* @see javax.swing.text.StyledEditorKit
|
|
81 |
*/
|
|
82 |
public class JTextPane extends JEditorPane {
|
|
83 |
|
|
84 |
/**
|
|
85 |
* Creates a new <code>JTextPane</code>. A new instance of
|
|
86 |
* <code>StyledEditorKit</code> is
|
|
87 |
* created and set, and the document model set to <code>null</code>.
|
|
88 |
*/
|
|
89 |
public JTextPane() {
|
|
90 |
super();
|
|
91 |
EditorKit editorKit = createDefaultEditorKit();
|
|
92 |
String contentType = editorKit.getContentType();
|
|
93 |
if (contentType != null
|
|
94 |
&& getEditorKitClassNameForContentType(contentType) ==
|
|
95 |
defaultEditorKitMap.get(contentType)) {
|
|
96 |
setEditorKitForContentType(contentType, editorKit);
|
|
97 |
}
|
|
98 |
setEditorKit(editorKit);
|
|
99 |
}
|
|
100 |
|
|
101 |
/**
|
|
102 |
* Creates a new <code>JTextPane</code>, with a specified document model.
|
|
103 |
* A new instance of <code>javax.swing.text.StyledEditorKit</code>
|
|
104 |
* is created and set.
|
|
105 |
*
|
|
106 |
* @param doc the document model
|
|
107 |
*/
|
|
108 |
public JTextPane(StyledDocument doc) {
|
|
109 |
this();
|
|
110 |
setStyledDocument(doc);
|
|
111 |
}
|
|
112 |
|
|
113 |
/**
|
|
114 |
* Returns the class ID for the UI.
|
|
115 |
*
|
|
116 |
* @return the string "TextPaneUI"
|
|
117 |
*
|
|
118 |
* @see JComponent#getUIClassID
|
|
119 |
* @see UIDefaults#getUI
|
|
120 |
*/
|
|
121 |
public String getUIClassID() {
|
|
122 |
return uiClassID;
|
|
123 |
}
|
|
124 |
|
|
125 |
/**
|
|
126 |
* Associates the editor with a text document. This
|
|
127 |
* must be a <code>StyledDocument</code>.
|
|
128 |
*
|
|
129 |
* @param doc the document to display/edit
|
|
130 |
* @exception IllegalArgumentException if <code>doc</code> can't
|
|
131 |
* be narrowed to a <code>StyledDocument</code> which is the
|
|
132 |
* required type of model for this text component
|
|
133 |
*/
|
|
134 |
public void setDocument(Document doc) {
|
|
135 |
if (doc instanceof StyledDocument) {
|
|
136 |
super.setDocument(doc);
|
|
137 |
} else {
|
|
138 |
throw new IllegalArgumentException("Model must be StyledDocument");
|
|
139 |
}
|
|
140 |
}
|
|
141 |
|
|
142 |
/**
|
|
143 |
* Associates the editor with a text document.
|
|
144 |
* The currently registered factory is used to build a view for
|
|
145 |
* the document, which gets displayed by the editor.
|
|
146 |
*
|
|
147 |
* @param doc the document to display/edit
|
|
148 |
*/
|
|
149 |
public void setStyledDocument(StyledDocument doc) {
|
|
150 |
super.setDocument(doc);
|
|
151 |
}
|
|
152 |
|
|
153 |
/**
|
|
154 |
* Fetches the model associated with the editor.
|
|
155 |
*
|
|
156 |
* @return the model
|
|
157 |
*/
|
|
158 |
public StyledDocument getStyledDocument() {
|
|
159 |
return (StyledDocument) getDocument();
|
|
160 |
}
|
|
161 |
|
|
162 |
/**
|
|
163 |
* Replaces the currently selected content with new content
|
|
164 |
* represented by the given string. If there is no selection
|
|
165 |
* this amounts to an insert of the given text. If there
|
|
166 |
* is no replacement text this amounts to a removal of the
|
|
167 |
* current selection. The replacement text will have the
|
|
168 |
* attributes currently defined for input at the point of
|
|
169 |
* insertion. If the document is not editable, beep and return.
|
|
170 |
*
|
|
171 |
* @param content the content to replace the selection with
|
|
172 |
*/
|
|
173 |
public void replaceSelection(String content) {
|
|
174 |
replaceSelection(content, true);
|
|
175 |
}
|
|
176 |
|
|
177 |
private void replaceSelection(String content, boolean checkEditable) {
|
|
178 |
if (checkEditable && !isEditable()) {
|
|
179 |
UIManager.getLookAndFeel().provideErrorFeedback(JTextPane.this);
|
|
180 |
return;
|
|
181 |
}
|
|
182 |
Document doc = getStyledDocument();
|
|
183 |
if (doc != null) {
|
|
184 |
try {
|
|
185 |
Caret caret = getCaret();
|
|
186 |
int p0 = Math.min(caret.getDot(), caret.getMark());
|
|
187 |
int p1 = Math.max(caret.getDot(), caret.getMark());
|
|
188 |
AttributeSet attr = getInputAttributes().copyAttributes();
|
|
189 |
if (doc instanceof AbstractDocument) {
|
|
190 |
((AbstractDocument)doc).replace(p0, p1 - p0, content,attr);
|
|
191 |
}
|
|
192 |
else {
|
|
193 |
if (p0 != p1) {
|
|
194 |
doc.remove(p0, p1 - p0);
|
|
195 |
}
|
|
196 |
if (content != null && content.length() > 0) {
|
|
197 |
doc.insertString(p0, content, attr);
|
|
198 |
}
|
|
199 |
}
|
|
200 |
} catch (BadLocationException e) {
|
|
201 |
UIManager.getLookAndFeel().provideErrorFeedback(JTextPane.this);
|
|
202 |
}
|
|
203 |
}
|
|
204 |
}
|
|
205 |
|
|
206 |
/**
|
|
207 |
* Inserts a component into the document as a replacement
|
|
208 |
* for the currently selected content. If there is no
|
|
209 |
* selection the component is effectively inserted at the
|
|
210 |
* current position of the caret. This is represented in
|
|
211 |
* the associated document as an attribute of one character
|
|
212 |
* of content.
|
|
213 |
* <p>
|
|
214 |
* The component given is the actual component used by the
|
|
215 |
* JTextPane. Since components cannot be a child of more than
|
|
216 |
* one container, this method should not be used in situations
|
|
217 |
* where the model is shared by text components.
|
|
218 |
* <p>
|
|
219 |
* The component is placed relative to the text baseline
|
|
220 |
* according to the value returned by
|
|
221 |
* <code>Component.getAlignmentY</code>. For Swing components
|
|
222 |
* this value can be conveniently set using the method
|
|
223 |
* <code>JComponent.setAlignmentY</code>. For example, setting
|
|
224 |
* a value of <code>0.75</code> will cause 75 percent of the
|
|
225 |
* component to be above the baseline, and 25 percent of the
|
|
226 |
* component to be below the baseline.
|
|
227 |
*
|
|
228 |
* @param c the component to insert
|
|
229 |
*/
|
|
230 |
public void insertComponent(Component c) {
|
|
231 |
MutableAttributeSet inputAttributes = getInputAttributes();
|
|
232 |
inputAttributes.removeAttributes(inputAttributes);
|
|
233 |
StyleConstants.setComponent(inputAttributes, c);
|
|
234 |
replaceSelection(" ", false);
|
|
235 |
inputAttributes.removeAttributes(inputAttributes);
|
|
236 |
}
|
|
237 |
|
|
238 |
/**
|
|
239 |
* Inserts an icon into the document as a replacement
|
|
240 |
* for the currently selected content. If there is no
|
|
241 |
* selection the icon is effectively inserted at the
|
|
242 |
* current position of the caret. This is represented in
|
|
243 |
* the associated document as an attribute of one character
|
|
244 |
* of content.
|
|
245 |
*
|
|
246 |
* @param g the icon to insert
|
|
247 |
* @see Icon
|
|
248 |
*/
|
|
249 |
public void insertIcon(Icon g) {
|
|
250 |
MutableAttributeSet inputAttributes = getInputAttributes();
|
|
251 |
inputAttributes.removeAttributes(inputAttributes);
|
|
252 |
StyleConstants.setIcon(inputAttributes, g);
|
|
253 |
replaceSelection(" ", false);
|
|
254 |
inputAttributes.removeAttributes(inputAttributes);
|
|
255 |
}
|
|
256 |
|
|
257 |
/**
|
|
258 |
* Adds a new style into the logical style hierarchy. Style attributes
|
|
259 |
* resolve from bottom up so an attribute specified in a child
|
|
260 |
* will override an attribute specified in the parent.
|
|
261 |
*
|
|
262 |
* @param nm the name of the style (must be unique within the
|
|
263 |
* collection of named styles). The name may be <code>null</code>
|
|
264 |
* if the style is unnamed, but the caller is responsible
|
|
265 |
* for managing the reference returned as an unnamed style can't
|
|
266 |
* be fetched by name. An unnamed style may be useful for things
|
|
267 |
* like character attribute overrides such as found in a style
|
|
268 |
* run.
|
|
269 |
* @param parent the parent style. This may be <code>null</code>
|
|
270 |
* if unspecified
|
|
271 |
* attributes need not be resolved in some other style.
|
|
272 |
* @return the new <code>Style</code>
|
|
273 |
*/
|
|
274 |
public Style addStyle(String nm, Style parent) {
|
|
275 |
StyledDocument doc = getStyledDocument();
|
|
276 |
return doc.addStyle(nm, parent);
|
|
277 |
}
|
|
278 |
|
|
279 |
/**
|
|
280 |
* Removes a named non-<code>null</code> style previously added to
|
|
281 |
* the document.
|
|
282 |
*
|
|
283 |
* @param nm the name of the style to remove
|
|
284 |
*/
|
|
285 |
public void removeStyle(String nm) {
|
|
286 |
StyledDocument doc = getStyledDocument();
|
|
287 |
doc.removeStyle(nm);
|
|
288 |
}
|
|
289 |
|
|
290 |
/**
|
|
291 |
* Fetches a named non-<code>null</code> style previously added.
|
|
292 |
*
|
|
293 |
* @param nm the name of the style
|
|
294 |
* @return the <code>Style</code>
|
|
295 |
*/
|
|
296 |
public Style getStyle(String nm) {
|
|
297 |
StyledDocument doc = getStyledDocument();
|
|
298 |
return doc.getStyle(nm);
|
|
299 |
}
|
|
300 |
|
|
301 |
/**
|
|
302 |
* Sets the logical style to use for the paragraph at the
|
|
303 |
* current caret position. If attributes aren't explicitly set
|
|
304 |
* for character and paragraph attributes they will resolve
|
|
305 |
* through the logical style assigned to the paragraph, which
|
|
306 |
* in term may resolve through some hierarchy completely
|
|
307 |
* independent of the element hierarchy in the document.
|
|
308 |
*
|
|
309 |
* @param s the logical style to assign to the paragraph,
|
|
310 |
* or <code>null</code> for no style
|
|
311 |
*/
|
|
312 |
public void setLogicalStyle(Style s) {
|
|
313 |
StyledDocument doc = getStyledDocument();
|
|
314 |
doc.setLogicalStyle(getCaretPosition(), s);
|
|
315 |
}
|
|
316 |
|
|
317 |
/**
|
|
318 |
* Fetches the logical style assigned to the paragraph represented
|
|
319 |
* by the current position of the caret, or <code>null</code>.
|
|
320 |
*
|
|
321 |
* @return the <code>Style</code>
|
|
322 |
*/
|
|
323 |
public Style getLogicalStyle() {
|
|
324 |
StyledDocument doc = getStyledDocument();
|
|
325 |
return doc.getLogicalStyle(getCaretPosition());
|
|
326 |
}
|
|
327 |
|
|
328 |
/**
|
|
329 |
* Fetches the character attributes in effect at the
|
|
330 |
* current location of the caret, or <code>null</code>.
|
|
331 |
*
|
|
332 |
* @return the attributes, or <code>null</code>
|
|
333 |
*/
|
|
334 |
public AttributeSet getCharacterAttributes() {
|
|
335 |
StyledDocument doc = getStyledDocument();
|
|
336 |
Element run = doc.getCharacterElement(getCaretPosition());
|
|
337 |
if (run != null) {
|
|
338 |
return run.getAttributes();
|
|
339 |
}
|
|
340 |
return null;
|
|
341 |
}
|
|
342 |
|
|
343 |
/**
|
|
344 |
* Applies the given attributes to character
|
|
345 |
* content. If there is a selection, the attributes
|
|
346 |
* are applied to the selection range. If there
|
|
347 |
* is no selection, the attributes are applied to
|
|
348 |
* the input attribute set which defines the attributes
|
|
349 |
* for any new text that gets inserted.
|
|
350 |
*
|
|
351 |
* @param attr the attributes
|
|
352 |
* @param replace if true, then replace the existing attributes first
|
|
353 |
*/
|
|
354 |
public void setCharacterAttributes(AttributeSet attr, boolean replace) {
|
|
355 |
int p0 = getSelectionStart();
|
|
356 |
int p1 = getSelectionEnd();
|
|
357 |
if (p0 != p1) {
|
|
358 |
StyledDocument doc = getStyledDocument();
|
|
359 |
doc.setCharacterAttributes(p0, p1 - p0, attr, replace);
|
|
360 |
} else {
|
|
361 |
MutableAttributeSet inputAttributes = getInputAttributes();
|
|
362 |
if (replace) {
|
|
363 |
inputAttributes.removeAttributes(inputAttributes);
|
|
364 |
}
|
|
365 |
inputAttributes.addAttributes(attr);
|
|
366 |
}
|
|
367 |
}
|
|
368 |
|
|
369 |
/**
|
|
370 |
* Fetches the current paragraph attributes in effect
|
|
371 |
* at the location of the caret, or <code>null</code> if none.
|
|
372 |
*
|
|
373 |
* @return the attributes
|
|
374 |
*/
|
|
375 |
public AttributeSet getParagraphAttributes() {
|
|
376 |
StyledDocument doc = getStyledDocument();
|
|
377 |
Element paragraph = doc.getParagraphElement(getCaretPosition());
|
|
378 |
if (paragraph != null) {
|
|
379 |
return paragraph.getAttributes();
|
|
380 |
}
|
|
381 |
return null;
|
|
382 |
}
|
|
383 |
|
|
384 |
/**
|
|
385 |
* Applies the given attributes to paragraphs. If
|
|
386 |
* there is a selection, the attributes are applied
|
|
387 |
* to the paragraphs that intersect the selection.
|
|
388 |
* If there is no selection, the attributes are applied
|
|
389 |
* to the paragraph at the current caret position.
|
|
390 |
*
|
|
391 |
* @param attr the non-<code>null</code> attributes
|
|
392 |
* @param replace if true, replace the existing attributes first
|
|
393 |
*/
|
|
394 |
public void setParagraphAttributes(AttributeSet attr, boolean replace) {
|
|
395 |
int p0 = getSelectionStart();
|
|
396 |
int p1 = getSelectionEnd();
|
|
397 |
StyledDocument doc = getStyledDocument();
|
|
398 |
doc.setParagraphAttributes(p0, p1 - p0, attr, replace);
|
|
399 |
}
|
|
400 |
|
|
401 |
/**
|
|
402 |
* Gets the input attributes for the pane.
|
|
403 |
*
|
|
404 |
* @return the attributes
|
|
405 |
*/
|
|
406 |
public MutableAttributeSet getInputAttributes() {
|
|
407 |
return getStyledEditorKit().getInputAttributes();
|
|
408 |
}
|
|
409 |
|
|
410 |
/**
|
|
411 |
* Gets the editor kit.
|
|
412 |
*
|
|
413 |
* @return the editor kit
|
|
414 |
*/
|
|
415 |
protected final StyledEditorKit getStyledEditorKit() {
|
|
416 |
return (StyledEditorKit) getEditorKit();
|
|
417 |
}
|
|
418 |
|
|
419 |
/**
|
|
420 |
* @see #getUIClassID
|
|
421 |
* @see #readObject
|
|
422 |
*/
|
|
423 |
private static final String uiClassID = "TextPaneUI";
|
|
424 |
|
|
425 |
|
|
426 |
/**
|
|
427 |
* See <code>readObject</code> and <code>writeObject</code> in
|
|
428 |
* <code>JComponent</code> for more
|
|
429 |
* information about serialization in Swing.
|
|
430 |
*
|
|
431 |
* @param s the output stream
|
|
432 |
*/
|
|
433 |
private void writeObject(ObjectOutputStream s) throws IOException {
|
|
434 |
s.defaultWriteObject();
|
|
435 |
if (getUIClassID().equals(uiClassID)) {
|
|
436 |
byte count = JComponent.getWriteObjCounter(this);
|
|
437 |
JComponent.setWriteObjCounter(this, --count);
|
|
438 |
if (count == 0 && ui != null) {
|
|
439 |
ui.installUI(this);
|
|
440 |
}
|
|
441 |
}
|
|
442 |
}
|
|
443 |
|
|
444 |
|
|
445 |
// --- JEditorPane ------------------------------------
|
|
446 |
|
|
447 |
/**
|
|
448 |
* Creates the <code>EditorKit</code> to use by default. This
|
|
449 |
* is implemented to return <code>javax.swing.text.StyledEditorKit</code>.
|
|
450 |
*
|
|
451 |
* @return the editor kit
|
|
452 |
*/
|
|
453 |
protected EditorKit createDefaultEditorKit() {
|
|
454 |
return new StyledEditorKit();
|
|
455 |
}
|
|
456 |
|
|
457 |
/**
|
|
458 |
* Sets the currently installed kit for handling
|
|
459 |
* content. This is the bound property that
|
|
460 |
* establishes the content type of the editor.
|
|
461 |
*
|
|
462 |
* @param kit the desired editor behavior
|
|
463 |
* @exception IllegalArgumentException if kit is not a
|
|
464 |
* <code>StyledEditorKit</code>
|
|
465 |
*/
|
|
466 |
public final void setEditorKit(EditorKit kit) {
|
|
467 |
if (kit instanceof StyledEditorKit) {
|
|
468 |
super.setEditorKit(kit);
|
|
469 |
} else {
|
|
470 |
throw new IllegalArgumentException("Must be StyledEditorKit");
|
|
471 |
}
|
|
472 |
}
|
|
473 |
|
|
474 |
/**
|
|
475 |
* Returns a string representation of this <code>JTextPane</code>.
|
|
476 |
* This method
|
|
477 |
* is intended to be used only for debugging purposes, and the
|
|
478 |
* content and format of the returned string may vary between
|
|
479 |
* implementations. The returned string may be empty but may not
|
|
480 |
* be <code>null</code>.
|
|
481 |
*
|
|
482 |
* @return a string representation of this <code>JTextPane</code>
|
|
483 |
*/
|
|
484 |
protected String paramString() {
|
|
485 |
return super.paramString();
|
|
486 |
}
|
|
487 |
|
|
488 |
}
|