I am having trouble redirecting input from the console to a GUI in Java.
I have received an assignment, to build a Java command based game. That game includes i.e. the Parser and Console classes. The Parser is used to recognize commands, and Console is a graphical representation of a console.
The Parser class are using System.in and Scanner() to read the commands.
The Console class are redirecting the input from a jTextArea object by using System.setIn().
The Parser works fine with Intellij console and Windows cmd, but the Parser does not receive the text line when I redirect the input. What am I doing wrong?
The code in a test version.
Just run the Console class to open the console window. In the window type a word and press Enter. If it works, you will see your word repeated twice in the window below your word. If it doesn't work, your word will only be repeated once below your word.
Console:
import javax.swing.*;
import javax.swing.text.DefaultHighlighter.DefaultHighlightPainter;
import javax.swing.text.Highlighter.HighlightPainter;
import java.awt.*;
import java.awt.event.*;
import java.io.ByteArrayInputStream;
import java.util.Scanner;
public class Console implements KeyListener, ActionListener {
final String title = "World of Plastic";
final int width = 1380;
final int height = 750;
final String font = "Consolas";
final int fontSize = 12;
final int fontStyle = Font.PLAIN;
final JTextArea jta;
final HighlightPainter painter = new DefaultHighlightPainter(Color.gray);
int initialCaretPosition = 0;
// key codes
final int BACKSPACE = 8;
final int ENTER = 10;
final int PG_UP = 33;
final int PG_DN = 34;
final int END = 35;
final int HOME = 36;
final int LEFT_ARROW = 37;
final int UP_ARROW = 38;
final int RIGHT_ARROW = 39;
final int DOWN_ARROW = 40;
final int A = 65;
final int H = 72;
public Console() {
JFrame jf = this.frame();
//*** JTextArea ***//
this.jta = this.textArea("", 1, 1, false);
this.jta.addKeyListener(this);
// remove all mouse listeners
for (MouseListener listener : this.jta.getMouseListeners()) {
this.jta.removeMouseListener(listener);
}
// remove all mouse motion listeners
for (MouseMotionListener listener : this.jta.getMouseMotionListeners()) {
this.jta.removeMouseMotionListener(listener);
}
// remove all mouse wheel listeners
for (MouseWheelListener listener : this.jta.getMouseWheelListeners()) {
this.jta.removeMouseWheelListener(listener);
}
//*** Scrollable JTextArea ***//
jf.add(this.scrolls(15, 15, this.width - 45, this.height - 70));
jf.setVisible(true);
System.setOut(new Interceptor(System.out, this));
}
public static void main(String[] args) {
Console cfjp = new Console();
//new Game().play();
// A simpel parser
while (true) {
System.out.println();
System.out.print("> ");
Scanner reader = new Scanner(System.in);
System.out.println("parser " + reader.nextLine());
}
}
public void actionPerformed(ActionEvent ae) {
System.out.println("actionPerformed");
}
public void keyTyped(KeyEvent ke) {
}
public void keyReleased(KeyEvent ke) {
}
public void keyPressed(KeyEvent ke) {
int keyCode = ke.getKeyCode();
if (keyCode == PG_UP || keyCode == PG_DN || keyCode == UP_ARROW || keyCode == DOWN_ARROW) {
ke.consume();
} else if (keyCode == A && ke.getModifiersEx() == InputEvent.CTRL_DOWN_MASK) {
//this.highlightText(this.getStartCaretPosition(), this.getEndCaretPosition());
ke.consume();
} else if (keyCode == HOME) {
this.setCaretPosition(this.getStartCaretPosition());
ke.consume();
} else if (keyCode == END) {
this.setCaretPosition(this.getEndCaretPosition());
ke.consume();
} else if (keyCode == RIGHT_ARROW || keyCode == LEFT_ARROW || keyCode == BACKSPACE ||
(keyCode == H && ke.getModifiersEx() == InputEvent.CTRL_DOWN_MASK)) {
int pos = this.getCaretPosition();
if (pos <= this.getStartCaretPosition() || pos >= this.getEndCaretPosition()) {
ke.consume();
}
} else if (keyCode == ENTER) {
int from = this.getStartCaretPosition();
int to = this.getEndCaretPosition();
this.setStartCaretPosition(to);
try {
String inputFromUser = this.jta.getText(from, to - from);
System.out.println("\ninputFromUser " + inputFromUser.trim());
ByteArrayInputStream bais = new ByteArrayInputStream((inputFromUser).getBytes());
System.setIn(bais);
//System.setIn(new ByteArrayInputStream("5".getBytes()));
//System.setIn(new ByteArrayInputStream(inputFromUser.getBytes(StandardCharsets.UTF_8)));
} catch (Exception ignored) {
}
} else if (this.getCaretPosition() < this.getStartCaretPosition()) {
this.setCaretPosition(this.getEndCaretPosition());
}
}
public int getCaretPosition() {
return this.jta.getCaretPosition();
}
public void setCaretPosition(int position) {
this.jta.setCaretPosition(position);
}
public int getStartCaretPosition() {
return this.initialCaretPosition;
}
public void setStartCaretPosition(int position) {
this.setCaretPosition(position);
this.initialCaretPosition = position;
}
public int getEndCaretPosition() {
return this.jta.getDocument().getLength();
}
public void print(String text) {
this.jta.append(text);
this.setStartCaretPosition(this.getEndCaretPosition());
}
public JFrame frame() {
JFrame tmpJF = new JFrame(this.title);
tmpJF.setSize(this.width, this.height);
tmpJF.setLocationRelativeTo(null);
tmpJF.setLayout(null);
tmpJF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
tmpJF.setResizable(false);
return tmpJF;
}
public JTextArea textArea(String text, int rows, int columns, boolean lineWrap) {
JTextArea tmpJTA = new JTextArea(text, rows, columns);
tmpJTA.setEditable(true);
tmpJTA.setLineWrap(lineWrap);
tmpJTA.setWrapStyleWord(true);
tmpJTA.setFont(new Font(this.font, this.fontStyle, this.fontSize));
tmpJTA.setForeground(Color.WHITE);
tmpJTA.setBackground(Color.black);
return tmpJTA;
}
public JScrollPane scrolls(int xpos, int ypos, int width, int height) {
JScrollPane tmpJSP = new JScrollPane(this.jta);
tmpJSP.setBounds(xpos, ypos, width, height);
tmpJSP.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
tmpJSP.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
return tmpJSP;
}
}
Interceptor:
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Arrays;
public class Interceptor extends PrintStream {
private final Console obj;
public Interceptor(OutputStream out, Console obj) {
super(out, true);
this.obj = obj;
}
#Override
public void print(boolean b) {
this.obj.print(String.valueOf(b));
}
#Override
public void print(char c) {
this.obj.print(String.valueOf(c));
}
#Override
public void print(char[] s) {
this.obj.print(Arrays.toString(s));
}
#Override
public void print(double d) {
this.obj.print(String.valueOf(d));
}
#Override
public void print(float f) {
this.obj.print(String.valueOf(f));
}
#Override
public void print(int i) {
this.obj.print(String.valueOf(i));
}
#Override
public void print(long l) {
this.obj.print(String.valueOf(l));
}
#Override
public void print(Object obj) {
if (obj != null) {
this.obj.print(obj.toString());
}
this.obj.print("null");
}
#Override
public void print(String s) {
this.obj.print(String.valueOf(s));
}
#Override
public void println() {
this.obj.print("\n");
}
#Override
public void println(boolean x) {
this.obj.print(x + "\n");
}
#Override
public void println(char x) {
this.obj.print(x + "\n");
}
#Override
public void println(char[] x) {
this.obj.print(Arrays.toString(x) + "\n");
}
#Override
public void println(double x) {
this.obj.print(x + "\n");
}
#Override
public void println(float x) {
this.obj.print(x + "\n");
}
#Override
public void println(int x) {
this.obj.print(x + "\n");
}
#Override
public void println(long x) {
this.obj.print(x + "\n");
}
#Override
public void println(Object x) {
if (x != null) {
this.obj.print(x.toString() + "\n");
}
this.obj.print("null\n");
}
#Override
public void println(String x) {
this.obj.print(x + "\n");
}
}
The problem is that with
ByteArrayInputStream bais = new ByteArrayInputStream((inputFromUser).getBytes());
System.setIn(bais);
you replace the System.in InputStream, but your code in main
Scanner reader = new Scanner(System.in);
System.out.println("parser " + reader.nextLine());
is still waiting on the old System.in and therefore never receives any characters.
One way to solve it is to create a connected pair of PipedInputStream / PipedOutputStream and use these:
static PipedOutpuStream out;
public static void main(String[] args) {
PipedInputStream in = new PipedInputStream();
out = new PipedOutputStream();
in.connect(out);
System.setIn(in);
// rest of your main
}
public void keyPressed(KeyEvent ke) {
// much code left out
try {
String inputFromUser = this.jta.getText(from, to - from);
System.out.println("\ninputFromUser " + inputFromUser.trim());
out.write((inputFromUser + "\n").getBytes());
out.flush();
} catch (Exception ignored) {
}
// much code left out
}
I made PipedOutputStream out into a static field because of laziness, you could also pass it as constructor argument into the Console constructor.
Related
I wanna make a programmable calculator, i got the basic GUI, and now i'm trying to set up the buttons, and the display. My display text will be "0" basically and if the user type in a number, that number should to be displayed. I tried to do it with KeyListener, but if i press a key it will display the key twice. Why?
textField.addKeyListener(new KeyListener(){
boolean newNumber = true;
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == e.VK_BACK_SPACE && textField.getText().length() == 1){
textField.setText("0");
newNumber = true;
}
if(textField.getText().equals("0") && newNumber){
textField.setText(KeyEvent.getKeyText(keyCode));
newNumber = false;
}
}
public void keyReleased(KeyEvent e) {
}
});
Before input:
After "1" input:
Here a simple solution:
If you use keyPressed, you have to do something in keyReleased and this become
complicated. keyTyped is a more simple way.
You can use e.consume() to prevent having the double digit inserted.
textField.addKeyListener(new KeyListener() {
int codeDelete = KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_DELETE);
int codeBackSpace = KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_BACK_SPACE);
#Override
public void keyTyped(KeyEvent e) {
char keyChar = e.getKeyChar();
if (textField.getText().length() == 0) {
textField.setText("0");
}
else if (textField.getText().equals("0") && keyChar != codeDelete && keyChar != codeBackSpace) {
textField.setText(String.valueOf(e.getKeyChar()));
e.consume();
}
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
});
For doing that, I derive PlainDocument like this:
import java.awt.EventQueue;
import java.util.regex.Pattern;
import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
public class DigitDocument extends PlainDocument {
private static final long serialVersionUID = 1L;
protected static final Pattern patternStartZero = Pattern.compile("^0.+");
protected final JTextField textField;
private final int limit;
private final Runnable runnableFormat;
public DigitDocument(JTextField textField, int limit) {
super();
this.textField = textField;
this.limit = limit;
runnableFormat = new Runnable() {
#Override
public void run() {
String text = textField.getText();
if (text.length() == 0) {
textField.setText("0");
}
else if (patternStartZero.matcher(text).matches()) {
textField.setText(text.replaceAll("^0+", ""));
}
}
};
}
#Override
public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException {
str = str.replaceAll("[^0-9]", "");
if (str.length() == 0)
return;
else if ((getLength() + str.length()) <= limit)
super.insertString(offset, str, attr);
EventQueue.invokeLater(runnableFormat);
}
#Override
public void remove(int offs, int len) throws BadLocationException {
if (!"0".equals(textField.getText()))
super.remove(offs, len);
EventQueue.invokeLater(runnableFormat);
}
}
The usage is:
textField.setDocument(new DigitDocument(textField, 10));
textField.setText("0");
In DigitDocument,
First arg is the JTextField himself.
Second arg (10) is the maximum input length,
You can enter only digit.
I have this code from java2s.com and I just modified it. I don't know if I need to use the runnable or the documentlistener so that the application will automatically highlight the word that was defined in the code. I don't have much knowledge about the two, I tried the runnable but I encountered errors. Can someone help me? Here's the code.
public class Sample {
public static void main(String[] args) {
JFrame f = new JFrame();
JTextPane textPane = new JTextPane();
String word = "";
Highlighter highlighter = new UnderlineHighlighter(null);
textPane.setHighlighter(highlighter);
textPane.setText("This is a test");
final WordSearcher searcher = new WordSearcher(textPane);
final UnderlineHighlighter uhp = new UnderlineHighlighter(Color.red);
String w = "i";
int offset = searcher.search(w);
if (offset == -1) {
return;
}
try {
textPane.scrollRectToVisible(textPane.modelToView(offset));
} catch (BadLocationException ex) {
}
textPane.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent evt) {
searcher.search(word);
}
#Override
public void removeUpdate(DocumentEvent evt) {
searcher.search(word);
}
#Override
public void changedUpdate(DocumentEvent evt) {
}
});
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//f.add(panel, "South");
f.add(new JScrollPane(textPane), "Center");
f.setSize(400, 400);
f.setVisible(true);
}
public static String word;
public static Highlighter highlighter = new UnderlineHighlighter(null);
}
class WordSearcher {
public WordSearcher(JTextComponent comp) {
this.comp = comp;
this.painter = new UnderlineHighlighter.UnderlineHighlightPainter(
Color.red);
}
public int search(String word) {
int firstOffset = -1;
Highlighter highlighter = comp.getHighlighter();
Highlighter.Highlight[] highlights = highlighter.getHighlights();
for (int i = 0; i < highlights.length; i++) {
Highlighter.Highlight h = highlights[i];
if (h.getPainter() instanceof
UnderlineHighlighter.UnderlineHighlightPainter) {
highlighter.removeHighlight(h);
}
}
if (word == null || word.equals("")) {
return -1;
}
String content = null;
try {
Document d = comp.getDocument();
content = d.getText(0, d.getLength()).toLowerCase();
} catch (BadLocationException e) {
// Cannot happen
return -1;
}
word = word.toLowerCase();
int lastIndex = 0;
int wordSize = word.length();
while ((lastIndex = content.indexOf(word, lastIndex)) != -1) {
int endIndex = lastIndex + wordSize;
try {
highlighter.addHighlight(lastIndex, endIndex, painter);
} catch (BadLocationException e) {
// Nothing to do
}
if (firstOffset == -1) {
firstOffset = lastIndex;
}
lastIndex = endIndex;
}
return firstOffset;
}
protected JTextComponent comp;
protected Highlighter.HighlightPainter painter;
}
class UnderlineHighlighter extends DefaultHighlighter {
public UnderlineHighlighter(Color c) {
painter = (c == null ? sharedPainter : new UnderlineHighlightPainter(c));
}
public Object addHighlight(int p0, int p1) throws BadLocationException {
return addHighlight(p0, p1, painter);
}
public void setDrawsLayeredHighlights(boolean newValue) {
// Illegal if false - we only support layered highlights
if (newValue == false) {
throw new IllegalArgumentException(
"UnderlineHighlighter only draws layered highlights");
}
super.setDrawsLayeredHighlights(true);
}
public static class UnderlineHighlightPainter extends
LayeredHighlighter.LayerPainter {
public UnderlineHighlightPainter(Color c) {
color = c;
}
public void paint(Graphics g, int offs0, int offs1, Shape bounds,
JTextComponent c) {
// Do nothing: this method will never be called
}
public Shape paintLayer(Graphics g, int offs0, int offs1, Shape bounds,
JTextComponent c, View view) {
g.setColor(color == null ? c.getSelectionColor() : color);
Rectangle alloc = null;
if (offs0 == view.getStartOffset() && offs1 == view.getEndOffset()) {
if (bounds instanceof Rectangle) {
alloc = (Rectangle) bounds;
} else {
alloc = bounds.getBounds();
}
} else {
try {
Shape shape = view.modelToView(offs0,
Position.Bias.Forward, offs1,
Position.Bias.Backward, bounds);
alloc = (shape instanceof Rectangle) ? (Rectangle) shape
: shape.getBounds();
} catch (BadLocationException e) {
return null;
}
}
FontMetrics fm = c.getFontMetrics(c.getFont());
int baseline = alloc.y + alloc.height - fm.getDescent() + 1;
g.drawLine(alloc.x, baseline, alloc.x + alloc.width, baseline);
g.drawLine(alloc.x, baseline + 1, alloc.x + alloc.width,
baseline + 1);
return alloc;
}
protected Color color; // The color for the underline
}
protected static final Highlighter.HighlightPainter sharedPainter = new
UnderlineHighlightPainter(
null);
protected Highlighter.HighlightPainter painter;
}
Maybe your code has some import errors? It runs fine with Java 1.8. In this situation it is Ok to use DocumentListener. Made some modifications in main class for finding text "test":
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.*;
import java.awt.*;
public class Sample {
public static void main(String[] args) {
JFrame f = new JFrame();
JTextPane textPane = new JTextPane();
String word = "test";
Highlighter highlighter = new UnderlineHighlighter(null);
textPane.setHighlighter(highlighter);
textPane.setText("This is a test");
final WordSearcher searcher = new WordSearcher(textPane);
final UnderlineHighlighter uhp = new UnderlineHighlighter(Color.red);
String w = "i";
int offset = searcher.search(w);
if (offset == -1) {
return;
}
try {
textPane.scrollRectToVisible(textPane.modelToView(offset));
} catch (BadLocationException ex) {
}
textPane.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent evt) {
searcher.search(word);
}
#Override
public void removeUpdate(DocumentEvent evt) {
searcher.search(word);
}
#Override
public void changedUpdate(DocumentEvent evt) {
}
});
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JScrollPane(textPane), "Center");
f.setSize(400, 400);
f.setVisible(true);
searcher.search(word);
}
public static String word;
public static Highlighter highlighter = new UnderlineHighlighter(null);
}
}
I have created a swt textbox and trying to do undo and redo functionality but when i press "ctrl+z" the listener itself is not working .so how can we do undo and redo operations.the code to implement undo and redo is as follows
private static class CTabItemControl extends Composite {
private static class UndoRedoStack<T> {
private Stack<T> undo;
private Stack<T> redo;
public UndoRedoStack() {
undo = new Stack<T>();
redo = new Stack<T>();
}
public void pushUndo(T delta) {
undo.add(delta);
}
public void pushRedo(T delta) {
redo.add(delta);
}
public T popUndo() {
T res = undo.pop();
return res;
}
public T popRedo() {
T res = redo.pop();
return res;
}
public T peekUndo() {
T res = undo.peek();
return res;
}
public void clearRedo() {
redo.clear();
}
public void clearUndo() {
undo.clear();
}
public boolean hasUndo() {
return !undo.isEmpty();
}
public boolean hasRedo() {
return !redo.isEmpty();
}
}
//private StyledText editor;
private UndoRedoStack<ExtendedModifyEvent> stack;
private boolean isUndo;
private boolean isRedo;
public CTabItemControl(Composite parentComposite,final CTabItem tabitem){
super(parentComposite, SWT.NONE);
setLayout(new GridLayout(1, false));
editor = new StyledText(this, SWT.MULTI | SWT.V_SCROLL);
editor.setLayoutData(new GridData(GridData.FILL_BOTH));
editor.setFont(new Font(Display.getDefault(),"Cambria", 10, SWT.NORMAL));
editor.addListener(SWT.KeyDown, new Listener(){
public void handleEvent(Event event) {
event.doit = true;
if(!tabitem.getText().contains("*"))
{
tabitem.setText('*'+tabitem.getText());
System.out.println("inserted *");
}
}
});
editor.addExtendedModifyListener(new ExtendedModifyListener(){
//editor.addKeyListener(this);
public void modifyText(ExtendedModifyEvent event) {
if (isUndo) {
stack.pushRedo(event);
} else { // is Redo or a normal user action
stack.pushUndo(event);
if (!isRedo) {
stack.clearRedo();
}
}
}
});
editor.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
// Listen to CTRL+Z for Undo, to CTRL+Y or CTRL+SHIFT+Z for Redo
boolean isCtrl = (e.stateMask & SWT.CTRL) > 0;
boolean isAlt = (e.stateMask & SWT.ALT) > 0;
if (isCtrl && !isAlt) {
boolean isShift = (e.stateMask & SWT.SHIFT) > 0;
if (!isShift && e.keyCode == 'z') {
{
System.out.println("call undo");
undo();
}
} else if (!isShift && e.keyCode == 'y' || isShift
&& e.keyCode == 'z') {
redo();
}
}
if(e.stateMask == SWT.CTRL && e.keyCode == 'a'){
editor.selectAll();
}
}
public void keyReleased(KeyEvent e) {
// ignore
}
});
//this.editor = editor;
stack = new UndoRedoStack<ExtendedModifyEvent>();
}
private void revertEvent(ExtendedModifyEvent event) {
System.out.println("calling revertevent");
editor.replaceTextRange(event.start, event.length, event.replacedText);
// (causes the modifyText() listener method to be called)
editor.setSelectionRange(event.start, event.replacedText.length());
}
private void undo() {
System.out.println("calling undo");
if (stack.hasUndo()) {
isUndo = true;
revertEvent(stack.popUndo());
isUndo = false;
}
}
private void redo() {
if (stack.hasRedo()) {
isRedo = true;
revertEvent(stack.popRedo());
isRedo = false;
}
}
public void clearUndoRedo() {
stack.clearUndo();
stack.clearRedo();
}
public boolean hasUndo() {
return stack.hasUndo();
}
public String peekUndo() {
return stack.peekUndo().toString();
}
}
When you press Ctrl + Z or Ctrl + Y, you get 2 key events, one for Ctrl and one for another key. So it is better to check for the character of the second event
e.character == 0x1a
for Ctr + Z, and
e.character == 0x19
for Ctrl + Y
I have a simple JComboBox filter code like this :
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class FilterComboBox extends JComboBox {
private List<String> array;
public FilterComboBox(List<String> array) {
super(array.toArray());
this.array = array;
this.setEditable(true);
final JTextField textfield = (JTextField) this.getEditor().getEditorComponent();
textfield.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent ke) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
comboFilter(textfield.getText());
}
});
}
});
}
public void comboFilter(String enteredText) {
List<String> filterArray= new ArrayList<String>();
for (int i = 0; i < array.size(); i++) {
if (array.get(i).toLowerCase().contains(enteredText.toLowerCase())) {
filterArray.add(array.get(i));
}
}
if (filterArray.size() > 0) {
this.setModel(new DefaultComboBoxModel(filterArray.toArray()));
this.setSelectedItem(enteredText);
this.showPopup();
}
else {
this.hidePopup();
}
}
/* Testing Codes */
public static List<String> populateArray() {
List<String> test = new ArrayList<String>();
test.add("");
test.add("Mountain Flight");
test.add("Mount Climbing");
test.add("Trekking");
test.add("Rafting");
test.add("Jungle Safari");
test.add("Bungie Jumping");
test.add("Para Gliding");
return test;
}
public static void makeUI() {
JFrame frame = new JFrame("Adventure in Nepal - Combo Filter Test");
FilterComboBox acb = new FilterComboBox(populateArray());
frame.getContentPane().add(acb);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args) throws Exception {
//UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
makeUI();
}
}
The performance of the combo filter is not so good but it is fine for few data set. My problem is - when I remove the comment UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); to change look and feel, the filter doesn't work. In WindowsLookAndFeel, the combo box only takes single character in it by replacing the previously entered character.
Can you please tell me whats going on? Manoj Shrestha's answer below helps in some way but , can you please provide some other suggestions to achieve combo box filter in Java?
Firstly you are creating new model everytime and then invoking show popup from code which leads to flickering etc. We can modify the model itself. Secondly you set the currently entered text as selected item which seems to have selectAll behavior as noted by others. I have modified the code as follows:
public void comboFilter(String enteredText) {
if (!this.isPopupVisible()) {
this.showPopup();
}
List<String> filterArray= new ArrayList<String>();
for (int i = 0; i < array.size(); i++) {
if (array.get(i).toLowerCase().contains(enteredText.toLowerCase())) {
filterArray.add(array.get(i));
}
}
if (filterArray.size() > 0) {
DefaultComboBoxModel model = (DefaultComboBoxModel) this.getModel();
model.removeAllElements();
for (String s: filterArray)
model.addElement(s);
JTextField textfield = (JTextField) this.getEditor().getEditorComponent();
textfield.setText(enteredText);
}
}
Hope it works for you.
very long answer, I think that exelent example about how different Look and Feel have got implemented methods in API and works
KeyListener isn't proper Listener for Swing JComponents, you really to have bothering with KeyBindings,
KeyListener is simple asynchronous,
JComboBox is Compound JComponent, then there is required override internal JComponents, all output from KeyListener must be wrapped into invokeLater(), notice I can create event from coumpond JComponents that twice invokeLater() doesn't returns expected output to the GUI, only Swing Timer with Swing Action can do that correctly, simple why to bothering wiht that example about wrong way,
code
import java.awt.Component;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.plaf.basic.BasicComboBoxRenderer;
public class ComboBoxHoverOver {
private JComboBox combo = new JComboBox();
public ComboBoxHoverOver() {
combo.setPrototypeDisplayValue("XXXXXXXXXXXXXXXXXXXXXX");
combo.setRenderer(new ComboToolTipRenderer(combo));
combo.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
//System.out.println(combo.getSelectedItem().toString());
}
});
combo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//System.out.println(combo.getSelectedItem().toString());
}
});
combo.addItem("");
combo.addItem("Long text 4");
combo.addItem("Long text 3");
combo.addItem("Long text 2");
combo.addItem("Long text 1");
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(combo);
f.pack();
f.setVisible(true);
}
private class ComboToolTipRenderer extends BasicComboBoxRenderer {
private static final long serialVersionUID = 1L;
private JComboBox combo;
private JList comboList;
ComboToolTipRenderer(JComboBox combo) {
this.combo = combo;
}
#Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
if (comboList == null) {
comboList = list;
KeyAdapter listener = new KeyAdapter() {
#Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_UP) {
int x = 5;
int y = comboList.indexToLocation(comboList.getSelectedIndex()).y;
System.out.println(comboList.getSelectedIndex());
}
}
};
combo.addKeyListener(listener);
combo.getEditor().getEditorComponent().addKeyListener(listener);
}
if (isSelected) {
//System.out.println(value.toString());
}
return this;
}
}
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
java.awt.EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ComboBoxHoverOver comboBoxHoverOver = new ComboBoxHoverOver();
}
});
}
}
JComboBox is Compound JComponent, then there is required override BasicComboBoxUI, please sorry I lazy to write and simulating too much longer code as code from first point
otherwise all effort from above two point are useless and contraproductive, nothing else, only DOT
please can someone to test follows code in *nix and apple OS X
from my Java6 WinXP compo (all important is hidden in the used methods, enless kudos for anonymous author from former Sun Microsystems)
Substance L&F
WindowsLookAndFeel L&F
Nimbus L&F
Metal L&F
from Java Classes
main
import java.awt.*;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;
import org.pushingpixels.substance.api.skin.SubstanceOfficeSilver2007LookAndFeel;
public class AutoCompleteTextField {
private static JFrame frame = new JFrame();
private ArrayList<String> listSomeString = new ArrayList<String>();
private Java2sAutoTextField someTextField = new Java2sAutoTextField(listSomeString);
private ArrayList<String> listSomeAnotherString = new ArrayList<String>();
private Java2sAutoComboBox someComboBox = new Java2sAutoComboBox(listSomeAnotherString);
public AutoCompleteTextField() {
listSomeString.add("-");
listSomeString.add("Snowboarding");
listSomeString.add("Rowing");
listSomeString.add("Knitting");
listSomeString.add("Speed reading");
listSomeString.add("Pool");
listSomeString.add("None of the above");
//
listSomeAnotherString.add("-");
listSomeAnotherString.add("XxxZxx Snowboarding");
listSomeAnotherString.add("AaaBbb Rowing");
listSomeAnotherString.add("CccDdd Knitting");
listSomeAnotherString.add("Eee Fff Speed reading");
listSomeAnotherString.add("Eee Fff Pool");
listSomeAnotherString.add("Eee Fff None of the above");
//
someTextField.setFont(new Font("Serif", Font.BOLD, 16));
someTextField.setForeground(Color.black);
someTextField.setBackground(Color.orange);
someTextField.setName("someTextField");
someTextField.setDataList(listSomeString);
//
someComboBox.setPrototypeDisplayValue("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
someComboBox.setFont(new Font("Serif", Font.BOLD, 16));
someComboBox.setForeground(Color.black);
someComboBox.setBackground(Color.YELLOW);
someComboBox.getEditor().selectAll();
someComboBox.getEditor().getEditorComponent().setBackground(Color.YELLOW);
((JTextField) someComboBox.getEditor().getEditorComponent()).setDisabledTextColor(Color.black);
someComboBox.setName("someComboBox");
someComboBox.setDataList(listSomeAnotherString);
//
frame.setLayout(new GridLayout(0, 1, 10, 10));
frame.add(someTextField);
frame.add(someComboBox);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocation(100, 100);
frame.pack();
frame.setVisible(true);
//
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
someTextField.setText("-");
someComboBox.getEditor().setItem(0);
someComboBox.getEditor().selectAll();
someTextField.grabFocus();
someTextField.requestFocus();
someTextField.setText(someTextField.getText());
someTextField.selectAll();
}
});
}
public static void main(String[] args) {
/*SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(new SubstanceOfficeSilver2007LookAndFeel());
SwingUtilities.updateComponentTreeUI(frame);
} catch (UnsupportedLookAndFeelException e) {
throw new RuntimeException(e);
}
}
});*/
/*try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
System.out.println(info.getName());
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (UnsupportedLookAndFeelException e) {
// handle exception
} catch (ClassNotFoundException e) {
// handle exception
} catch (InstantiationException e) {
// handle exception
} catch (IllegalAccessException e) {
// handle exception
}*/
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
AutoCompleteTextField aCTF = new AutoCompleteTextField();
}
});
}
}
AutoComboBox
import java.awt.event.ItemEvent;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.plaf.basic.BasicComboBoxEditor;
public class Java2sAutoComboBox extends JComboBox {
private static final long serialVersionUID = 1L;
private AutoTextFieldEditor autoTextFieldEditor;
private boolean isFired;
private class AutoTextFieldEditor extends BasicComboBoxEditor {
private Java2sAutoTextField getAutoTextFieldEditor() {
return (Java2sAutoTextField) editor;
}
AutoTextFieldEditor(java.util.List<String> list) {
editor = new Java2sAutoTextField(list, Java2sAutoComboBox.this);
}
}
public Java2sAutoComboBox(java.util.List<String> list) {
isFired = false;
autoTextFieldEditor = new AutoTextFieldEditor(list);
setEditable(true);
setModel(new DefaultComboBoxModel(list.toArray()) {
private static final long serialVersionUID = 1L;
#Override
protected void fireContentsChanged(Object obj, int i, int j) {
if (!isFired) {
super.fireContentsChanged(obj, i, j);
}
}
});
setEditor(autoTextFieldEditor);
}
public boolean isCaseSensitive() {
return autoTextFieldEditor.getAutoTextFieldEditor().isCaseSensitive();
}
public void setCaseSensitive(boolean flag) {
autoTextFieldEditor.getAutoTextFieldEditor().setCaseSensitive(flag);
}
public boolean isStrict() {
return autoTextFieldEditor.getAutoTextFieldEditor().isStrict();
}
public void setStrict(boolean flag) {
autoTextFieldEditor.getAutoTextFieldEditor().setStrict(flag);
}
public java.util.List<String> getDataList() {
return autoTextFieldEditor.getAutoTextFieldEditor().getDataList();
}
public void setDataList(java.util.List<String> list) {
autoTextFieldEditor.getAutoTextFieldEditor().setDataList(list);
setModel(new DefaultComboBoxModel(list.toArray()));
}
void setSelectedValue(Object obj) {
if (isFired) {
return;
} else {
isFired = true;
setSelectedItem(obj);
fireItemStateChanged(new ItemEvent(this, 701, selectedItemReminder, 1));
isFired = false;
return;
}
}
#Override
protected void fireActionEvent() {
if (!isFired) {
super.fireActionEvent();
}
}
}
AutoTextField
import java.util.List;
import javax.swing.JTextField;
import javax.swing.text.*;
public class Java2sAutoTextField extends JTextField {
private static final long serialVersionUID = 1L;
private List<String> dataList;
private boolean isCaseSensitive;
private boolean isStrict;
private Java2sAutoComboBox autoComboBox;
public class AutoDocument extends PlainDocument {
private static final long serialVersionUID = 1L;
#Override
public void replace(int i, int j, String s, AttributeSet attributeset)
throws BadLocationException {
super.remove(i, j);
insertString(i, s, attributeset);
}
#Override
public void insertString(int i, String s, AttributeSet attributeset)
throws BadLocationException {
if (s == null || "".equals(s)) {
return;
}
String s1 = getText(0, i);
String s2 = getMatch(s1 + s);
int j = (i + s.length()) - 1;
if (isStrict && s2 == null) {
s2 = getMatch(s1);
j--;
} else if (!isStrict && s2 == null) {
super.insertString(i, s, attributeset);
return;
}
if (autoComboBox != null && s2 != null) {
autoComboBox.setSelectedValue(s2);
}
super.remove(0, getLength());
super.insertString(0, s2, attributeset);
setSelectionStart(j + 1);
setSelectionEnd(getLength());
}
#Override
public void remove(int i, int j) throws BadLocationException {
int k = getSelectionStart();
if (k > 0) {
k--;
}
String s = getMatch(getText(0, k));
if (!isStrict && s == null) {
super.remove(i, j);
} else {
super.remove(0, getLength());
super.insertString(0, s, null);
}
if (autoComboBox != null && s != null) {
autoComboBox.setSelectedValue(s);
}
try {
setSelectionStart(k);
setSelectionEnd(getLength());
} catch (Exception exception) {
}
}
}
public Java2sAutoTextField(List<String> list) {
isCaseSensitive = false;
isStrict = true;
autoComboBox = null;
if (list == null) {
throw new IllegalArgumentException("values can not be null");
} else {
dataList = list;
init();
return;
}
}
Java2sAutoTextField(List<String> list, Java2sAutoComboBox b) {
isCaseSensitive = false;
isStrict = true;
autoComboBox = null;
if (list == null) {
throw new IllegalArgumentException("values can not be null");
} else {
dataList = list;
autoComboBox = b;
init();
return;
}
}
private void init() {
setDocument(new AutoDocument());
if (isStrict && dataList.size() > 0) {
setText(dataList.get(0).toString());
}
}
private String getMatch(String s) {
for (int i = 0; i < dataList.size(); i++) {
String s1 = dataList.get(i).toString();
if (s1 != null) {
if (!isCaseSensitive
&& s1.toLowerCase().startsWith(s.toLowerCase())) {
return s1;
}
if (isCaseSensitive && s1.startsWith(s)) {
return s1;
}
}
}
return null;
}
#Override
public void replaceSelection(String s) {
AutoDocument _lb = (AutoDocument) getDocument();
if (_lb != null) {
try {
int i = Math.min(getCaret().getDot(), getCaret().getMark());
int j = Math.max(getCaret().getDot(), getCaret().getMark());
_lb.replace(i, j - i, s, null);
} catch (Exception exception) {
}
}
}
public boolean isCaseSensitive() {
return isCaseSensitive;
}
public void setCaseSensitive(boolean flag) {
isCaseSensitive = flag;
}
public boolean isStrict() {
return isStrict;
}
public void setStrict(boolean flag) {
isStrict = flag;
}
public List<String> getDataList() {
return dataList;
}
public void setDataList(List<String> list) {
if (list == null) {
throw new IllegalArgumentException("values can not be null");
} else {
dataList = list;
return;
}
}
}
EDIT
output from Win7 64b / Java7
Metal L&F
Windows L&F (funny empty white space near Button in JComboBox)
Nimbus L&F
feel free for edit(s)
This component is called autocomplete and is included in a so called swing extensions porject.
Just have a look at: http://swingx.java.net/
There is a webstart: http://swinglabs-demos.java.net/demos/swingxset6/swingxset.jnlp
Autocomplete is the Menu to select. Have fun and less error prone code :)
obviously the glitch is in the textfield component used. What happens is, when windows look and feel is used , the text in this component is selected just as using the line "textfield.selectAll();", and hence when you type anything else the selected text is cleared form the textfield component. So in the below code the caret position of this component is adjusted. This is how it works, first store the current caret position of text field in a variable "currentCaretPosition", then move the caret position to the beginning in the text in the component. After filtering restoring the caret position back to the "currentCaretPosition" variable value. I hope it works as you want it to.
The refined code is given below:-
/****Beginning of code****/
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class FilterComboBox extends JComboBox {
private List<String> array;
private int currentCaretPosition=0;
public FilterComboBox(List<String> array) {
super(array.toArray());
this.array = array;
this.setEditable(true);
final JTextField textfield = (JTextField) this.getEditor().getEditorComponent();
textfield.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent ke) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
currentCaretPosition=textfield.getCaretPosition();
if(textfield.getSelectedText()==null)
{
textfield.setCaretPosition(0);
comboFilter(textfield.getText());
textfield.setCaretPosition(currentCaretPosition);
}
}
});
}
});
}
public void comboFilter(String enteredText) {
List<String> filterArray= new ArrayList<String>();
for (int i = 0; i < array.size(); i++) {
if (array.get(i).toLowerCase().contains(enteredText.toLowerCase())) {
filterArray.add(array.get(i));
}
}
if (filterArray.size() > 0) {
this.setModel(new DefaultComboBoxModel(filterArray.toArray()));
this.setSelectedItem(enteredText);
this.showPopup();
}
else {
this.hidePopup();
}
}
/* Testing Codes */
public static List<String> populateArray() {
List<String> test = new ArrayList<String>();
test.add("");
test.add("Mountain Flight");
test.add("Mount Climbing");
test.add("Trekking");
test.add("Rafting");
test.add("Jungle Safari");
test.add("Bungie Jumping");
test.add("Para Gliding");
return test;
}
public static void makeUI() {
JFrame frame = new JFrame("Adventure in Nepal - Combo Filter Test");
FilterComboBox acb = new FilterComboBox(populateArray());
frame.getContentPane().add(acb);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
makeUI();
}
}
/******* End of code**********/
It looks like, as you mentioned, when user inputs any texts in combobox, the Windows Look & Feel selects (highlights) the entered text. So, when you press another key, it replaces the previous one. So, the solution is not to highlight the entered texts. You can achieve this by adding any one of the following statements in your keylistener.
textfield.setCaretPosition(textfield.getText().length());
OR
textfield.setSelectionStart(textfield.getText().length());
So, your keylistener should look like this :
final JTextField textfield = (JTextField) this.getEditor().getEditorComponent();
textfield.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent ke) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
comboFilter(textfield.getText());
textfield.setCaretPosition(textfield.getText().length());
}
});
}
});
All I want to do, If I'm pressing '{' this key in JtextArea.automatically '}' this will be print also.
if(evt.KEY_PRESSED == '{')
System.out.print("}");
is this is okay??
for listening changes into JTextComponent is there DocumentListener, if you have to need control over inputed Char, sings, whitespace chars or word(s) you have to implements DocumentFilter
notice for Chars reservated by programing language(s) you have to use double escapes,
\\( instead of (
or
\\{ instead of {
otherwise you get
Exception in thread "AWT-EventQueue-0" java.util.regex.PatternSyntaxException:
Illegal repetition
for example
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
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 {
text = text.replaceAll("\\{", "\\{}");
//TODO must do something here
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);
}
});
}
}
You need KeyBinding
http://tips4java.wordpress.com/2008/10/10/key-bindings/
and
http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html
I think this is what you are looking for
if(evt.getID() == evt.KEY_PRESSED) {
if(evt.getKeyChar() == '{') {
System.out.print("}");
}
}
Tested and works