41 |
import java.awt.Color; |
import java.awt.Color; |
42 |
import java.awt.Font; |
import java.awt.Font; |
43 |
import java.io.Serializable; |
import java.io.Serializable; |
44 |
|
import java.util.Enumeration; |
45 |
import java.util.Vector; |
import java.util.Vector; |
46 |
|
|
47 |
|
import javax.swing.event.ChangeEvent; |
48 |
|
import javax.swing.event.ChangeListener; |
49 |
import javax.swing.event.DocumentEvent; |
import javax.swing.event.DocumentEvent; |
50 |
|
import javax.swing.undo.AbstractUndoableEdit; |
51 |
|
import javax.swing.undo.UndoableEdit; |
52 |
|
|
53 |
/** |
/** |
54 |
* The default implementation of {@link StyledDocument}. |
* The default implementation of {@link StyledDocument}. |
66 |
implements StyledDocument |
implements StyledDocument |
67 |
{ |
{ |
68 |
/** |
/** |
69 |
|
* An {@link UndoableEdit} that can undo attribute changes to an element. |
70 |
|
* |
71 |
|
* @author Roman Kennke (kennke@aicas.com) |
72 |
|
*/ |
73 |
|
public static class AttributeUndoableEdit |
74 |
|
extends AbstractUndoableEdit |
75 |
|
{ |
76 |
|
/** |
77 |
|
* A copy of the old attributes. |
78 |
|
*/ |
79 |
|
protected AttributeSet copy; |
80 |
|
|
81 |
|
/** |
82 |
|
* The new attributes. |
83 |
|
*/ |
84 |
|
protected AttributeSet newAttributes; |
85 |
|
|
86 |
|
/** |
87 |
|
* If the new attributes replaced the old attributes or if they only were |
88 |
|
* added to them. |
89 |
|
*/ |
90 |
|
protected boolean isReplacing; |
91 |
|
|
92 |
|
/** |
93 |
|
* The element that has changed. |
94 |
|
*/ |
95 |
|
protected Element element; |
96 |
|
|
97 |
|
/** |
98 |
|
* Creates a new <code>AttributeUndoableEdit</code>. |
99 |
|
* |
100 |
|
* @param el the element that changes attributes |
101 |
|
* @param newAtts the new attributes |
102 |
|
* @param replacing if the new attributes replace the old or only append to |
103 |
|
* them |
104 |
|
*/ |
105 |
|
public AttributeUndoableEdit(Element el, AttributeSet newAtts, |
106 |
|
boolean replacing) |
107 |
|
{ |
108 |
|
element = el; |
109 |
|
newAttributes = newAtts; |
110 |
|
isReplacing = replacing; |
111 |
|
copy = el.getAttributes().copyAttributes(); |
112 |
|
} |
113 |
|
|
114 |
|
/** |
115 |
|
* Undos the attribute change. The <code>copy</code> field is set as |
116 |
|
* attributes on <code>element</code>. |
117 |
|
*/ |
118 |
|
public void undo() |
119 |
|
{ |
120 |
|
super.undo(); |
121 |
|
AttributeSet atts = element.getAttributes(); |
122 |
|
if (atts instanceof MutableAttributeSet) |
123 |
|
{ |
124 |
|
MutableAttributeSet mutable = (MutableAttributeSet) atts; |
125 |
|
mutable.removeAttributes(atts); |
126 |
|
mutable.addAttributes(copy); |
127 |
|
} |
128 |
|
} |
129 |
|
|
130 |
|
/** |
131 |
|
* Redos an attribute change. This adds <code>newAttributes</code> to the |
132 |
|
* <code>element</code>'s attribute set, possibly clearing all attributes |
133 |
|
* if <code>isReplacing</code> is true. |
134 |
|
*/ |
135 |
|
public void redo() |
136 |
|
{ |
137 |
|
super.undo(); |
138 |
|
AttributeSet atts = element.getAttributes(); |
139 |
|
if (atts instanceof MutableAttributeSet) |
140 |
|
{ |
141 |
|
MutableAttributeSet mutable = (MutableAttributeSet) atts; |
142 |
|
if (isReplacing) |
143 |
|
mutable.removeAttributes(atts); |
144 |
|
mutable.addAttributes(newAttributes); |
145 |
|
} |
146 |
|
} |
147 |
|
} |
148 |
|
|
149 |
|
/** |
150 |
* Carries specification information for new {@link Element}s that should |
* Carries specification information for new {@link Element}s that should |
151 |
* be created in {@link ElementBuffer}. This allows the parsing process |
* be created in {@link ElementBuffer}. This allows the parsing process |
152 |
* to be decoupled from the <code>Element</code> creation process. |
* to be decoupled from the <code>Element</code> creation process. |
800 |
} |
} |
801 |
} |
} |
802 |
|
|
803 |
|
/** |
804 |
|
* Receives notification when any of the document's style changes and calls |
805 |
|
* {@link DefaultStyledDocument#styleChanged(Style)}. |
806 |
|
* |
807 |
|
* @author Roman Kennke (kennke@aicas.com) |
808 |
|
*/ |
809 |
|
private class StyleChangeListener |
810 |
|
implements ChangeListener |
811 |
|
{ |
812 |
|
|
813 |
|
/** |
814 |
|
* Receives notification when any of the document's style changes and calls |
815 |
|
* {@link DefaultStyledDocument#styleChanged(Style)}. |
816 |
|
* |
817 |
|
* @param event the change event |
818 |
|
*/ |
819 |
|
public void stateChanged(ChangeEvent event) |
820 |
|
{ |
821 |
|
Style style = (Style) event.getSource(); |
822 |
|
styleChanged(style); |
823 |
|
} |
824 |
|
} |
825 |
|
|
826 |
/** The serialization UID (compatible with JDK1.5). */ |
/** The serialization UID (compatible with JDK1.5). */ |
827 |
private static final long serialVersionUID = 940485415728614849L; |
private static final long serialVersionUID = 940485415728614849L; |
828 |
|
|
838 |
protected DefaultStyledDocument.ElementBuffer buffer; |
protected DefaultStyledDocument.ElementBuffer buffer; |
839 |
|
|
840 |
/** |
/** |
841 |
|
* Listens for changes on this document's styles and notifies styleChanged(). |
842 |
|
*/ |
843 |
|
private StyleChangeListener styleChangeListener; |
844 |
|
|
845 |
|
/** |
846 |
* Creates a new <code>DefaultStyledDocument</code>. |
* Creates a new <code>DefaultStyledDocument</code>. |
847 |
*/ |
*/ |
848 |
public DefaultStyledDocument() |
public DefaultStyledDocument() |
895 |
public Style addStyle(String nm, Style parent) |
public Style addStyle(String nm, Style parent) |
896 |
{ |
{ |
897 |
StyleContext context = (StyleContext) getAttributeContext(); |
StyleContext context = (StyleContext) getAttributeContext(); |
898 |
return context.addStyle(nm, parent); |
Style newStyle = context.addStyle(nm, parent); |
899 |
|
|
900 |
|
// Register change listener. |
901 |
|
if (styleChangeListener == null) |
902 |
|
styleChangeListener = new StyleChangeListener(); |
903 |
|
newStyle.addChangeListener(styleChangeListener); |
904 |
|
|
905 |
|
return newStyle; |
906 |
} |
} |
907 |
|
|
908 |
/** |
/** |
1265 |
(ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]); |
(ElementSpec[]) specs.toArray(new ElementSpec[specs.size()]); |
1266 |
|
|
1267 |
buffer.insert(offset, length, elSpecs, ev); |
buffer.insert(offset, length, elSpecs, ev); |
1268 |
} |
} |
1269 |
|
|
1270 |
|
/** |
1271 |
|
* Returns an enumeration of all style names. |
1272 |
|
* |
1273 |
|
* @return an enumeration of all style names |
1274 |
|
*/ |
1275 |
|
public Enumeration getStyleNames() |
1276 |
|
{ |
1277 |
|
StyleContext context = (StyleContext) getAttributeContext(); |
1278 |
|
return context.getStyleNames(); |
1279 |
|
} |
1280 |
|
|
1281 |
|
/** |
1282 |
|
* Called when any of this document's styles changes. |
1283 |
|
* |
1284 |
|
* @param style the style that changed |
1285 |
|
*/ |
1286 |
|
protected void styleChanged(Style style) |
1287 |
|
{ |
1288 |
|
// Nothing to do here. This is intended to be overridden by subclasses. |
1289 |
|
} |
1290 |
|
|
1291 |
|
/** |
1292 |
|
* Inserts a bulk of structured content at once. |
1293 |
|
* |
1294 |
|
* @param offset the offset at which the content should be inserted |
1295 |
|
* @param data the actual content spec to be inserted |
1296 |
|
*/ |
1297 |
|
protected void insert(int offset, ElementSpec[] data) |
1298 |
|
throws BadLocationException |
1299 |
|
{ |
1300 |
|
writeLock(); |
1301 |
|
// First we insert the content. |
1302 |
|
int index = offset; |
1303 |
|
for (int i = 0; i < data.length; i++) |
1304 |
|
{ |
1305 |
|
ElementSpec spec = data[i]; |
1306 |
|
if (spec.getArray() != null && spec.getLength() > 0) |
1307 |
|
{ |
1308 |
|
String insertString = new String(spec.getArray(), spec.getOffset(), |
1309 |
|
spec.getLength()); |
1310 |
|
content.insertString(index, insertString); |
1311 |
|
} |
1312 |
|
index += spec.getLength(); |
1313 |
|
} |
1314 |
|
// Update the view structure. |
1315 |
|
DefaultDocumentEvent ev = new DefaultDocumentEvent(offset, index - offset, |
1316 |
|
DocumentEvent.EventType.INSERT); |
1317 |
|
for (int i = 0; i < data.length; i++) |
1318 |
|
{ |
1319 |
|
ElementSpec spec = data[i]; |
1320 |
|
AttributeSet atts = spec.getAttributes(); |
1321 |
|
if (atts != null) |
1322 |
|
insertUpdate(ev, atts); |
1323 |
|
} |
1324 |
|
|
1325 |
|
// Finally we must update the document structure and fire the insert update |
1326 |
|
// event. |
1327 |
|
buffer.insert(offset, index - offset, data, ev); |
1328 |
|
fireInsertUpdate(ev); |
1329 |
|
writeUnlock(); |
1330 |
|
} |
1331 |
|
|
1332 |
|
/** |
1333 |
|
* Initializes the <code>DefaultStyledDocument</code> with the specified |
1334 |
|
* data. |
1335 |
|
* |
1336 |
|
* @param data the specification of the content with which the document is |
1337 |
|
* initialized |
1338 |
|
*/ |
1339 |
|
protected void create(ElementSpec[] data) |
1340 |
|
{ |
1341 |
|
try |
1342 |
|
{ |
1343 |
|
// Clear content. |
1344 |
|
content.remove(0, content.length()); |
1345 |
|
// Clear buffer and root element. |
1346 |
|
buffer = new ElementBuffer(createDefaultRoot()); |
1347 |
|
// Insert the data. |
1348 |
|
insert(0, data); |
1349 |
|
} |
1350 |
|
catch (BadLocationException ex) |
1351 |
|
{ |
1352 |
|
AssertionError err = new AssertionError("Unexpected bad location"); |
1353 |
|
err.initCause(ex); |
1354 |
|
throw err; |
1355 |
|
} |
1356 |
|
} |
1357 |
} |
} |