I'm trying to monitor changes to a JTextPane. But I'm a bit stumped
by the DocumentEvent.GetChange method. No matter what element I pass
to it it returns null. The code has gotten a bit out of hand, but my
last act of desperation was the following:
doc.addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
logChange(e);
}
public void insertUpdate(DocumentEvent e) {
logChange(e);
}
public void removeUpdate(DocumentEvent e) {
logChange(e);
}
private void logChange(DocumentEvent e) {
if (e.getDocument() instanceof
DefaultStyledDocument) {
try {
DefaultStyledDocument doc =
(DefaultStyledDocument)e.getDocument();
ArrayList<Element> els = new
ArrayList<Element>();
GetAllElements(doc, els);
DocumentEvent.ElementChange chg =
null;
for (Element elem : els) {
chg = e.getChange(elem);
if (chg != null) {
s.append("Found One!!" +
elem.getName());
break;
}
}
} catch (Exception ex) {
LogTextArea.append(ex.getMessage());
}
}
}
});
Needless to say the words "Found One!!" are never appended to s. I
really have no idea what I need to do to get hold of the
DocumentEvent.ElementChange object. Any ideas?
Cheers,
Mick
Post an SSCCE* that has been run through the TWC**.
* <http://pscode.org/sscce.html>
** Linked from *.
> Cheers,
No worries.
--
Andrew Thompson
http://pscode.org/
I can only suggest you to look at the DocumentEvent from a java debugger
and see what's in it.
Fair call, example follows. I'm sure it could be shorter, but this
compiles and should show what the problem is.
import java.util.ArrayList;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.*;
import javax.swing.*;
public class MainJFrame extends JFrame {
public MainJFrame() {
initComponents();
AddListener();
}
@SuppressWarnings("unchecked")
private void initComponents() {
EditScrollPane = new javax.swing.JScrollPane();
EditPane = new javax.swing.JTextPane();
LogScrollPane = new javax.swing.JScrollPane();
LogTextArea = new javax.swing.JTextArea();
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
EditScrollPane.setViewportView(EditPane);
LogTextArea.setColumns(20);
LogTextArea.setRows(5);
LogScrollPane.setViewportView(LogTextArea);
GroupLayout layout = new GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(
GroupLayout.Alignment.LEADING)
.addGroup(GroupLayout.Alignment.TRAILING,
layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(
GroupLayout.Alignment.TRAILING)
.addComponent(LogScrollPane,
GroupLayout.Alignment.LEADING,
GroupLayout.DEFAULT_SIZE,
380, Short.MAX_VALUE)
.addComponent(EditScrollPane,
GroupLayout.Alignment.LEADING,
GroupLayout.DEFAULT_SIZE,
380, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(
GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(EditScrollPane,
GroupLayout.PREFERRED_SIZE,
232,
GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(LogScrollPane,
GroupLayout.PREFERRED_SIZE,
105,
GroupLayout.PREFERRED_SIZE)
.addContainerGap(GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE))
);
pack();
}
private void AddListener() {
try {
Document doc = EditPane.getDocument();
doc.addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
logChange(e);
}
public void insertUpdate(DocumentEvent e) {
logChange(e);
}
public void removeUpdate(DocumentEvent e) {
logChange(e);
}
private void logChange(DocumentEvent e) {
StringBuffer s = new StringBuffer();
s.append(e.getType().toString() + ":");
if (e.getDocument()
instanceof DefaultStyledDocument) {
try {
DefaultStyledDocument doc =
(DefaultStyledDocument)
e.getDocument();
s.append(doc.getText(
e.getOffset(),
e.getLength()));
ArrayList<Element> els =
new ArrayList<Element>();
GetAllElements(doc, els);
DocumentEvent.ElementChange chg
= null;
for (Element elem : els) {
chg = e.getChange(elem);
if (chg != null) {
s.append("Found One!!"
+ elem.getName());
break;
}
}
} catch (Exception ex) {
LogTextArea.append(
ex.getMessage());
}
}
LogTextArea.append(s.toString());
}
});
} catch (Exception e) {
LogTextArea.append(e.getMessage());
}
}
private void GetAllElements(Document doc,
ArrayList<Element> array) {
Element[] roots = doc.getRootElements();
for (Element el : roots) {
array.add(el);
GetAllElements(el, array);
}
}
private void GetAllElements(Element el,
ArrayList<Element> array) {
for (int i = 0; i < el.getElementCount(); i++) {
array.add(el.getElement(i));
GetAllElements(el.getElement(i), array);
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MainJFrame().setVisible(true);
}
});
}
private JTextPane EditPane;
private JScrollPane EditScrollPane;
private JScrollPane LogScrollPane;
private JTextArea LogTextArea;
}
> On Dec 5 2008, 5:18 pm, Andrew Thompson <andrewtho...@gmail.com>
> wrote:
> > On Dec 5, 4:57 pm, mick.heyw...@gmail.com wrote:
[...]
> > * <http://pscode.org/sscce.html>
[...]
> Fair call, example follows. I'm sure it could be shorter, but this
> compiles and should show what the problem is.
Try this. I modified your example for compatibility and style: 1.5,
case, layout, StringBuilder, etc. I re-factored your getAllXXX methods,
which were coming up empty.
OT: For some reason the document element "bidi" (bi-directional text)
always reminds me of the homonymous Hindi word:
<http://en.wikipedia.org/wiki/Beedi>
<sscce>
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
public class MainJFrame extends JFrame {
private JTextPane editPane = new javax.swing.JTextPane();
private JScrollPane editScroll = new javax.swing.JScrollPane();
private JTextArea logArea = new javax.swing.JTextArea();
private JScrollPane logScroll = new javax.swing.JScrollPane();
public MainJFrame() {
initComponents();
addListener();
}
private void initComponents() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
editPane.setPreferredSize(new Dimension(0, 60));
editScroll.setViewportView(editPane);
logArea.setColumns(32);
logArea.setRows(24);
logScroll.setViewportView(logArea);
this.add(editScroll, BorderLayout.NORTH);
this.add(logScroll, BorderLayout.CENTER);
pack();
}
private void addListener() {
Document doc = editPane.getDocument();
doc.addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
logChange(e);
}
public void insertUpdate(DocumentEvent e) {
logChange(e);
}
public void removeUpdate(DocumentEvent e) {
logChange(e);
}
private void logChange(DocumentEvent e) {
StringBuilder s = new StringBuilder();
s.append(e.getType());
s.append(": ");
Document doc = e.getDocument();
try {
int offset = e.getOffset();
int length = e.getLength();
s.append(doc.getText(offset, length));
s.append(" at [");
s.append(offset);
s.append(",");
s.append(length);
s.append("]\n");
List<Element> eList = getAllElements(doc);
for (Element elem : eList) {
s.append("Found: ");
s.append(elem.getName());
s.append("\n");
}
} catch (Exception ex) {
logArea.append(ex.getMessage());
}
logArea.append(s.toString());
}
});
}
private List<Element> getAllElements(Document doc) {
List<Element> list = new ArrayList<Element>();
Element[] roots = doc.getRootElements();
for (Element root : roots) {
list.add(root);
getAllChildren(root, list);
}
return list;
}
private void getAllChildren(Element root, List<Element> list) {
for (int i = 0; i < root.getElementCount(); i++) {
list.add(root.getElement(i));
getAllChildren(root.getElement(i), list);
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MainJFrame().setVisible(true);
}
});
}
}
</sscce>
--
John B. Matthews
trashgod at gmail dot com
http://home.roadrunner.com/~jbmatthews/
This works for inserts but not for removes, though. Is there any way to retrieve
the actual removed part of the element this way?
Regards
Stefan
Good point; no way that I can see. DocumentListener#removeUpdate() says,
"The range is given in terms of what the view last saw..."
<http://java.sun.com/javase/6/docs/api/javax/swing/event/DocumentListener.html>
I would seem that a different handler for each DocumentEvent is warranted.
Even then, a changed child may go unreported:
<http://java.sun.com/javase/6/docs/api/javax/swing/event/DocumentEvent.html>
Alas, there's no built-in version control.
Stefan has nailed the problem on the head - what happens with
removes? Your example, whilst much cleaner and neater than mine, has
removed the call to DocumentEvent.getChange(Element), which was what I
was trying to investigate (poorly) in the first place. My issue is
that getChange never returns anything except null - I tried with your
code as follows:
public MainJFrame() {
initComponents();
addListener();
}
private void initComponents() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.add(editScroll, BorderLayout.NORTH);
this.add(logScroll, BorderLayout.CENTER);
pack();
}
if (e.getChange(elem) != null) {
s.append("Found!!");
> On Jan 6, 5:23 am, "John B. Matthews" <nos...@nospam.com> wrote:
> > In article <4962335...@news.uni-rostock.de>,
> > Stefan Rybacki <noem...@noemail.foobar> wrote:
> >
> > > John B. Matthews schrieb:
[...]
> > > This works for inserts but not for removes, though. Is there any way
> > > to retrieve the actual removed part of the element this way?
> >
> > Good point; no way that I can see. DocumentListener#removeUpdate() says,
> > "The range is given in terms of what the view last saw..."
> >
> > <http://java.sun.com/javase/6/docs/api/javax/swing/event/DocumentListe...>
> >
> > I would seem that a different handler for each DocumentEvent is warranted.
> > Even then, a changed child may go unreported:
> >
> > <http://java.sun.com/javase/6/docs/api/javax/swing/event/DocumentEvent...>
> >
> > Alas, there's no built-in version control.
>
> Stefan has nailed the problem on the head - what happens with
> removes? Your example, whilst much cleaner and neater than mine, has
> removed the call to DocumentEvent.getChange(Element), which was what I
> was trying to investigate (poorly) in the first place. My issue is
> that getChange never returns anything except null - I tried with your
> code as follows:
[...]
> if (e.getChange(elem) != null) {
> s.append("Found!!");
> }
Oops, trust me to solve the problem by deleting it!
Looking at the changed element on typing VK_ENTER VK_BACK_SPACE, I see
this:
[...]
if (e.getChange(elem) != null) {
s.append("Found!!" + elem);
}
INSERT:
at [0,1]
Found: sectionFound!!BranchElement(section) 0,2
Found: paragraphFound!!BranchElement(paragraph) 0,1
Found: content
Found: paragraph
Found: content
Found: bidi root
Found: bidi level
REMOVE:
at [0,1]
Found: sectionFound!!BranchElement(section) 0,1
Found: paragraph
Found: content
Found: bidi root
Found: bidi level
OK now I understand. It only returns if there is a structural change
to the document, not for content changes within an element.
I must admit I find that non-intuitive - at the very least the doco
could be more verbose about what changes will yield a getChange
result. Many thanks for your help John.
[...]
> > Looking at the changed element on typing VK_ENTER VK_BACK_SPACE, I see
> > this:
> >
> > [...]
> > if (e.getChange(elem) != null) {
> > s.append("Found!!" + elem);
> > }
> >
> > INSERT:
> > at [0,1]
> > Found: sectionFound!!BranchElement(section) 0,2
> >
> > Found: paragraphFound!!BranchElement(paragraph) 0,1
> >
> > Found: content
> > Found: paragraph
> > Found: content
> > Found: bidi root
> > Found: bidi level
> > REMOVE:
> > at [0,1]
> > Found: sectionFound!!BranchElement(section) 0,1
> >
> > Found: paragraph
> > Found: content
> > Found: bidi root
> > Found: bidi level
>
> OK now I understand. It only returns if there is a structural change
> to the document, not for content changes within an element.
>
> I must admit I find that non-intuitive - at the very least the doco
> could be more verbose about what changes will yield a getChange
> result.
Hard to argue with that. The overview discusses remove() at some length:
<http://java.sun.com/javase/6/docs/api/javax/swing/text/Document.html>
And DocumentEvent#getChange() mentions structural changes:
<http://java.sun.com/javase/6/docs/api/javax/swing/event/DocumentEvent.ht
ml>
Finally, the Element interface mentions that the structural components
are "intended to capture the spirit of an SGML element."
<http://java.sun.com/javase/6/docs/api/javax/swing/text/Element.html>
> Many thanks for your help John.
--