author | peterz |
Fri, 25 Dec 2009 17:47:08 +0300 | |
changeset 4842 | c9f791782a29 |
parent 3750 | 4195b035138f |
child 5506 | 202f599c92aa |
permissions | -rw-r--r-- |
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 |
*/ |
|
3750
4195b035138f
6699856: Creating text in a JTextPane using Chinese text causes undesired behavior
gsm
parents:
715
diff
changeset
|
173 |
@Override |
2 | 174 |
public void replaceSelection(String content) { |
175 |
replaceSelection(content, true); |
|
176 |
} |
|
177 |
||
178 |
private void replaceSelection(String content, boolean checkEditable) { |
|
179 |
if (checkEditable && !isEditable()) { |
|
180 |
UIManager.getLookAndFeel().provideErrorFeedback(JTextPane.this); |
|
181 |
return; |
|
182 |
} |
|
183 |
Document doc = getStyledDocument(); |
|
184 |
if (doc != null) { |
|
185 |
try { |
|
186 |
Caret caret = getCaret(); |
|
3750
4195b035138f
6699856: Creating text in a JTextPane using Chinese text causes undesired behavior
gsm
parents:
715
diff
changeset
|
187 |
boolean composedTextSaved = saveComposedText(caret.getDot()); |
2 | 188 |
int p0 = Math.min(caret.getDot(), caret.getMark()); |
189 |
int p1 = Math.max(caret.getDot(), caret.getMark()); |
|
190 |
AttributeSet attr = getInputAttributes().copyAttributes(); |
|
191 |
if (doc instanceof AbstractDocument) { |
|
192 |
((AbstractDocument)doc).replace(p0, p1 - p0, content,attr); |
|
193 |
} |
|
194 |
else { |
|
195 |
if (p0 != p1) { |
|
196 |
doc.remove(p0, p1 - p0); |
|
197 |
} |
|
198 |
if (content != null && content.length() > 0) { |
|
199 |
doc.insertString(p0, content, attr); |
|
200 |
} |
|
201 |
} |
|
3750
4195b035138f
6699856: Creating text in a JTextPane using Chinese text causes undesired behavior
gsm
parents:
715
diff
changeset
|
202 |
if (composedTextSaved) { |
4195b035138f
6699856: Creating text in a JTextPane using Chinese text causes undesired behavior
gsm
parents:
715
diff
changeset
|
203 |
restoreComposedText(); |
4195b035138f
6699856: Creating text in a JTextPane using Chinese text causes undesired behavior
gsm
parents:
715
diff
changeset
|
204 |
} |
2 | 205 |
} catch (BadLocationException e) { |
206 |
UIManager.getLookAndFeel().provideErrorFeedback(JTextPane.this); |
|
207 |
} |
|
208 |
} |
|
209 |
} |
|
210 |
||
211 |
/** |
|
212 |
* Inserts a component into the document as a replacement |
|
213 |
* for the currently selected content. If there is no |
|
214 |
* selection the component is effectively inserted at the |
|
215 |
* current position of the caret. This is represented in |
|
216 |
* the associated document as an attribute of one character |
|
217 |
* of content. |
|
218 |
* <p> |
|
219 |
* The component given is the actual component used by the |
|
220 |
* JTextPane. Since components cannot be a child of more than |
|
221 |
* one container, this method should not be used in situations |
|
222 |
* where the model is shared by text components. |
|
223 |
* <p> |
|
224 |
* The component is placed relative to the text baseline |
|
225 |
* according to the value returned by |
|
226 |
* <code>Component.getAlignmentY</code>. For Swing components |
|
227 |
* this value can be conveniently set using the method |
|
228 |
* <code>JComponent.setAlignmentY</code>. For example, setting |
|
229 |
* a value of <code>0.75</code> will cause 75 percent of the |
|
230 |
* component to be above the baseline, and 25 percent of the |
|
231 |
* component to be below the baseline. |
|
232 |
* |
|
233 |
* @param c the component to insert |
|
234 |
*/ |
|
235 |
public void insertComponent(Component c) { |
|
236 |
MutableAttributeSet inputAttributes = getInputAttributes(); |
|
237 |
inputAttributes.removeAttributes(inputAttributes); |
|
238 |
StyleConstants.setComponent(inputAttributes, c); |
|
239 |
replaceSelection(" ", false); |
|
240 |
inputAttributes.removeAttributes(inputAttributes); |
|
241 |
} |
|
242 |
||
243 |
/** |
|
244 |
* Inserts an icon into the document as a replacement |
|
245 |
* for the currently selected content. If there is no |
|
246 |
* selection the icon is effectively inserted at the |
|
247 |
* current position of the caret. This is represented in |
|
248 |
* the associated document as an attribute of one character |
|
249 |
* of content. |
|
250 |
* |
|
251 |
* @param g the icon to insert |
|
252 |
* @see Icon |
|
253 |
*/ |
|
254 |
public void insertIcon(Icon g) { |
|
255 |
MutableAttributeSet inputAttributes = getInputAttributes(); |
|
256 |
inputAttributes.removeAttributes(inputAttributes); |
|
257 |
StyleConstants.setIcon(inputAttributes, g); |
|
258 |
replaceSelection(" ", false); |
|
259 |
inputAttributes.removeAttributes(inputAttributes); |
|
260 |
} |
|
261 |
||
262 |
/** |
|
263 |
* Adds a new style into the logical style hierarchy. Style attributes |
|
264 |
* resolve from bottom up so an attribute specified in a child |
|
265 |
* will override an attribute specified in the parent. |
|
266 |
* |
|
267 |
* @param nm the name of the style (must be unique within the |
|
268 |
* collection of named styles). The name may be <code>null</code> |
|
269 |
* if the style is unnamed, but the caller is responsible |
|
270 |
* for managing the reference returned as an unnamed style can't |
|
271 |
* be fetched by name. An unnamed style may be useful for things |
|
272 |
* like character attribute overrides such as found in a style |
|
273 |
* run. |
|
274 |
* @param parent the parent style. This may be <code>null</code> |
|
275 |
* if unspecified |
|
276 |
* attributes need not be resolved in some other style. |
|
277 |
* @return the new <code>Style</code> |
|
278 |
*/ |
|
279 |
public Style addStyle(String nm, Style parent) { |
|
280 |
StyledDocument doc = getStyledDocument(); |
|
281 |
return doc.addStyle(nm, parent); |
|
282 |
} |
|
283 |
||
284 |
/** |
|
285 |
* Removes a named non-<code>null</code> style previously added to |
|
286 |
* the document. |
|
287 |
* |
|
288 |
* @param nm the name of the style to remove |
|
289 |
*/ |
|
290 |
public void removeStyle(String nm) { |
|
291 |
StyledDocument doc = getStyledDocument(); |
|
292 |
doc.removeStyle(nm); |
|
293 |
} |
|
294 |
||
295 |
/** |
|
296 |
* Fetches a named non-<code>null</code> style previously added. |
|
297 |
* |
|
298 |
* @param nm the name of the style |
|
299 |
* @return the <code>Style</code> |
|
300 |
*/ |
|
301 |
public Style getStyle(String nm) { |
|
302 |
StyledDocument doc = getStyledDocument(); |
|
303 |
return doc.getStyle(nm); |
|
304 |
} |
|
305 |
||
306 |
/** |
|
307 |
* Sets the logical style to use for the paragraph at the |
|
308 |
* current caret position. If attributes aren't explicitly set |
|
309 |
* for character and paragraph attributes they will resolve |
|
310 |
* through the logical style assigned to the paragraph, which |
|
311 |
* in term may resolve through some hierarchy completely |
|
312 |
* independent of the element hierarchy in the document. |
|
313 |
* |
|
314 |
* @param s the logical style to assign to the paragraph, |
|
315 |
* or <code>null</code> for no style |
|
316 |
*/ |
|
317 |
public void setLogicalStyle(Style s) { |
|
318 |
StyledDocument doc = getStyledDocument(); |
|
319 |
doc.setLogicalStyle(getCaretPosition(), s); |
|
320 |
} |
|
321 |
||
322 |
/** |
|
323 |
* Fetches the logical style assigned to the paragraph represented |
|
324 |
* by the current position of the caret, or <code>null</code>. |
|
325 |
* |
|
326 |
* @return the <code>Style</code> |
|
327 |
*/ |
|
328 |
public Style getLogicalStyle() { |
|
329 |
StyledDocument doc = getStyledDocument(); |
|
330 |
return doc.getLogicalStyle(getCaretPosition()); |
|
331 |
} |
|
332 |
||
333 |
/** |
|
334 |
* Fetches the character attributes in effect at the |
|
335 |
* current location of the caret, or <code>null</code>. |
|
336 |
* |
|
337 |
* @return the attributes, or <code>null</code> |
|
338 |
*/ |
|
339 |
public AttributeSet getCharacterAttributes() { |
|
340 |
StyledDocument doc = getStyledDocument(); |
|
341 |
Element run = doc.getCharacterElement(getCaretPosition()); |
|
342 |
if (run != null) { |
|
343 |
return run.getAttributes(); |
|
344 |
} |
|
345 |
return null; |
|
346 |
} |
|
347 |
||
348 |
/** |
|
349 |
* Applies the given attributes to character |
|
350 |
* content. If there is a selection, the attributes |
|
351 |
* are applied to the selection range. If there |
|
352 |
* is no selection, the attributes are applied to |
|
353 |
* the input attribute set which defines the attributes |
|
354 |
* for any new text that gets inserted. |
|
355 |
* |
|
356 |
* @param attr the attributes |
|
357 |
* @param replace if true, then replace the existing attributes first |
|
358 |
*/ |
|
359 |
public void setCharacterAttributes(AttributeSet attr, boolean replace) { |
|
360 |
int p0 = getSelectionStart(); |
|
361 |
int p1 = getSelectionEnd(); |
|
362 |
if (p0 != p1) { |
|
363 |
StyledDocument doc = getStyledDocument(); |
|
364 |
doc.setCharacterAttributes(p0, p1 - p0, attr, replace); |
|
365 |
} else { |
|
366 |
MutableAttributeSet inputAttributes = getInputAttributes(); |
|
367 |
if (replace) { |
|
368 |
inputAttributes.removeAttributes(inputAttributes); |
|
369 |
} |
|
370 |
inputAttributes.addAttributes(attr); |
|
371 |
} |
|
372 |
} |
|
373 |
||
374 |
/** |
|
375 |
* Fetches the current paragraph attributes in effect |
|
376 |
* at the location of the caret, or <code>null</code> if none. |
|
377 |
* |
|
378 |
* @return the attributes |
|
379 |
*/ |
|
380 |
public AttributeSet getParagraphAttributes() { |
|
381 |
StyledDocument doc = getStyledDocument(); |
|
382 |
Element paragraph = doc.getParagraphElement(getCaretPosition()); |
|
383 |
if (paragraph != null) { |
|
384 |
return paragraph.getAttributes(); |
|
385 |
} |
|
386 |
return null; |
|
387 |
} |
|
388 |
||
389 |
/** |
|
390 |
* Applies the given attributes to paragraphs. If |
|
391 |
* there is a selection, the attributes are applied |
|
392 |
* to the paragraphs that intersect the selection. |
|
393 |
* If there is no selection, the attributes are applied |
|
394 |
* to the paragraph at the current caret position. |
|
395 |
* |
|
396 |
* @param attr the non-<code>null</code> attributes |
|
397 |
* @param replace if true, replace the existing attributes first |
|
398 |
*/ |
|
399 |
public void setParagraphAttributes(AttributeSet attr, boolean replace) { |
|
400 |
int p0 = getSelectionStart(); |
|
401 |
int p1 = getSelectionEnd(); |
|
402 |
StyledDocument doc = getStyledDocument(); |
|
403 |
doc.setParagraphAttributes(p0, p1 - p0, attr, replace); |
|
404 |
} |
|
405 |
||
406 |
/** |
|
407 |
* Gets the input attributes for the pane. |
|
408 |
* |
|
409 |
* @return the attributes |
|
410 |
*/ |
|
411 |
public MutableAttributeSet getInputAttributes() { |
|
412 |
return getStyledEditorKit().getInputAttributes(); |
|
413 |
} |
|
414 |
||
415 |
/** |
|
416 |
* Gets the editor kit. |
|
417 |
* |
|
418 |
* @return the editor kit |
|
419 |
*/ |
|
420 |
protected final StyledEditorKit getStyledEditorKit() { |
|
421 |
return (StyledEditorKit) getEditorKit(); |
|
422 |
} |
|
423 |
||
424 |
/** |
|
425 |
* @see #getUIClassID |
|
426 |
* @see #readObject |
|
427 |
*/ |
|
428 |
private static final String uiClassID = "TextPaneUI"; |
|
429 |
||
430 |
||
431 |
/** |
|
432 |
* See <code>readObject</code> and <code>writeObject</code> in |
|
433 |
* <code>JComponent</code> for more |
|
434 |
* information about serialization in Swing. |
|
435 |
* |
|
436 |
* @param s the output stream |
|
437 |
*/ |
|
438 |
private void writeObject(ObjectOutputStream s) throws IOException { |
|
439 |
s.defaultWriteObject(); |
|
440 |
if (getUIClassID().equals(uiClassID)) { |
|
441 |
byte count = JComponent.getWriteObjCounter(this); |
|
442 |
JComponent.setWriteObjCounter(this, --count); |
|
443 |
if (count == 0 && ui != null) { |
|
444 |
ui.installUI(this); |
|
445 |
} |
|
446 |
} |
|
447 |
} |
|
448 |
||
449 |
||
450 |
// --- JEditorPane ------------------------------------ |
|
451 |
||
452 |
/** |
|
453 |
* Creates the <code>EditorKit</code> to use by default. This |
|
454 |
* is implemented to return <code>javax.swing.text.StyledEditorKit</code>. |
|
455 |
* |
|
456 |
* @return the editor kit |
|
457 |
*/ |
|
458 |
protected EditorKit createDefaultEditorKit() { |
|
459 |
return new StyledEditorKit(); |
|
460 |
} |
|
461 |
||
462 |
/** |
|
463 |
* Sets the currently installed kit for handling |
|
464 |
* content. This is the bound property that |
|
465 |
* establishes the content type of the editor. |
|
466 |
* |
|
467 |
* @param kit the desired editor behavior |
|
468 |
* @exception IllegalArgumentException if kit is not a |
|
469 |
* <code>StyledEditorKit</code> |
|
470 |
*/ |
|
471 |
public final void setEditorKit(EditorKit kit) { |
|
472 |
if (kit instanceof StyledEditorKit) { |
|
473 |
super.setEditorKit(kit); |
|
474 |
} else { |
|
475 |
throw new IllegalArgumentException("Must be StyledEditorKit"); |
|
476 |
} |
|
477 |
} |
|
478 |
||
479 |
/** |
|
480 |
* Returns a string representation of this <code>JTextPane</code>. |
|
481 |
* This method |
|
482 |
* is intended to be used only for debugging purposes, and the |
|
483 |
* content and format of the returned string may vary between |
|
484 |
* implementations. The returned string may be empty but may not |
|
485 |
* be <code>null</code>. |
|
486 |
* |
|
487 |
* @return a string representation of this <code>JTextPane</code> |
|
488 |
*/ |
|
489 |
protected String paramString() { |
|
490 |
return super.paramString(); |
|
491 |
} |
|
492 |
||
493 |
} |