How to make difference between textField.setText() and adding text to textField manually in java?
Asked Answered
P

3

10

I have a textField in my application which will be initiated programmatically (textField.setText() ) when user clicked in an item in JList. later user will change this value manually. I get stuck with using document-listener to detect changes in this text field. When changes happens programmatically it must do nothing but if manually happens, it should change the background to red.

How to detect whether textField has been filled out manually or by textField.setText()?

txtMode.getDocument().addDocumentListener(new DocumentListener() {
        public void insertUpdate(DocumentEvent e) {
            if (!mode.equals(e.getDocument()))
            txtMode.setBackground(Color.red);
        }

        public void removeUpdate(DocumentEvent e) {
            if (mode.equals(e.getDocument()))
            txtMode.setBackground(Color.white);              
        }

        public void changedUpdate(DocumentEvent e) {
            //To change body of implemented methods
        }
    });
Ph answered 20/6, 2012 at 12:53 Comment(0)
E
8

there are two ways

  • remove DocumentListener before setText("...") add DocumentListener back if done

code

public void attachDocumentListener(JComponent compo){
      compo.addDocumentListener(someDocumentListener);
}

//similair void for remove....
  • use boolean value for disabling "if needed", but you have to change contens of your DocumentListener

for example

 txtMode.getDocument().addDocumentListener(new DocumentListener() {
    public void insertUpdate(DocumentEvent e) {
        if (!mode.equals(e.getDocument()))

        if (!updateFromModel){
           txtMode.setBackground(Color.red);
        }  
    }

    public void removeUpdate(DocumentEvent e) {
        if (mode.equals(e.getDocument()))

        if (!updateFromModel){
           txtMode.setBackground(Color.white);
        }  
    }

    public void changedUpdate(DocumentEvent e) {
        //To change body of implemented methods
    }
});
Executrix answered 20/6, 2012 at 13:2 Comment(4)
The catch here is that sometimes the setText() is called by someone that isn't your own code. This is the problem I'm fighting against at the moment, anyway. I wish they could have just had a wasTriggeredByUser() on every event.Abscond
@Trejkaz please how, where, why, is there some special reason, btw Document is Model for JTextComponents, then consume / firing all changes/events betweens model to view and vice versaExecutrix
This problem is much more complicated than it first appears. Here are two complications: 1) some property changes on Swing components call invokeLater within (or some equivalent), which posts additional events to the event queue! This means removing and adding a listener may not exclude those additional events. 2) What constitutes "wasTriggeredByUser"? If the user enters text in the JList per the OP and that updates the JTextField... is that triggered by the user? I mean, the definition of this proposed method is highly opinionated.Bewray
Neither of the solutions here would work reliably. I've been struggling with this problem for 6 months or more and I've tried these solutions.Bewray
S
5

Keep in mind that all the event listeners are executed on the Swing Event Thread. So things might not go in the exact order as you want them to. In this situation, any solution will be hack-ish because you can not get total control over the swing thread and who posts events on it.

What I'm trying to say is this: suppose you chose to use some flag to let your listeners know that it's a programmatic change. Here's the possible scenario (I assume you're following the nice rule of doing any UI updates from the swing thread via invokeLater):

  1. set the flag to skip events
  2. setText
  3. set flag to false

if you do all of it in one invocation, the setText will trigger update events posted to the end of the event queue, thus, by the time they are executed, the flag will already be false.

So you should do step 1 and 2 in one invokeLater call, or even invokeAndWait, and then post another event with invokeLater to unset the flag. And pray that your user is not so fast as to make some changes between these two invocations, otherwise, they'de be considered a programmatic change as well.

Shaer answered 20/6, 2012 at 13:30 Comment(1)
This is exactly the problem. Furthermore, the user does not have to be so fast. Because sometimes events get backlogged and there's a queue of user events waiting to be processed. If the app hangs for just a fraction of a second while the user is typing then it's possible two characters will be queued up as KeyEvents in succession. The first will do steps 1 and 2 and the second KeyEvent will be considered as not a user change. In the OP, this won't be a problem since the OP is trying to color the text field on change, but in other scenarios this would be a problem.Bewray
N
0

This doesn't solve the issue that user Hakanai mentioned about being unable to detect external, programmatic calls to setText. However, if only being able to guard against calls made from within your own code is satisfactory, then one solution could be to create a specific lock object within your class, synchronize on it during the block of code that calls setText, and then call Thread.holdsLock() to detect if you're executing in this block, and if so bail out.

public class SomeGuiClass {

    private final Object txtModeSetTextLock = new Object();

    public SomeGuiClass() {
        // ...
        txtMode.getDocument().addDocumentListener(
            new DocumentListener() {
                public void insertUpdate(DocumentEvent e) {
                    if (!isProgrammaticUpdate()){
                        txtMode.setBackground(Color.red);
                    }
                }

                public void removeUpdate(DocumentEvent e) {
                    if (!isProgrammaticUpdate()){
                        txtMode.setBackground(Color.white);
                    }
                }

                public void changedUpdate(DocumentEvent e) {
                }

                private boolean isProgrammaticUpdate() {
                    return Thread.holdsLock(txtModeSetTextLock);
                }
            }
        );
    }

    private void someOtherListenerInvocation() {
        synchronized(txtModeSetTextLock) {
            // ...
            txtMode.setText("...");
        }
    }

}
Naiad answered 26/4 at 23:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.