I'm using JavaFX and Scene Builder and I have a form with textfields. Three of these textfields are parsed from strings to doubles.
I want them to be school marks so they should only be allowed to be between 1.0 and 6.0. The user should not be allowed to write something like "2.34.4" but something like "5.5" or "2.9" would be ok.
Validation for the parsed fields:
public void validate(KeyEvent event) {
String content = event.getCharacter();
if ("123456.".contains(content)) {
// No numbers smaller than 1.0 or bigger than 6.0 - How?
} else {
event.consume();
}
}
How can I test if the user inputs a correct value?
I already searched on Stackoverflow and on Google but I didn't find a satisfying solution.
textField.focusedProperty().addListener((arg0, oldValue, newValue) -> {
if (!newValue) { //when focus lost
if(!textField.getText().matches("[1-5]\\.[0-9]|6\\.0")){
//when it not matches the pattern (1.0 - 6.0)
//set the textField empty
textField.setText("");
}
}
});
you could also change the pattern to [1-5](\.[0-9]){0,1}|6(.0){0,1} then 1,2,3,4,5,6would also be ok (not only 1.0,2.0,...)
update
Here is a small test application with the values 1(.00) to 6(.00) allowed:
public class JavaFxSample extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Enter number and hit the button");
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
Label label1To6 = new Label("1.0-6.0:");
grid.add(label1To6, 0, 1);
TextField textField1To6 = new TextField();
textField1To6.focusedProperty().addListener((arg0, oldValue, newValue) -> {
if (!newValue) { // when focus lost
if (!textField1To6.getText().matches("[1-5](\\.[0-9]{1,2}){0,1}|6(\\.0{1,2}){0,1}")) {
// when it not matches the pattern (1.0 - 6.0)
// set the textField empty
textField1To6.setText("");
}
}
});
grid.add(textField1To6, 1, 1);
grid.add(new Button("Hit me!"), 2, 1);
Scene scene = new Scene(grid, 300, 275);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I would not advise you to use KeyEvent for that.
You should use a more classical way such as validated the user input when the user finish to fill the text field or click on a save button.
/**
* Called this when the user clicks on the save button or finish to fill the text field.
*/
private void handleSave() {
// If the inputs are valid we save the data
if(isInputValid()){
note=(DOUBLE.parseDouble(textField.getText()));
}else // do something such as notify the user and empty the field
}
/**
* Validates the user input in the text fields.
*
* #return true if the input is valid
*/
private boolean isInputValid() {
Boolean b= false;
if (!(textField.getText() == null || textFiled.getText().length() == 0)) {
try {
// Do all the validation you need here such as
Double d = Double.parseInt(textFiled.getText());
if ( 1.0<d<6.0){
b=true;
}
} catch (NumberFormatException e) {
}
return b;
}
You can prevent illegal input using TextFormatter:
final Pattern pattern = Pattern.compile("(6\\.0)|([1-5]\\.[0-9])");
textField.setTextFormatter(new TextFormatter<>(new DoubleStringConverter(), 0.0, change -> {
final Matcher matcher = pattern.matcher(change.getControlNewText());
return (matcher.matches() || matcher.hitEnd()) ? change : null;
}));
In case you can use a third party library:
Similar question has been aswered here: Form validator message
.
For your case, you would choose a RegexValidator to check the textfield input, and pass the regex that you arrived to from previous answers:
JFXTextField validationField = new JFXTextField();
validationField.setPromptText("decimal between 1.0 and 6.0");
RegexValidator validator = new RegexValidator();
validator.setRegexPattern("[1-5](\\.[0-9]{1,2}){0,1}|6(\\.0{1,2}){0,1}");
validator.setMessage("Please enter proper value");
validationField.getValidators().add(validator);
validationField.focusedProperty().addListener((observable, oldValue, newValue) -> {
if(!newValue)
validationField.validate();
});
You can make a custom TextField that does input validation if you want.
import java.awt.Toolkit;
import javafx.event.EventHandler;
import javafx.scene.control.TextField;
import javafx.scene.control.TextInputControl;
import javafx.scene.input.KeyEvent;
/**
* A text field that limits the user to certain number of characters and
* prevents the user from typing certain characters
*
*
*/
public class CustomTextField extends TextField
{
/**
* The maximum number of characters this text field will allow
* */
private int maxNumOfCharacters;
/**
* A regular expression of characters that this text field does not allow
* */
private String unallowedCharactersRegEx;
/*
* If no max number of characters is specified the default value is set
* */
private static final int DEFAULT_MAX_NUM_OF_CHARACTERS = 1000;
public CustomTextField()
{
maxNumOfCharacters = DEFAULT_MAX_NUM_OF_CHARACTERS;
this.setOnKeyTyped(new EventHandler<KeyEvent>() {
public void handle(KeyEvent event)
{
// get the typed character
String characterString = event.getCharacter();
char c = characterString.charAt(0);
// if it is a control character or it is undefined, ignore it
if (Character.isISOControl(c) || characterString.contentEquals(KeyEvent.CHAR_UNDEFINED))
return;
// get the text field/area that triggered this key event and its text
TextInputControl source = (TextInputControl) event.getSource();
String text = source.getText();
// If the text exceeds its max length or if a character that matches
// notAllowedCharactersRegEx is typed
if (text.length() > maxNumOfCharacters
|| (unallowedCharactersRegEx != null && characterString.matches(unallowedCharactersRegEx)))
{
// remove the last character
source.deletePreviousChar();
// make a beep sound effect
Toolkit.getDefaultToolkit().beep();
}
}
});
}
public int getMaxNumOfCharacters()
{
return maxNumOfCharacters;
}
public void setMaxNumOfCharacters(int maxNumOfCharacters)
{
this.maxNumOfCharacters = maxNumOfCharacters;
}
public String getUnallowedCharactersRegEx()
{
return unallowedCharactersRegEx;
}
public void setUnallowedCharactersRegEx(String notAllowedRegEx)
{
this.unallowedCharactersRegEx = notAllowedRegEx;
}
}
Related
I work with Vaadin. I have a text field and a button. My button is initially disabled. When my text field is filled with valid data my button must activate. I can not activate my button. Could you help me ? Thank you
public static DynTextField createFromElement(Element elt, DynForm form) {
if (elt.getNodeName().equals("param") && elt.getAttribute("type").equals("TEXT")) {
DynTextField dtf = new DynTextField();
dtf.setForm(form);
if (elt.hasAttribute("texte"))
dtf.setCaption(elt.getAttribute("texte"));
dtf.nom = elt.getAttribute("nom");
if (elt.hasAttribute("FORMAT"))
dtf.setFormat(elt.getAttribute("FORMAT"));
dtf.setDescription(elt.getAttribute("description"));
dtf.setStyleName("param" + (elt.hasAttribute("class") ? elt.getAttribute("class") : ""));
return dtf;
} else
return null;
}
private void setFormat(String attribute) {
binder = new Binder<>();
binder.forField(this).withValidator(new RegexpValidator("Saisie obligatoire !!", attribute)).asRequired("Format Erroné").bind(No.getter(), No.setter());
//new Binder<>().forField(this).withValidator(new RegexpValidator(attribute, "Format Erroné")).asRequired();
}
// convenience empty getter and setter implementation for better readability
public static class No {
public static <SOURCE, TARGET> ValueProvider<SOURCE, TARGET> getter() {
return source -> null;
}
public static <BEAN, FIELDVALUE> Setter<BEAN, FIELDVALUE> setter() {
return (bean, fieldValue) -> {
//no op
};
}
}
The program that creates my button. This is where I would like to make my button active.
public DynButton(DynForm form, String as400PGMName, String[] parameterList) {
super(VaadinIcons.CHECK);
this.as400PGMName = as400PGMName;
if (parameterList.length == 1 && parameterList[0].equals(""))
this.parameterList = new String[] {};
else
this.parameterList = parameterList;
this.form = form;
addClickListener(event -> {
fireClickEvent(event);
});
addClickListener(this);
impl = new DynComponentImpl();
//boutton initially disable
this.setEnabled(isActif());
}
You can do it with a listener on either the text field or the binder
textField.addValueChangeListener(e ->
myButton.setEnabled(!e.getValue().equals("")));
or
binder.addStatusChangeListener(e ->
myButton.setEnabled(!e.hasValidationErrors()));
I am developing a system with Java (using NetBeans) and, to make it more "professional", I've added some cool functions, such as Placeholders (Yes, I know, it's from HTML).
For ALL the JTextFields I have, I've used the following code to generate their placeholders (The name of the JTextField in this example is "tfUser") :
private void tfUserFocusGained(java.awt.event.FocusEvent evt) {
if (tfUser.getText().equals("Your User Name...")) {
tfUser.setText("");
tfUser.setForeground(Color.BLACK);
}
}
private void tfUserFocusLost(java.awt.event.FocusEvent evt) {
if (tfUser.getText().equals("")) {
tfUser.setText("Your User Name...");
tfUser.setForeground(Color.GRAY);
}
}
It's a "focus match":
The Text Field has initially the text "Your User Name...", with a "GRAY" foreground. Every time this text field gains the focus, it verifies its text: if the text.equals("Your User Name..."), its text is set to "" (An empty String) and the Foreground is set to BLACK (default). On the other hand, if the text.equals("Anything else..."), it means that the user has probably inserted the user name, so, do not do anything with this code.
Every time the text field loses the focus, it verifies its text: if the text.equals("") (An empty String again), its text is set back to "Your User Name..." and the Foreground is set to GRAY. And again, if the text.equals("Anything else..."), it means that the user has probably inserted the user name, so, do not do anything with this code.
This code is working perfectly with the JTextFields But, when I do the same with JPasswordFields, I get the following result:
****************
(It should be "Your Password...")
Can anyone help me to add a placeholder to this JPasswordField? Thanks in advance.
What i did for my login code was add a checkbox that "shows password" and in the jframe i added this statement:
//Setting checkbox selected to true so the word "password" is seen when program runs
passCheckBox.setSelected(true);
if(passCheckBox.isSelected())
{
PasswordField.setEchoChar((char)0);
}`
Code for password checkbox:
if(passCheckBox.isSelected())
{
PasswordField.setEchoChar((char)0);
}else{
PasswordField.setEchoChar('*');
}
Code for Password Field Focus Gained:
private void PasswordFieldFocusGained(java.awt.event.FocusEvent evt) {
passCheckBox.setSelected(false);
PasswordField.setEchoChar('*');
String password = String.valueOf(PasswordField.getPassword());
if(password.toLowerCase().equals("password"))
{
PasswordField.setText("");
PasswordField.setForeground(Color.black);
}
}
Code for Password Field focus lost:
private void PasswordFieldFocusLost(java.awt.event.FocusEvent evt) {
String password = String.valueOf(PasswordField.getPassword());
if(password.toLowerCase().equals("password") || password.toLowerCase().equals("") )
{
PasswordField.setText("Password");
PasswordField.setEchoChar((char)0);
PasswordField.setForeground(new Color(153, 153, 153));
}
}
I kinda did half of this on my own and took some bits from videos i hope this helps :>
Here is my method for getting the placeholders
private void getPlaceholders(JTextField text) {
String temp = text.getText();
text.addFocusListener(new FocusListener() {
#Override
public void focusGained(FocusEvent e) {
text.setText("");
text.setForeground(Color.BLACK);
}
#Override
public void focusLost(FocusEvent e) {
if (text.getText().isEmpty()) {
text.setForeground(Color.GRAY);
text.setText(temp);
}
}
});
}
Here is how i implemented it.
// Password Field
passwordField = new JPasswordField();
passwordField.setBounds(48, 340, 288, 32);
roundedPanel.add(passwordField);
passwordField.setText("Password");
passwordField.setEchoChar((char) 0);
getPlaceholders(passwordField);
what allows you to see placeholders in passwordFields is
passwordField.setEchoChar((char) 0);
you can take it out to use in regular text fields
I want the message box to appear immediately after the user changes the value in the textfield. Currently, I need to hit the enter key to get the message box to pop out. Is there anything wrong with my code?
textField.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent e) {
if (Integer.parseInt(textField.getText())<=0){
JOptionPane.showMessageDialog(null,
"Error: Please enter number bigger than 0", "Error Message",
JOptionPane.ERROR_MESSAGE);
}
}
}
Any help would be appreciated!
Add a listener to the underlying Document, which is automatically created for you.
// Listen for changes in the text
textField.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
warn();
}
public void removeUpdate(DocumentEvent e) {
warn();
}
public void insertUpdate(DocumentEvent e) {
warn();
}
public void warn() {
if (Integer.parseInt(textField.getText())<=0){
JOptionPane.showMessageDialog(null,
"Error: Please enter number bigger than 0", "Error Message",
JOptionPane.ERROR_MESSAGE);
}
}
});
The usual answer to this is "use a DocumentListener". However, I always find that interface cumbersome. Truthfully the interface is over-engineered. It has three methods, for insertion, removal, and replacement of text, when it only needs one method: replacement. (An insertion can be viewed as a replacement of no text with some text, and a removal can be viewed as a replacement of some text with no text.)
Usually all you want is to know is when the text in the box has changed, so a typical DocumentListener implementation has the three methods calling one method.
Therefore I made the following utility method, which lets you use a simpler ChangeListener rather than a DocumentListener. (It uses Java 8's lambda syntax, but you can adapt it for old Java if needed.)
/**
* Installs a listener to receive notification when the text of any
* {#code JTextComponent} is changed. Internally, it installs a
* {#link DocumentListener} on the text component's {#link Document},
* and a {#link PropertyChangeListener} on the text component to detect
* if the {#code Document} itself is replaced.
*
* #param text any text component, such as a {#link JTextField}
* or {#link JTextArea}
* #param changeListener a listener to receieve {#link ChangeEvent}s
* when the text is changed; the source object for the events
* will be the text component
* #throws NullPointerException if either parameter is null
*/
public static void addChangeListener(JTextComponent text, ChangeListener changeListener) {
Objects.requireNonNull(text);
Objects.requireNonNull(changeListener);
DocumentListener dl = new DocumentListener() {
private int lastChange = 0, lastNotifiedChange = 0;
#Override
public void insertUpdate(DocumentEvent e) {
changedUpdate(e);
}
#Override
public void removeUpdate(DocumentEvent e) {
changedUpdate(e);
}
#Override
public void changedUpdate(DocumentEvent e) {
lastChange++;
SwingUtilities.invokeLater(() -> {
if (lastNotifiedChange != lastChange) {
lastNotifiedChange = lastChange;
changeListener.stateChanged(new ChangeEvent(text));
}
});
}
};
text.addPropertyChangeListener("document", (PropertyChangeEvent e) -> {
Document d1 = (Document)e.getOldValue();
Document d2 = (Document)e.getNewValue();
if (d1 != null) d1.removeDocumentListener(dl);
if (d2 != null) d2.addDocumentListener(dl);
dl.changedUpdate(null);
});
Document d = text.getDocument();
if (d != null) d.addDocumentListener(dl);
}
Unlike with adding a listener directly to the document, this handles the (uncommon) case that you install a new document object on a text component. Additionally, it works around the problem mentioned in Jean-Marc Astesana's answer, where the document sometimes fires more events than it needs to.
Anyway, this method lets you replace annoying code which looks like this:
someTextBox.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent e) {
doSomething();
}
#Override
public void removeUpdate(DocumentEvent e) {
doSomething();
}
#Override
public void changedUpdate(DocumentEvent e) {
doSomething();
}
});
With:
addChangeListener(someTextBox, e -> doSomething());
Code released to public domain. Have fun!
Just create an interface that extends DocumentListener and implements all DocumentListener methods:
#FunctionalInterface
public interface SimpleDocumentListener extends DocumentListener {
void update(DocumentEvent e);
#Override
default void insertUpdate(DocumentEvent e) {
update(e);
}
#Override
default void removeUpdate(DocumentEvent e) {
update(e);
}
#Override
default void changedUpdate(DocumentEvent e) {
update(e);
}
}
and then:
jTextField.getDocument().addDocumentListener(new SimpleDocumentListener() {
#Override
public void update(DocumentEvent e) {
// Your code here
}
});
or you can even use lambda expression:
jTextField.getDocument().addDocumentListener((SimpleDocumentListener) e -> {
// Your code here
});
Be aware that when the user modify the field, the DocumentListener can, sometime, receive two events. For instance if the user selects the whole field content, then press a key, you'll receive a removeUpdate (all the content is remove) and an insertUpdate.
In your case, I don't think it is a problem but, generally speaking, it is.
Unfortunately, it seems there's no way to track the content of the textField without subclassing JTextField.
Here is the code of a class that provide a "text" property :
package net.yapbam.gui.widget;
import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
/** A JTextField with a property that maps its text.
* <br>I've found no way to track efficiently the modifications of the text of a JTextField ... so I developed this widget.
* <br>DocumentListeners are intended to do it, unfortunately, when a text is replace in a field, the listener receive two events:<ol>
* <li>One when the replaced text is removed.</li>
* <li>One when the replacing text is inserted</li>
* </ul>
* The first event is ... simply absolutely misleading, it corresponds to a value that the text never had.
* <br>Anoter problem with DocumentListener is that you can't modify the text into it (it throws IllegalStateException).
* <br><br>Another way was to use KeyListeners ... but some key events are throw a long time (probably the key auto-repeat interval)
* after the key was released. And others events (for example a click on an OK button) may occurs before the listener is informed of the change.
* <br><br>This widget guarantees that no "ghost" property change is thrown !
* #author Jean-Marc Astesana
* <BR>License : GPL v3
*/
public class CoolJTextField extends JTextField {
private static final long serialVersionUID = 1L;
public static final String TEXT_PROPERTY = "text";
public CoolJTextField() {
this(0);
}
public CoolJTextField(int nbColumns) {
super("", nbColumns);
this.setDocument(new MyDocument());
}
#SuppressWarnings("serial")
private class MyDocument extends PlainDocument {
private boolean ignoreEvents = false;
#Override
public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
String oldValue = CoolJTextField.this.getText();
this.ignoreEvents = true;
super.replace(offset, length, text, attrs);
this.ignoreEvents = false;
String newValue = CoolJTextField.this.getText();
if (!oldValue.equals(newValue)) CoolJTextField.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue);
}
#Override
public void remove(int offs, int len) throws BadLocationException {
String oldValue = CoolJTextField.this.getText();
super.remove(offs, len);
String newValue = CoolJTextField.this.getText();
if (!ignoreEvents && !oldValue.equals(newValue)) CoolJTextField.this.firePropertyChange(TEXT_PROPERTY, oldValue, newValue);
}
}
I know this relates to a really old problem, however, it caused me some problems too. As kleopatra responded in a comment above, I solved the problem with a JFormattedTextField. However, the solution requires a bit more work, but is neater.
The JFormattedTextField doesn't by default trigger a property change after every text changes in the field. The default constructor of JFormattedTextField does not create a formatter.
However, to do what the OP suggested, you need to use a formatter which will invoke the commitEdit() method after each valid edit of the field. The commitEdit() method is what triggers the property change from what I can see and without the formatter, this is triggered by default on a focus change or when the enter key is pressed.
See http://docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html#value for more details.
Create a default formatter (DefaultFormatter) object to be passed to the JFormattedTextField either via its constructor or a setter method. One method of the default formatter is setCommitsOnValidEdit(boolean commit), which sets the formatter to trigger the commitEdit() method every time the text is changed. This can then be picked up using a PropertyChangeListener and the propertyChange() method.
An elegant way is to add the listener to the caret position, because it changes every time something is typed/deleted, then just compare old value with current one.
String oldVal = ""; // empty string or default value
JTextField tf = new JTextField(oldVal);
tf.addCaretListener(e -> {
String currentVal = tf.getText();
if(!currentVal.equals(oldVal)) {
oldVal = currentVal;
System.out.println("Change"); // do something
}
});
(This event is also being triggered every time a user just clicks into a TextField).
textBoxName.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent e) {
onChange();
}
#Override
public void removeUpdate(DocumentEvent e) {
onChange();
}
#Override
public void changedUpdate(DocumentEvent e) {
onChange();
}
});
But I would not just parse anything the user (maybe on accident) touches on his keyboard into an Integer. You should catch any Exceptions thrown and make sure the JTextField is not empty.
If we use runnable method SwingUtilities.invokeLater() while using Document listener application is getting stuck sometimes and taking time to update the result(As per my experiment). Instead of that we can also use KeyReleased event for text field change listener as mentioned here.
usernameTextField.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
JTextField textField = (JTextField) e.getSource();
String text = textField.getText();
textField.setText(text.toUpperCase());
}
});
it was the update version of Codemwnci. his code is quite fine and works great except the error message. To avoid error you must change the condition statement.
// Listen for changes in the text
textField.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
warn();
}
public void removeUpdate(DocumentEvent e) {
warn();
}
public void insertUpdate(DocumentEvent e) {
warn();
}
public void warn() {
if (textField.getText().length()>0){
JOptionPane.showMessageDialog(null,
"Error: Please enter number bigger than 0", "Error Massage",
JOptionPane.ERROR_MESSAGE);
}
}
});
You can use even "MouseExited" to control.
example:
private void jtSoMauMouseExited(java.awt.event.MouseEvent evt) {
// TODO add your handling code here:
try {
if (Integer.parseInt(jtSoMau.getText()) > 1) {
//auto update field
SoMau = Integer.parseInt(jtSoMau.getText());
int result = SoMau / 5;
jtSoBlockQuan.setText(String.valueOf(result));
}
} catch (Exception e) {
}
}
Use a KeyListener (which triggers on any key) rather than the ActionListener (which triggers on enter)
DocumentFilter ? It gives you the ability to manipulate.
[ http://www.java2s.com/Tutorial/Java/0240__Swing/FormatJTextFieldstexttouppercase.htm ]
Sorry. J am using Jython (Python in Java) - but easy to understand
# python style
# upper chars [ text.upper() ]
class myComboBoxEditorDocumentFilter( DocumentFilter ):
def __init__(self,jtext):
self._jtext = jtext
def insertString(self,FilterBypass_fb, offset, text, AttributeSet_attrs):
txt = self._jtext.getText()
print('DocumentFilter-insertString:',offset,text,'old:',txt)
FilterBypass_fb.insertString(offset, text.upper(), AttributeSet_attrs)
def replace(self,FilterBypass_fb, offset, length, text, AttributeSet_attrs):
txt = self._jtext.getText()
print('DocumentFilter-replace:',offset, length, text,'old:',txt)
FilterBypass_fb.replace(offset, length, text.upper(), AttributeSet_attrs)
def remove(self,FilterBypass_fb, offset, length):
txt = self._jtext.getText()
print('DocumentFilter-remove:',offset, length, 'old:',txt)
FilterBypass_fb.remove(offset, length)
// (java style ~example for ComboBox-jTextField)
cb = new ComboBox();
cb.setEditable( true );
cbEditor = cb.getEditor();
cbEditorComp = cbEditor.getEditorComponent();
cbEditorComp.getDocument().setDocumentFilter(new myComboBoxEditorDocumentFilter(cbEditorComp));
I am brand new to WindowBuilder, and, in fact, just getting back into Java after a few years, but I implemented "something", then thought I'd look it up and came across this thread.
I'm in the middle of testing this, so, based on being new to all this, I'm sure I must be missing something.
Here's what I did, where "runTxt" is a textbox and "runName" is a data member of the class:
public void focusGained(FocusEvent e) {
if (e.getSource() == runTxt) {
System.out.println("runTxt got focus");
runTxt.selectAll();
}
}
public void focusLost(FocusEvent e) {
if (e.getSource() == runTxt) {
System.out.println("runTxt lost focus");
if(!runTxt.getText().equals(runName))runName= runTxt.getText();
System.out.println("runText.getText()= " + runTxt.getText() + "; runName= " + runName);
}
}
Seems a lot simpler than what's here so far, and seems to be working, but, since I'm in the middle of writing this, I'd appreciate hearing of any overlooked gotchas. Is it an issue that the user could enter & leave the textbox w/o making a change? I think all you've done is an unnecessary assignment.
Here is a Kotlin port of #Boann's answer, which is a great solution that has been working well for me.
import java.beans.*
import javax.swing.*
import javax.swing.event.*
import javax.swing.text.*
/**
* Installs a listener to receive notification when the text of this
* [JTextComponent] is changed. Internally, it installs a [DocumentListener] on the
* text component's [Document], and a [PropertyChangeListener] on the text component
* to detect if the `Document` itself is replaced.
*
* #param changeListener a listener to receive [ChangeEvent]s when the text is changed;
* the source object for the events will be the text component
*/
fun JTextComponent.addChangeListener(changeListener: ChangeListener) {
val dl: DocumentListener = object : DocumentListener {
private var lastChange = 0
private var lastNotifiedChange = 0
override fun insertUpdate(e: DocumentEvent) = changedUpdate(e)
override fun removeUpdate(e: DocumentEvent) = changedUpdate(e)
override fun changedUpdate(e: DocumentEvent) {
lastChange++
SwingUtilities.invokeLater {
if (lastNotifiedChange != lastChange) {
lastNotifiedChange = lastChange
changeListener.stateChanged(ChangeEvent(this))
}
}
}
}
addPropertyChangeListener("document") { e: PropertyChangeEvent ->
(e.oldValue as? Document)?.removeDocumentListener(dl)
(e.newValue as? Document)?.addDocumentListener(dl)
dl.changedUpdate(null)
}
document?.addDocumentListener(dl)
}
You can use it on any text component as follows:
myTextField.addChangeListener { event -> myEventHandler(event) }
Like his code, also public domain.
SOURCE:
javax.swing.JButton[,571,647,80x80,alignmentX=0.0,alignmentY=0.5,border=com.apple.laf.AquaButtonBorder$Toggle#1380cf2a,flags=288,maximumSize=java.awt.Dimension[width=80,height=80],minimumSize=java.awt.Dimension[width=80,height=80],preferredSize=java.awt.Dimension[width=80,height=80],defaultIcon=file:/Users/andreaks/Desktop/PreEntregaiDomino/build/classes/imagenes/A23.png,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=0,left=2,bottom=0,right=2],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=false,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=,defaultCapable=true]
NAME: null
the code im using is
private void JBsetseleccionadActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Object boton = evt.getSource();
JButton este= (JButton) boton;
seleccionado = este;
System.out.println("SOURCE " + boton.toString());
System.out.println("NAME " + este.getName());
}
any ideas?
Try something like:
String text = ((JButton) e.getSource()).getText();
BTW, a better pattern for code like this is:
private JButton button;
button = new JButton("Button");
button.addActionListener(new BListener());
private class BListener implements ActionListener{
public void actionPerformed(ActionEvent e){
if(e.getSource() == button){
//code for when the JButton button is pushed
}
}
}
getComponentVariableName(component)
IF you're using NetBeans or a similar IDE that by default creates private variables (fields) to hold references to all of your components, then you may be able to do something like this ...
private void JBsetseleccionadActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Object boton = evt.getSource();
JButton este= (JButton) boton;
seleccionado = null;
System.out.println("SOURCE " + boton.toString());
System.out.println("NAME " + Awt2.getComponentVariableName(boton));
seleccionado = este;
}
The code to make the above code possible is as follows ...
import java.awt.Component;
import java.lang.reflect.Field;
/**
* additional utilities for working with AWT/Swing.
* this is a single method for demo purposes.
* recommended to be combined into a single class
* module with other similar methods,
* e.g. MySwingUtilities
*
* #author http://javajon.blogspot.com/2013/07/java-awtswing-getcomponentvariablenamec.html
*/
public class Awt2 {
/**
* substitute for component.getName() when used in NetBeans or other IDE
* that creates class fields to hold the components. uses reflection to
* search through class fields for a match.
* #param component the component to look for
* #return hopefully the variable name used to hold this component
*/
static public String getComponentVariableName(Object object) {
if (object instanceof Component) {
final Component component = (Component) object;
final StringBuilder sb = new StringBuilder();
// find the form where the variable name would be likely to exist
final Component parentForm = getParentForm(component);
// loop through all of the class fields on that form
for (Field field : parentForm.getClass().getDeclaredFields()) {
try {
// let us look at private fields, please
field.setAccessible(true);
// get a potential match
final Object potentialMatch = field.get(parentForm);
// compare it
if (potentialMatch == component) {
// return the name of the variable used
// to hold this component
if (sb.length() > 0) sb.append(",");
sb.append(field.getName());
}
} catch (SecurityException | IllegalArgumentException
| IllegalAccessException ex) {
// ignore exceptions
}
}
if (sb.length() > 0) {
return sb.toString();
}
}
// if we get here, we're probably trying to find the form
// itself, in which case it may be more useful to print
// the class name (MyJFrame) than the AWT-assigned name
// of the form (frame0)
final String className = object.getClass().getName();
final String[] split = className.split("\\.");
final int lastIndex = split.length - 1;
return (lastIndex >= 0) ? split[lastIndex] : className;
}
/**
* traverses up the component tree to find the top, which i assume is the
* dialog or frame upon which this component lives.
* #param sourceComponent
* #return top level parent component
*/
static public Component getParentForm(Component sourceComponent) {
while (sourceComponent.getParent() != null) {
sourceComponent = sourceComponent.getParent();
}
return sourceComponent;
}
}
It uses reflection to look at all of the private variables on the form and try to match them to the component you are trying to identify. If it finds a match, it returns the name of the java variable used to refer to the component. If it finds two or more matches, it returns a comma separated list, which is why I added the seleccionado = null; into the first example. If we put the code back the way it was...
private void JBsetseleccionadActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Object boton = evt.getSource();
JButton este= (JButton) boton;
seleccionado = este;
System.out.println("SOURCE " + boton.toString());
System.out.println("NAME " + Awt2.getComponentVariableName(boton));
}
The results may appear as like this...
SOURCE javax.swing.JButton[,6,6,126x28,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.synth.SynthBorder#7c0d41,flags=288,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=0,left=0,bottom=0,right=0],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=What's my name?,defaultCapable=true]
NAME seleccionado,jButton1
Hope this helps!
Although its a old question, i would still like to answer if it helps.
When you are adding the button, make sure you invoke the setName() function as under:
private void JBsetseleccionadActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
Object boton = evt.getSource();
JButton este = (JButton) boton;
este.setName("button_name"); // set the name first
seleccionado = este;
System.out.println("SOURCE " + boton.toString());
System.out.println("NAME " + este.getName());
}
That should resolve your issue and getName() should be able to provide the name. It is sort of weird but still the easiest way to get it done.
I need a wizard which second page content depends on the first page's selection. The first page asks the user the "kind" of filter he wants to create and the second one asks the user to create one filter instance of the selected "kind".
JFace's wizards pages contents (createControl(...) method) are all created when the wizard is open and not when a given page is displayed (this allow JFace to know the wizard size I guess ??).
Because of this, I have to create my second page content BEFORE the wizard is opened BUT I can't since the second page's content depends on the first page selection.
For now the cleaner solution I found consists in creating all (seconds) pages before the wizard is open (with their content) and override the getNextPage() method in the first page's implementation.
The main drawback of that solution is that it can be be expensive when there are many second pages to create.
What do you think about that solution ? How do you manage your wizard's pages ? Is there any cleaner solution I missed ?
The approach is right if you are several other pages which are
completely different one with another
depends on the previous choices made in a previous page
Then you can add the next page dynamically (also as described here)
But if you have just a next page with a dynamic content, you should be able to create that content in the onEnterPage() method
public void createControl(Composite parent)
{
//
// create the composite to hold the widgets
//
this.composite = new Composite(parent, SWT.NONE);
//
// create the desired layout for this wizard page
//
GridLayout layout = new GridLayout();
layout.numColumns = 4;
this.composite.setLayout(layout);
// set the composite as the control for this page
setControl(this.composite);
}
void onEnterPage()
{
final MacroModel model = ((MacroWizard) getWizard()).model;
String selectedKey = model.selectedKey;
String[] attrs = (String[]) model.macroMap.get(selectedKey);
for (int i = 0; i < attrs.length; i++)
{
String attr = attrs[i];
Label label = new Label(this.composite, SWT.NONE);
label.setText(attr + ":");
new Text(this.composite, SWT.NONE);
}
pack();
}
As shown in the eclipse corner article Creating JFace Wizards:
We can change the order of the wizard pages by overwriting the getNextPage method of any wizard page.Before leaving the page, we save in the model the values chosen by the user. In our example, depending on the choice of travel the user will next see either the page with flights or the page for travelling by car.
public IWizardPage getNextPage(){
saveDataToModel();
if (planeButton.getSelection()) {
PlanePage page = ((HolidayWizard)getWizard()).planePage;
page.onEnterPage();
return page;
}
// Returns the next page depending on the selected button
if (carButton.getSelection()) {
return ((HolidayWizard)getWizard()).carPage;
}
return null;
}
We define a method to do this initialization for the PlanePage, onEnterPage() and we invoke this method when moving to the PlanePage, that is in the getNextPage() method for the first page.
If you want to start a new wizard based on your selection on the first page, you can use the JFace base class org.eclipse.jface.wizard.WizardSelectionPage.
The example below shows a list of available wizards defined by an extension point.
When you press Next, the selected wizard is started.
public class ModelSetupWizardSelectionPage extends WizardSelectionPage {
private ComboViewer providerViewer;
private IConfigurationElement selectedProvider;
public ModelSetupWizardSelectionPage(String pageName) {
super(pageName);
}
private class WizardNode implements IWizardNode {
private IWizard wizard = null;
private IConfigurationElement configurationElement;
public WizardNode(IConfigurationElement c) {
this.configurationElement = c;
}
#Override
public void dispose() {
}
#Override
public Point getExtent() {
return new Point(-1, -1);
}
#Override
public IWizard getWizard() {
if (wizard == null) {
try {
wizard = (IWizard) configurationElement
.createExecutableExtension("wizardClass");
} catch (CoreException e) {
}
}
return wizard;
}
#Override
public boolean isContentCreated() {
// TODO Auto-generated method stub
return wizard != null;
}
}
#Override
public void createControl(Composite parent) {
setTitle("Select model provider");
Composite main = new Composite(parent, SWT.NONE);
GridLayout gd = new GridLayout(2, false);
main.setLayout(gd);
new Label(main, SWT.NONE).setText("Model provider");
Combo providerList = new Combo(main, SWT.NONE);
providerViewer = new ComboViewer(providerList);
providerViewer.setLabelProvider(new LabelProvider() {
#Override
public String getText(Object element) {
if (element instanceof IConfigurationElement) {
IConfigurationElement c = (IConfigurationElement) element;
String result = c.getAttribute("name");
if (result == null || result.length() == 0) {
result = c.getAttribute("class");
}
return result;
}
return super.getText(element);
}
});
providerViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
#Override
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection = event.getSelection();
if (!selection.isEmpty()
&& selection instanceof IStructuredSelection) {
Object o = ((IStructuredSelection) selection)
.getFirstElement();
if (o instanceof IConfigurationElement) {
selectedProvider = (IConfigurationElement) o;
setMessage(selectedProvider.getAttribute("description"));
setSelectedNode(new WizardNode(selectedProvider));
}
}
}
});
providerViewer.setContentProvider(new ArrayContentProvider());
List<IConfigurationElement> providers = new ArrayList<IConfigurationElement>();
IExtensionRegistry registry = Platform.getExtensionRegistry();
IExtensionPoint extensionPoint = registry
.getExtensionPoint(<your extension point namespace>,<extension point name>);
if (extensionPoint != null) {
IExtension extensions[] = extensionPoint.getExtensions();
for (IExtension extension : extensions) {
IConfigurationElement configurationElements[] = extension
.getConfigurationElements();
for (IConfigurationElement c : configurationElements) {
providers.add(c);
}
}
}
providerViewer.setInput(providers);
setControl(main);
}
The corresponding wizard class looks like this:
public class ModelSetupWizard extends Wizard {
private ModelSetupWizardSelectionPage wizardSelectionPage;
public ModelSetupWizard() {
setForcePreviousAndNextButtons(true);
}
#Override
public boolean performFinish() {
// Do what you have to do to finish the wizard
return true;
}
#Override
public void addPages() {
wizardSelectionPage = new ModelSetupWizardSelectionPage("Select a wizard");
addPage(wizardSelectionPage);
}
}
Another alternative is to #Override setVisible. You can update page values or add additional widgets at that time.
I have a different solution.
If page depends on the result of page 1, create a variable and pass it into to first page, when that wizard page has the option from the user, then the last thing before the page is closed is to set the variable to the required value.
Then pass this variable to wizard, then pass it to the next wizard page. Then do a simple if statement and that way you get both choices together.
Remember that in most code there is only a small difference in the user options, so remember not to get bogged down in duplicating your code.