/* |
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. |
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
* |
|
* This code is free software; you can redistribute it and/or modify it |
|
* under the terms of the GNU General Public License version 2 only, as |
|
* published by the Free Software Foundation. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle in the LICENSE file that accompanied this code. |
|
* |
|
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
* version 2 for more details (a copy is included in the LICENSE file that |
|
* accompanied this code). |
|
* |
|
* You should have received a copy of the GNU General Public License version |
|
* 2 along with this work; if not, write to the Free Software Foundation, |
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
* |
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.com if you need additional information or have any |
|
* questions. |
|
*/ |
|
package javax.swing; |
|
import java.awt.*; |
|
import java.awt.event.ActionEvent; |
|
import java.io.ObjectOutputStream; |
|
import java.io.ObjectInputStream; |
|
import java.io.IOException; |
|
import javax.swing.text.*; |
|
import javax.swing.event.*; |
|
import javax.swing.plaf.*; |
|
/** |
|
* A text component that can be marked up with attributes that are |
|
* represented graphically. |
|
* You can find how-to information and examples of using text panes in |
|
* <a href="https://docs.oracle.com/javase/tutorial/uiswing/components/text.html">Using Text Components</a>, |
|
* a section in <em>The Java Tutorial.</em> |
|
* |
|
* <p> |
|
* This component models paragraphs |
|
* that are composed of runs of character level attributes. Each |
|
* paragraph may have a logical style attached to it which contains |
|
* the default attributes to use if not overridden by attributes set |
|
* on the paragraph or character run. Components and images may |
|
* be embedded in the flow of text. |
|
* |
|
* <dl> |
|
* <dt><b><font size=+1>Newlines</font></b> |
|
* <dd> |
|
* For a discussion on how newlines are handled, see |
|
* <a href="text/DefaultEditorKit.html">DefaultEditorKit</a>. |
|
* </dl> |
|
* |
|
* <p> |
|
* <strong>Warning:</strong> Swing is not thread safe. For more |
|
* information see <a |
|
* href="package-summary.html#threading">Swing's Threading |
|
* Policy</a>. |
|
* <p> |
|
* <strong>Warning:</strong> |
|
* Serialized objects of this class will not be compatible with |
|
* future Swing releases. The current serialization support is |
|
* appropriate for short term storage or RMI between applications running |
|
* the same version of Swing. As of 1.4, support for long term storage |
|
* of all JavaBeans™ |
|
* has been added to the <code>java.beans</code> package. |
|
* Please see {@link java.beans.XMLEncoder}. |
|
* |
|
* @beaninfo |
|
* attribute: isContainer true |
|
* description: A text component that can be marked up with attributes that are graphically represented. |
|
* |
|
* @author Timothy Prinzing |
|
* @see javax.swing.text.StyledEditorKit |
|
*/ |
|
public class JTextPane extends JEditorPane { |
|
/** |
|
* Creates a new <code>JTextPane</code>. A new instance of |
|
* <code>StyledEditorKit</code> is |
|
* created and set, and the document model set to <code>null</code>. |
|
*/ |
|
public JTextPane() { |
|
super(); |
|
EditorKit editorKit = createDefaultEditorKit(); |
|
String contentType = editorKit.getContentType(); |
|
if (contentType != null |
|
&& getEditorKitClassNameForContentType(contentType) == |
|
defaultEditorKitMap.get(contentType)) { |
|
setEditorKitForContentType(contentType, editorKit); |
|
} |
|
setEditorKit(editorKit); |
|
} |
|
/** |
|
* Creates a new <code>JTextPane</code>, with a specified document model. |
|
* A new instance of <code>javax.swing.text.StyledEditorKit</code> |
|
* is created and set. |
|
* |
|
* @param doc the document model |
|
*/ |
|
public JTextPane(StyledDocument doc) { |
|
this(); |
|
setStyledDocument(doc); |
|
} |
|
/** |
|
* Returns the class ID for the UI. |
|
* |
|
* @return the string "TextPaneUI" |
|
* |
|
* @see JComponent#getUIClassID |
|
* @see UIDefaults#getUI |
|
*/ |
|
public String getUIClassID() { |
|
return uiClassID; |
|
} |
|
/** |
|
* Associates the editor with a text document. This |
|
* must be a <code>StyledDocument</code>. |
|
* |
|
* @param doc the document to display/edit |
|
* @exception IllegalArgumentException if <code>doc</code> can't |
|
* be narrowed to a <code>StyledDocument</code> which is the |
|
* required type of model for this text component |
|
*/ |
|
public void setDocument(Document doc) { |
|
if (doc instanceof StyledDocument) { |
|
super.setDocument(doc); |
|
} else { |
|
throw new IllegalArgumentException("Model must be StyledDocument"); |
|
} |
|
} |
|
/** |
|
* Associates the editor with a text document. |
|
* The currently registered factory is used to build a view for |
|
* the document, which gets displayed by the editor. |
|
* |
|
* @param doc the document to display/edit |
|
*/ |
|
public void setStyledDocument(StyledDocument doc) { |
|
super.setDocument(doc); |
|
} |
|
/** |
|
* Fetches the model associated with the editor. |
|
* |
|
* @return the model |
|
*/ |
|
public StyledDocument getStyledDocument() { |
|
return (StyledDocument) getDocument(); |
|
} |
|
/** |
|
* Replaces the currently selected content with new content |
|
* represented by the given string. If there is no selection |
|
* this amounts to an insert of the given text. If there |
|
* is no replacement text this amounts to a removal of the |
|
* current selection. The replacement text will have the |
|
* attributes currently defined for input at the point of |
|
* insertion. If the document is not editable, beep and return. |
|
* |
|
* @param content the content to replace the selection with |
|
*/ |
|
@Override |
|
public void replaceSelection(String content) { |
|
replaceSelection(content, true); |
|
} |
|
private void replaceSelection(String content, boolean checkEditable) { |
|
if (checkEditable && !isEditable()) { |
|
UIManager.getLookAndFeel().provideErrorFeedback(JTextPane.this); |
|
return; |
|
} |
|
Document doc = getStyledDocument(); |
|
if (doc != null) { |
|
try { |
|
Caret caret = getCaret(); |
|
boolean composedTextSaved = saveComposedText(caret.getDot()); |
|
int p0 = Math.min(caret.getDot(), caret.getMark()); |
|
int p1 = Math.max(caret.getDot(), caret.getMark()); |
|
AttributeSet attr = getInputAttributes().copyAttributes(); |
|
if (doc instanceof AbstractDocument) { |
|
((AbstractDocument)doc).replace(p0, p1 - p0, content,attr); |
|
} |
|
else { |
|
if (p0 != p1) { |
|
doc.remove(p0, p1 - p0); |
|
} |
|
if (content != null && content.length() > 0) { |
|
doc.insertString(p0, content, attr); |
|
} |
|
} |
|
if (composedTextSaved) { |
|
restoreComposedText(); |
|
} |
|
} catch (BadLocationException e) { |
|
UIManager.getLookAndFeel().provideErrorFeedback(JTextPane.this); |
|
} |
|
} |
|
} |
|
/** |
|
* Inserts a component into the document as a replacement |
|
* for the currently selected content. If there is no |
|
* selection the component is effectively inserted at the |
|
* current position of the caret. This is represented in |
|
* the associated document as an attribute of one character |
|
* of content. |
|
* <p> |
|
* The component given is the actual component used by the |
|
* JTextPane. Since components cannot be a child of more than |
|
* one container, this method should not be used in situations |
|
* where the model is shared by text components. |
|
* <p> |
|
* The component is placed relative to the text baseline |
|
* according to the value returned by |
|
* <code>Component.getAlignmentY</code>. For Swing components |
|
* this value can be conveniently set using the method |
|
* <code>JComponent.setAlignmentY</code>. For example, setting |
|
* a value of <code>0.75</code> will cause 75 percent of the |
|
* component to be above the baseline, and 25 percent of the |
|
* component to be below the baseline. |
|
* |
|
* @param c the component to insert |
|
*/ |
|
public void insertComponent(Component c) { |
|
MutableAttributeSet inputAttributes = getInputAttributes(); |
|
inputAttributes.removeAttributes(inputAttributes); |
|
StyleConstants.setComponent(inputAttributes, c); |
|
replaceSelection(" ", false); |
|
inputAttributes.removeAttributes(inputAttributes); |
|
} |
|
/** |
|
* Inserts an icon into the document as a replacement |
|
* for the currently selected content. If there is no |
|
* selection the icon is effectively inserted at the |
|
* current position of the caret. This is represented in |
|
* the associated document as an attribute of one character |
|
* of content. |
|
* |
|
* @param g the icon to insert |
|
* @see Icon |
|
*/ |
|
public void insertIcon(Icon g) { |
|
MutableAttributeSet inputAttributes = getInputAttributes(); |
|
inputAttributes.removeAttributes(inputAttributes); |
|
StyleConstants.setIcon(inputAttributes, g); |
|
replaceSelection(" ", false); |
|
inputAttributes.removeAttributes(inputAttributes); |
|
} |
|
/** |
|
* Adds a new style into the logical style hierarchy. Style attributes |
|
* resolve from bottom up so an attribute specified in a child |
|
* will override an attribute specified in the parent. |
|
* |
|
* @param nm the name of the style (must be unique within the |
|
* collection of named styles). The name may be <code>null</code> |
|
* if the style is unnamed, but the caller is responsible |
|
* for managing the reference returned as an unnamed style can't |
|
* be fetched by name. An unnamed style may be useful for things |
|
* like character attribute overrides such as found in a style |
|
* run. |
|
* @param parent the parent style. This may be <code>null</code> |
|
* if unspecified |
|
* attributes need not be resolved in some other style. |
|
* @return the new <code>Style</code> |
|
*/ |
|
public Style addStyle(String nm, Style parent) { |
|
StyledDocument doc = getStyledDocument(); |
|
return doc.addStyle(nm, parent); |
|
} |
|
/** |
|
* Removes a named non-<code>null</code> style previously added to |
|
* the document. |
|
* |
|
* @param nm the name of the style to remove |
|
*/ |
|
public void removeStyle(String nm) { |
|
StyledDocument doc = getStyledDocument(); |
|
doc.removeStyle(nm); |
|
} |
|
/** |
|
* Fetches a named non-<code>null</code> style previously added. |
|
* |
|
* @param nm the name of the style |
|
* @return the <code>Style</code> |
|
*/ |
|
public Style getStyle(String nm) { |
|
StyledDocument doc = getStyledDocument(); |
|
return doc.getStyle(nm); |
|
} |
|
/** |
|
* Sets the logical style to use for the paragraph at the |
|
* current caret position. If attributes aren't explicitly set |
|
* for character and paragraph attributes they will resolve |
|
* through the logical style assigned to the paragraph, which |
|
* in term may resolve through some hierarchy completely |
|
* independent of the element hierarchy in the document. |
|
* |
|
* @param s the logical style to assign to the paragraph, |
|
* or <code>null</code> for no style |
|
*/ |
|
public void setLogicalStyle(Style s) { |
|
StyledDocument doc = getStyledDocument(); |
|
doc.setLogicalStyle(getCaretPosition(), s); |
|
} |
|
/** |
|
* Fetches the logical style assigned to the paragraph represented |
|
* by the current position of the caret, or <code>null</code>. |
|
* |
|
* @return the <code>Style</code> |
|
*/ |
|
public Style getLogicalStyle() { |
|
StyledDocument doc = getStyledDocument(); |
|
return doc.getLogicalStyle(getCaretPosition()); |
|
} |
|
/** |
|
* Fetches the character attributes in effect at the |
|
* current location of the caret, or <code>null</code>. |
|
* |
|
* @return the attributes, or <code>null</code> |
|
*/ |
|
public AttributeSet getCharacterAttributes() { |
|
StyledDocument doc = getStyledDocument(); |
|
Element run = doc.getCharacterElement(getCaretPosition()); |
|
if (run != null) { |
|
return run.getAttributes(); |
|
} |
|
return null; |
|
} |
|
/** |
|
* Applies the given attributes to character |
|
* content. If there is a selection, the attributes |
|
* are applied to the selection range. If there |
|
* is no selection, the attributes are applied to |
|
* the input attribute set which defines the attributes |
|
* for any new text that gets inserted. |
|
* |
|
* @param attr the attributes |
|
* @param replace if true, then replace the existing attributes first |
|
*/ |
|
public void setCharacterAttributes(AttributeSet attr, boolean replace) { |
|
int p0 = getSelectionStart(); |
|
int p1 = getSelectionEnd(); |
|
if (p0 != p1) { |
|
StyledDocument doc = getStyledDocument(); |
|
doc.setCharacterAttributes(p0, p1 - p0, attr, replace); |
|
} else { |
|
MutableAttributeSet inputAttributes = getInputAttributes(); |
|
if (replace) { |
|
inputAttributes.removeAttributes(inputAttributes); |
|
} |
|
inputAttributes.addAttributes(attr); |
|
} |
|
} |
|
/** |
|
* Fetches the current paragraph attributes in effect |
|
* at the location of the caret, or <code>null</code> if none. |
|
* |
|
* @return the attributes |
|
*/ |
|
public AttributeSet getParagraphAttributes() { |
|
StyledDocument doc = getStyledDocument(); |
|
Element paragraph = doc.getParagraphElement(getCaretPosition()); |
|
if (paragraph != null) { |
|
return paragraph.getAttributes(); |
|
} |
|
return null; |
|
} |
|
/** |
|
* Applies the given attributes to paragraphs. If |
|
* there is a selection, the attributes are applied |
|
* to the paragraphs that intersect the selection. |
|
* If there is no selection, the attributes are applied |
|
* to the paragraph at the current caret position. |
|
* |
|
* @param attr the non-<code>null</code> attributes |
|
* @param replace if true, replace the existing attributes first |
|
*/ |
|
public void setParagraphAttributes(AttributeSet attr, boolean replace) { |
|
int p0 = getSelectionStart(); |
|
int p1 = getSelectionEnd(); |
|
StyledDocument doc = getStyledDocument(); |
|
doc.setParagraphAttributes(p0, p1 - p0, attr, replace); |
|
} |
|
/** |
|
* Gets the input attributes for the pane. |
|
* |
|
* @return the attributes |
|
*/ |
|
public MutableAttributeSet getInputAttributes() { |
|
return getStyledEditorKit().getInputAttributes(); |
|
} |
|
/** |
|
* Gets the editor kit. |
|
* |
|
* @return the editor kit |
|
*/ |
|
protected final StyledEditorKit getStyledEditorKit() { |
|
return (StyledEditorKit) getEditorKit(); |
|
} |
|
/** |
|
* @see #getUIClassID |
|
* @see #readObject |
|
*/ |
|
private static final String uiClassID = "TextPaneUI"; |
|
/** |
|
* See <code>readObject</code> and <code>writeObject</code> in |
|
* <code>JComponent</code> for more |
|
* information about serialization in Swing. |
|
* |
|
* @param s the output stream |
|
*/ |
|
private void writeObject(ObjectOutputStream s) throws IOException { |
|
s.defaultWriteObject(); |
|
if (getUIClassID().equals(uiClassID)) { |
|
byte count = JComponent.getWriteObjCounter(this); |
|
JComponent.setWriteObjCounter(this, --count); |
|
if (count == 0 && ui != null) { |
|
ui.installUI(this); |
|
} |
|
} |
|
} |
|
// --- JEditorPane ------------------------------------ |
|
/** |
|
* Creates the <code>EditorKit</code> to use by default. This |
|
* is implemented to return <code>javax.swing.text.StyledEditorKit</code>. |
|
* |
|
* @return the editor kit |
|
*/ |
|
protected EditorKit createDefaultEditorKit() { |
|
return new StyledEditorKit(); |
|
} |
|
/** |
|
* Sets the currently installed kit for handling |
|
* content. This is the bound property that |
|
* establishes the content type of the editor. |
|
* |
|
* @param kit the desired editor behavior |
|
* @exception IllegalArgumentException if kit is not a |
|
* <code>StyledEditorKit</code> |
|
*/ |
|
public final void setEditorKit(EditorKit kit) { |
|
if (kit instanceof StyledEditorKit) { |
|
super.setEditorKit(kit); |
|
} else { |
|
throw new IllegalArgumentException("Must be StyledEditorKit"); |
|
} |
|
} |
|
/** |
|
* Returns a string representation of this <code>JTextPane</code>. |
|
* This method |
|
* is intended to be used only for debugging purposes, and the |
|
* content and format of the returned string may vary between |
|
* implementations. The returned string may be empty but may not |
|
* be <code>null</code>. |
|
* |
|
* @return a string representation of this <code>JTextPane</code> |
|
*/ |
|
protected String paramString() { |
|
return super.paramString(); |
|
} |
|
} |