inline copy paste JEditorPane HTML - java

I'm desperatly trying to implement a customized copy/paste in a JTextPane in HTML mode.
The most part is working well, I get the html content using EditorKit.write(), I paste it using editorKit.read(). Perfect world.
BUT, when I have : <p> test </p> in my editor and I try to copy "es" to obtain
<p> tesest </p>, I obtain instead
<p>tes</p>
<p>es</p>
<p>t</p>
Knowing that, I m trying to figure out a way to paste "inline" the part supposed to be inline, and in block the part which was in block during the copy. Typically,
if I have :
<p>mon beau sapin</p>
<p>roi des forêts</p>
<p>que j'aime ta verdure</p>
And if I copy :
beau sapin</p>
<p>roi des forêts</p>
<p>que
And paste it after "mon", I expect :
<p>mon beau sapin</p>
<p>roi des forêts</p>
<p>que beau sapin</p>
<p>roi des forêts</p>
<p>que j'aime ta verdure</p>
And I obtain instead :
<p>mon</p>
<p>beau sapin</p>
<p>roi des forêts</p>
<p>que</p>
<p>beau sapin</p>
<p>roi des forêts</p>
<p>que j'aime ta verdure</p>
I tried various approach, like removing the <p></p> of the first and last lines (EditorKit.read add it back by itself), using editorKit.insertHTML (but what kind of Tag should I put ?), insert line by line (most part of times, I obtain a p inside another p) etc.
but the real problem that it's impossible to write what you want in the htmlDocument. How can I write sapin</p> <p>roi at a specified position ?
EditorKit.read ? it will add <p>sapin</p> <p>roi</p>
Editorkit.insertHTML ? I need to precise a wrapping Tag...
I show you my last try :
private static void insertHTMLContent(JMathTextPane jtp, String html, int offset) {
Document doc = Jsoup.parse(html);
Elements elts = doc.body().children();
//unwrap the last and first element
if(elts.size()>2) { elts.last().unwrap(); }
if(elts.size()>=1) { elts.first().unwrap(); }
//We add a fake DIV element and remove it just at the next line
editorKit.insertHTML(jtp.htmlDoc, offset, "<div id='copie'>"+doc.body().html()+"</div>", 0, 0, HTML.Tag.DIV);
jtp.getHTMLdoc().setOuterHTML(jtp.getHTMLdoc().getElement("copie"),doc.body().html());
}
I can't show you the result : EditorKit.write tries to fix the html by itself. But the HTMLDocument is completely messy.
For you to try :
public class Test {
private static JTextPane editor = new Editor();
private static JMenuBar menu = new Menu();
private static String clipboard = "";
private static Action copy = new Copy();
private static Action paste = new Paste();
public static void main(String[] args) {
JFrame f = new JFrame();
f.setContentPane(editor);
f.setJMenuBar(menu);
f.setSize(600, 400);
f.setVisible(true);
}
public static class Editor extends JTextPane {
public Editor() {
this.setDocument(new HTMLDocument());
this.setEditorKit(new HTMLEditorKit());
}
}
public static class Menu extends JMenuBar {
public Menu() {
add(new JButton(copy));
add(new JButton(paste));
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK), "copy");
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK), "paste");
getActionMap().put("copy", copy);
getActionMap().put("paste", paste);
}
}
public static class Copy extends AbstractAction {
public Copy() {super("copy");}
#Override
public void actionPerformed(ActionEvent e) {
StringWriter w = new StringWriter();
try {
editor.getEditorKit().write(w, editor.getDocument(), editor.getCaretPosition(), editor.getSelectedText().length());
} catch (Exception ex) {Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);}
clipboard = w.toString();
}
}
public static class Paste extends AbstractAction {
public Paste() {super("paste");}
#Override
public void actionPerformed(ActionEvent e) {
try {
editor.getEditorKit().read(new StringReader(clipboard), editor.getDocument(), editor.getCaretPosition());
} catch (Exception ex) {Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);}
}
}
}
Sorry I was long. I accept any help.

I am afraid there is no easy way. When you paste you would like to keep original paragraph and avoid new <p> creation, right? The problem is current paragraph and copied one may have different attributes. E.g. current is left aligned but copied one has right alignment.
How to resolve the case? To simplify this kit just creates <p> element.
You can try to create an independent HTMLDocument from the clipboard content and iterate through the Document's structure extracting elements (paragraphs and texts) and inserting them in the original document.

For further readers, I solved it with a very easy trick. I just removed the \n added before and after the pasted text when needed.
public static void copyContent(JTextPane jtp, String text, int offset) {
try {
boolean start = offset>0 ? !jtp.getText(offset-1, 1).equals("\n") : false;
Position p = jtp.getDocument().createPosition(offset);
new HTMLEditorKit().read(new StringReader(html), jtp.getHTMLdoc(), offset);
if(start) {jtp.getDocument().remove(offset, 1);}
if(offset>0) {jtp.getDocument().remove(p.getOffset()-1, 1);}
} catch (IOException | BadLocationException ex) {
Logger.getLogger(EditeurIO.class.getName()).log(Level.SEVERE, null, ex);
}
}

Related

How can I save the color/size of a JLabel in a file?

I am trying to save the color, text and size (gridheight/gridwidth) of a JLabel in a file. So whenever the program runs I can view past JLabels.
I know how to read/write Strings onto a file but is it possible to save the state of a JLabel?
JLabel is a serialized object, you can save the whole JLabel object in a file using ObjectOutputStream and read it from ObjectInputStream. Like this.
UPDATED THE PREVIOUS CODE ACCORDING TO DataObject CLASS:
public static void main(String[] args) {
// Creating a JLabel
JLabel label = new JLabel("REHAN JAVED");
label.setForeground(Color.RED);
label.setSize(new Dimension(500, 500));
// adding it into the file.
addItIntoFile(new DataObject(label, 200, 50, 0, 1));
// reading file..
DataObject dataObj = readDataObject();
JLabel newLabel = dataObj.getLabel();
int x = dataObj.getXPosition();
// and take all the data from getters.
System.out.println(newLabel.getText()+"\n"+newLabel.getForeground()+"\n"+label.getSize());
}
public static void addItIntoFile(DataObject dataObj) {
File file = new File("data.txt");
try {
file.createNewFile();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(dataObj);
oos.close();
// You can handle the different exceptions according to you.
} catch (Exception e) {
e.printStackTrace();
}
}
public static DataObject readDataObject() {
DataObject dataObj = null;
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("data.txt")));
dataObj = (DataObject) ois.readObject();
ois.close();
// You can handle the different exceptions according to you.
} catch (Exception e) {
e.printStackTrace();
}
// Handling null data..
if(dataObj == null) {
dataObj = new DataObject(new JLabel(), 0, 0, 0, 0);
}
return dataObj;
}
It will works fine with it. :)
UPDATED VERSION:
To include the grid constraints like width, height, x, and y positions, create a new class and implements the Serializable interface with it and store it directly into the file.
public class DataObject implements Serializable {
private JLabel label;
private int gridWidth;
private int gridHeight;
private int gridXPosition;
private int gridYPosition;
// Your Constructor..
public DataObject(JLabel label, int gridWidth, int gridHeight,
int gridXPosition, int gridYPosition) {
this.label = label;
this.gridWidth = gridWidth;
this.gridHeight = gridHeight;
this.gridXPosition = gridXPosition;
this.gridYPosition = gridYPosition;
}
// your getter and setters...
}
You have to define what you need to save, and how.
What to save
You need to define what is important for you, as an example the color can be the with or without transparency informations.
The text can be a simple string or an object holding informations like character type, size, decorations (bold, underline... ).
How to save
You need to define a format for each information (for example the color can be saved with its name "red" or its hex value "#FF0000" or can be a reference to a custom variable "mainColorTemplate1").
You need also to define a format of the file (xml, json, custom binary, property file, yaml...).
I suggest to try the simplest solution for your knowledge. It is very simple to hold all the data in an object and save it as JSon using libraries like GSon or Faster Jackson.
Here is a possible format in json:
{
"labels": [
{
"text":"My first label",
"x":2,
"y":1,
"color":"#FF0000"
},
{
"text":"My Second label",
"x":4,
"y":3,
"color":"#FFFF00"
}
]
}
One way do it is, by utilizing object Serialization. All Swing components implement Serializable interface. You can save the component and the next time you run the application, the component will be as you left it. Two interesting questions to read about this are: Why is Java Swing serializable? and Swing components and serialization.

java changes in jtextfield [duplicate]

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.

Constantly reading a String from JTextField

I've got a DocumentListener to look for any changes in the JTextField:
public class MyDocumentListener implements DocumentListener {
static String text;
public void insertUpdate(DocumentEvent e) {
updateLog(e);
}
public void removeUpdate(DocumentEvent e) {
updateLog(e);
}
public void changedUpdate(DocumentEvent e) {
//Plain text components do not fire these events
}
public static String passText() {
System.out.println("string that will be passed is: "+text);
return text;
}
public void updateLog(DocumentEvent e) {
Document doc = (Document)e.getDocument();
int length = e.getLength();
try {
text = doc.getText(0, length);
} catch (BadLocationException e1) {
e1.printStackTrace();
}
System.out.println("you typed "+text);
}
}
And then, in the other class:
String info = MyDocumentListener.passText();
The problem is I'm getting only one character, instead of the whole String. Any suggestions?
You're getting the length of the change instead of the length of the document:
int length = e.getLength(); // probably 1
should be
int length = doc.getLength();
int getLength()javadoc
The answer provided by paislee is indeed correct. You would like to add just another way to do the same thing. You can use bindings, which adds the concept of ValueHolders, variables that will store and reflect imediatley any property changes of your graphical components. It can provide a very effective way to implement MVC design pattern with Swing since the communication between Model-Controller-View is much more affective and decoupled.
JGoodies has an excellent and open source implementation for it. If you can spend sometime and want to improve your design, don't hesitate to take a look.

JTextField variable returns null outside of actionlistener?

I'm making a program that adds and formats files. I actually have many classes, but for the purpose of this question let's say I have two, guidialog and guimain.
In guidialog I have a JTextField and an actionlistener for it. Here is the actionlistner:
public void actionPerformed(ActionEvent event) {
blockName=textFieldBlockName.getText();
System.out.println("Made new block: "+blockName);
canClose=true;
guimain blockAddWrite = new guimain();
blockAddWrite.addNewBlockFile();
}
});
public String blockName;
Now in guimain I have a formatter which writes a file based on the name given in the text field:
public void addNewBlockFile() {
blockdialog blockName = new blockdialog();
try {
newBlock = new Formatter("Block" + blockName.blockName + ".java");
System.out.println("Created File: Block" + blockName.blockName);
} catch (Exception e) {
System.out.println("ERROR: Could Not Output Block File");
}
}
I do edit and close the file, but it wasn't necessary. But when I try this, all of the stuff in guimain that refers to blockName outputs as "null". I can't figure it out.
That's because in guimain, you're not using the blockName field of the dialog where the user entered something: you're using the blockName field of another, newly constructed dialog:
public void addNewBlockFile() {
blockdialog blockName = new blockdialog();
^--- the dialog is not the one where the user entered something. It's a new one.
You should pass the blockName from the dialog to the guimain:
public void actionPerformed(ActionEvent event) {
blockName=textFieldBlockName.getText();
System.out.println("Made new block: "+blockName);
canClose=true;
guimain blockAddWrite = new guimain(blockName); // we construct a guimain instance with the entered text
blockAddWrite.addNewBlockFile();
}
});
Side notes:
you should not use public fields. Use getter methods.
classes should be start with an upper-case and be spelt in CamelCase: GuiMain.

Im trying to use .getName on a JButton, but it returns null, but when i do the toString it returns me all the right properties?

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.

Categories

Resources