Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Problem with DocumentEvent.GetChange

79 views
Skip to first unread message

mick.h...@gmail.com

unread,
Dec 5, 2008, 12:57:52 AM12/5/08
to
Hi there,

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

Andrew Thompson

unread,
Dec 5, 2008, 1:18:23 AM12/5/08
to
On Dec 5, 4:57 pm, mick.heyw...@gmail.com wrote:
> ...Any ideas?

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/

Sigfried

unread,
Dec 5, 2008, 3:03:56 AM12/5/08
to
mick.h...@gmail.com a écrit :

I can only suggest you to look at the DocumentEvent from a java debugger
and see what's in it.

mick.h...@gmail.com

unread,
Jan 4, 2009, 7:35:03 PM1/4/09
to
On Dec 5 2008, 5:18 pm, Andrew Thompson <andrewtho...@gmail.com>
wrote:

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;

}

John B. Matthews

unread,
Jan 5, 2009, 11:13:32 AM1/5/09
to
In article
<6332e879-ea0d-42bf...@x16g2000prn.googlegroups.com>,
mick.h...@gmail.com wrote:

> 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/

Stefan Rybacki

unread,
Jan 5, 2009, 11:20:38 AM1/5/09
to
John B. Matthews schrieb:
>...
>
> <sscce>
> ...
> </sscce>
>

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

John B. Matthews

unread,
Jan 5, 2009, 1:23:42 PM1/5/09
to
In article <49623353$1...@news.uni-rostock.de>,
Stefan Rybacki <noe...@noemail.foobar> wrote:

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.

mick.h...@gmail.com

unread,
Jan 5, 2009, 5:35:56 PM1/5/09
to
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:
> > >...
> > > <sscce>
> >  > ...
> > > </sscce>
>
> > 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.
>
> --
> John B. Matthews
> trashgod at gmail dot comhttp://home.roadrunner.com/~jbmatthews/

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!!");

John B. Matthews

unread,
Jan 5, 2009, 9:49:51 PM1/5/09
to
In article
<f14b6bbe-6f76-4d91...@r36g2000prf.googlegroups.com>,
mick.h...@gmail.com wrote:

> 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

mick.h...@gmail.com

unread,
Jan 5, 2009, 10:49:14 PM1/5/09
to
On Jan 6, 1:49 pm, "John B. Matthews" <nos...@nospam.com> wrote:
> In article
> <f14b6bbe-6f76-4d91-bfbf-3596e5867...@r36g2000prf.googlegroups.com>,

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.

John B. Matthews

unread,
Jan 6, 2009, 7:04:55 AM1/6/09
to
In article
<633cdfe1-85c0-441d...@y1g2000pra.googlegroups.com>,
mick.h...@gmail.com wrote:

[...]


> > 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.

--

0 new messages