0

I have a JPanel with a JTextField and I'd like to implement waitForTextFieldSpace. I think making a custom JTextField would be the most elegant solution, but I'm not entirely sure.

public class MyPanel {
   JTextField textField;

   //...constructor, methods, etc

   public void waitForTextFieldSpace() {
      //...don't return until the user has pressed space in the text field
   }
}

I do have some code that has a JFrame wait for the space bar. But I'm not sure how to do the text field task above.

public void waitForKey(final int key) {
        final CountDownLatch latch = new CountDownLatch(1);
        KeyEventDispatcher dispatcher = new KeyEventDispatcher() {
            public boolean dispatchKeyEvent(KeyEvent e) {
                if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == key) {
                    latch.countDown();
                }
                return false;
            }
        };
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(dispatcher);
        try {
            //current thread waits here until countDown() is called (see a few lines above)
            latch.await();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }  
        KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(dispatcher);

    }
mKorbel
  • 109,525
  • 20
  • 134
  • 319
CodeGuy
  • 28,427
  • 76
  • 200
  • 317
  • This isn't how Swing works. All Swing events are dispatched in a single thread, if you block that thread for any reason, events can no longer be dispatched. Your best option is to use listener a trigger a method when the key you want is triggered – MadProgrammer Jan 02 '13 at 20:19

3 Answers3

4
  • never to use Keylistener (without real reason, e.g. to determine 3 or more keys are presses) for JTextComponets, use Document, DocumentListener, DocumentFilter

  • use KeyBinding for standard key, notice - have to check if isn't desired key (key_shortcut) implemented in the API

  • in this case to use DocumentFilter with Patern

simple example

import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

public class TextAreaTest extends JFrame {

    private static final long serialVersionUID = 1L;
    private JTextArea textArea;

    public TextAreaTest() {
        textArea = new JTextArea();
        textArea.setPreferredSize(new Dimension(60, 32));
        textArea.setOpaque(true);
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        ((AbstractDocument) textArea.getDocument()).setDocumentFilter(new DocumentFilter() {

            @Override
            public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
                //string = string.replaceAll("\\{", "\\{}");
                super.insertString(fb, offset, string, attr);
            }

            @Override
            public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
                if (Pattern.compile("\\p{Space}").matcher(text).find()) {
                    System.exit(0);
                }
                //text = text.replaceAll("\\{", "\\{}");
                super.replace(fb, offset, length, text, attrs);
            }
        });

        textArea.getDocument().addDocumentListener(new DocumentListener() {

            @Override
            public void changedUpdate(DocumentEvent e) {
                update(e);
            }

            @Override
            public void insertUpdate(DocumentEvent e) {
                update(e);
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                update(e);
            }

            private void update(DocumentEvent e) {
                List<String> lines = getLines(textArea);
                String lastLine = lines.get(lines.size() - 1);
                int tabbedTextWidth = Utilities.getTabbedTextWidth(new Segment(
                        lastLine.toCharArray(), 0, lastLine.length()), textArea.getFontMetrics(textArea.getFont()), 0, null, 0);
                int lineHeight = getLineHeight(textArea);
                if (lines.size() * lineHeight > textArea.getHeight() || tabbedTextWidth > textArea.getWidth()) {
                    System.out.println("Too big! Should refuse the update!");
                }
            }
        });
        getContentPane().add(textArea);
    }

    private static List<String> getLines(JTextArea textArea) {
        int lineHeight = getLineHeight(textArea);
        List<String> list = new ArrayList<String>();
        for (int num = 0;; num++) {
            int i = textArea.viewToModel(new Point(0, num * lineHeight));
            int j = textArea.viewToModel(new Point(0, (num + 1) * lineHeight));
            if (i == 0 && j == 0) {
                continue;
            }
            if (textArea.getDocument().getLength() == i && i == j) {
                break;
            }
            String s = removeTrailingNewLine(textArea.getText().substring(i, j));
            list.add(s);
            //System.out.println(i + " " + j + " = " + s);
        }
        return list;
    }

    private static int getLineHeight(JTextArea textArea) {
        return textArea.getFontMetrics(textArea.getFont()).getHeight();
    }

    private static String removeTrailingNewLine(String s) {
        if (s.endsWith("\n")) {
            return s.substring(0, s.length() - 1);
        } else {
            return s;
        }
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                TextAreaTest test = new TextAreaTest();
                test.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                test.pack();
                test.setVisible(true);
            }
        });
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
mKorbel
  • 109,525
  • 20
  • 134
  • 319
0

Have you looked into using a Key Event Listener? You can add it to your text field or area and check it for specific keys. I use it for things like enter or tab, so that when a user presses one, it initiates a difference action.

Java Key Listener

Would a Document Listener work for you? That checks every time a value in the text field changes.

Document Listener

Here is some sample code for what I did to a text field to take action when a certain key is pressed.

textField.addKeyListener(new java.awt.event.KeyAdapter() {
    public void keyPressed(final java.awt.event.KeyEvent evt) {
        textFieldKeyPressed(evt);
    }
});

private void textFieldKeyPressed(final java.awt.event.KeyEvent evt) {
    final int key = evt.getKeyCode();
    if (key == KeyEvent.VK_ENTER || key == KeyEvent.VK_TAB) {
        if (!textField.getText().equals("Updating")) {
            loadTable();
        }
    }
}
Logan
  • 2,369
  • 19
  • 20
  • Yes, but that does not premit me from only making the method return on SPACE. I'm very familiar with KeyListener, but I need to create that method that you see above... – CodeGuy Jan 01 '13 at 19:51
  • I guess I'm not sure what you have here. Do you have 1 frame or 1 main frame with a second window opening? – Logan Jan 01 '13 at 20:06
0

The correct solution is to create a custom JTextField as follows

public class MyTextField extends JTextField {

    private static final long serialVersionUID = 1833734152463502703L;

    public MyTextField() {

    }

    public void waitForKey(final int key) {
        final CountDownLatch latch = new CountDownLatch(1);
        KeyEventDispatcher dispatcher = new KeyEventDispatcher() {
            public boolean dispatchKeyEvent(KeyEvent e) {
                if (e.getID() == KeyEvent.KEY_PRESSED && e.getKeyCode() == key) {
                    latch.countDown();
                }
                return false;
            }
        };
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(dispatcher);
        try {
            //current thread waits here until countDown() is called (see a few lines above)
            latch.await();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }  
        KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(dispatcher);

    }

}

and then in the original method, just invoke waitForKey() on the MyTextField object

CodeGuy
  • 28,427
  • 76
  • 200
  • 317