I have two separate components, one is a class that extends JTable, the other is a class that extends JPanel (contains a form).
Both are displayed on the same view when that application is run, when I click on a row on the table, I expect the textfileds on the form to be updated but nothing happens
I use observer (the form class is the listener) pattern to send the clicked row to the class/ panel containing the form fields to be updated, the values are received but the textfields are not updated.
The code below is in the form class and it updates the form fields, the form class is added as a listener in the table class, the method below is fired when a table row is clicked
public void onTableRowClick(CollectedParcelEvent e)
{
JOptionPane.showMessageDialog(null,"test", "test", 1);
txtCost.setText(Double.toString(e.getSource().getCost()));
txtCustomerName.setText(e.getSource().getCustomer().getName());
txtCost.repaint();
txtCost.revalidate();
}
public void onTableRowClick(CollectedParcelEvent e)
{
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
//JOptionPane.showMessageDialog(null,"test", "test", 1);
txtCost.setText(Double.toString(e.getSource().getCost()));
txtCustomerName.setText(e.getSource().getCustomer().getName());
}
});
}
Events are handled on the single event thread. There the GUI is not responsive for other events, and one should postpone doing such things later with invokeLater.
I don't understand why are you calling revalidate(); as its just tells the layout manager to reset based on the newly added or removed component list.
See this link for more answers about using revalidate(); and also this one
And perhaps, repaint(); should be enough for the required change.
So check your method to see if it really gets fired or not.
Related
I've written code for a Composite containing multiple controls (StyledTexts, Buttons, etc.).
I wanted to implement a FocusListener for the whole Composite (not only for one of the controls inside of it), but the FocusListener did not do anything.
I tried to implement it myself, and my attempt was: Add a FocusListener to every Control inside the composite and handle these events.
While trying to do so, I found some problems:
When two controls have a FocusListener and I change the focus from one to the other, the FocusLost event is fired before the FocusGained
So I can't find out if the focus was given to one of the other controls in my composite, or if the composite lost the focus completely.
Note: I tried using Display.getFocusControl() inside the focusLost(...) method, but it only returns the control from which the focus is taken!
My question: Is it possible to find out which control will receive the focus next while being inside the focusLost(...) method?
If not: Is there any other way to implement a FocusListener for a Composite?
At the time the focusLost() event is sent, it isn't yet known which control (if any) will receive the focus.
You can add a display filter that will inform you whenever a control within your entire application gains focus. Within your ' Listener` implementation, you can save the text input whenever a control gains focus that is not contained in the composite. For example:
Listener listener = new Listener() {
#Override
public void handleEvent( Event event ) {
if( event.widget != text || event.widget != fontButton || ... ) {
save();
}
}
};
display.addFilter( SWT.FocusIn, listener );
Make sure to add the listener only while the observed composite is alive. Remove the filter when the composite is disposed of.
display.removeFilter( SWT.FocusIn, listener );
#RüdigerHermanns works pretty good, but for the sake of completeness I'll post my solution here too:
I've written listeners for SWT.Activate and SWT.Deactivate in my composite:
public MyComposite(Composite parent, int style)
{
super(parent, style);
createGuiElements(this);
this.addListener(SWT.Deactivate, new Listener()
{
#Override
public void handleEvent(Event event)
{
for (FocusListener listener : focusListeners) listener.focusLost(new FocusEvent(event));
}
});
this.addListener(SWT.Activate, new Listener()
{
#Override
public void handleEvent(Event event)
{
for (FocusListener listener : focusListeners) listener.focusGained(new FocusEvent(event));
}
});
}
which notify all focusListeners on the Composite.
There are two listeners:
table.addListener(new ItemClickListener() {
public void itemClick(ItemClickEvent event) {
// fireEvent(...);
}
});
table.addListener(new Table.ValueChangeListener() {
public void valueChange(final ValueChangeEvent event) {
// do something
}
});
I want to fire an event from the first listener to call the second.
In one listener, I want to update the data source for the table by clicking on the row; in the second listener, I want to restore the cursor on the selected row.
How can I do it?
If you change a column value of a row, then second listener is fired. To do so, you can change the column then you can regain its value in the second listener.
You shouldn't be trying to manually trigger events. If you have code in the second listener that needs to be called, you should abstract it out into a third method.
It's difficult to understand exactly what you want, however. I would suggest adding more details to your question.
I have written a Swing GUI with several controls associated with the same Action subclass. The implementation of the Action subclass follows this psudocode:
public class MyGUI
{
Gizmo gizmo_; // Defined elsewhere
public class Action_StartPlayback extends AbstractAction
{
/* ctor */
public Action_StartPlayback(String text, ImageIcon icon, String desc, Integer mnem)
{
super(text, icon);
putValue(SHORT_DESCRIPTION, desc);
putValue(MNEMONIC_KEY, mnem);
}
#Override public boolean isEnabled()
{
return gizmo_ == null;
}
#Override public void actionPerformed(ActionEvent e)
{
gizmo_ = new Gizmo();
}
Action_StartPlayback act_;
};
The action is associated with both a button and a menu item, in a way similar to this psudocode:
act_ = new Action_StartPlayback(/*...*/);
// ...
JButton btn = new JButton(act_);
JMenu mnu = new JMenu(act_);
When I click the button or the menu item, the action's actionPerformed is fired correctly, gizmo_ is initialized and is non-null and everything works as expected -- except that the button and menu item are still enabled.
I expected that isEnabled would have been called again "automagically" but this is obviously not happening. isEnabled() is never called again.
This evokes two questions:
Is it OK for me to #Override the isEnabled() method as I have done here?
Assuming the answer to #1 is yes, how do I trigger a refresh of the GUI so that isEnabled() is called again, resulting in the button & menu item being disabled?
Instead of overriding setEnabled you could simply call setEnabled(false) after you intitialize your gizmo in your actionPerformed method:
#Override public void actionPerformed(ActionEvent e)
{
gizmo_ = new Gizmo();
setEnabled(false);
}
Here's the setEnabled implementation from AbstractAction:
public void setEnabled(boolean newValue) {
boolean oldValue = this.enabled;
if (oldValue != newValue) {
this.enabled = newValue;
firePropertyChange("enabled",
Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
}
}
The automagical you're looking for is the call to firePropertyChange, which notifies components based on this action that the state has changed, so the component can update its own state accordingly.
I am no pro at this, but I don't see a see an automatic way of doing this, of notifying listeners that the state of enabled has changed. Of course you can call setEnabled(false) at the start of the actionPerformed, and then code Gizmo (or a wrapper on Gizmo) to have property change support and then add a PropertyChangeListener to Gizmo, and in that listener, when the state changes to DONE, call setEnabled(true). A bit kludgy but it would work.
This is not strictly limited to Swing, but a more general Java principle. A lot of classes in the JDK (and in other libraries) have a getter and a setter for a property. Those methods are not meant to be overridden to return a dynamic value as most of the times the superclass accesses the corresponding field directly and does not go through the getters.
If you have dynamic behavior, you should call the corresponding setter each time the value changes. This will notify the super class changes have been made, and typically this will also fire a property change event to notify other interested parties.
You can find a bit more on this convention if you do a search on Java beans.
In your case, a possible solution is to let your UI class fire a PropertyChangeEvent when that gizmo instance changes, and let your actions listen for that event. When they receive such an event, they update their own enabled state.
The enabled-state is stored in both of your objects, in the AbstractAction and in the JButton.
This is important because you only need one instance of Action_StartPlayback for multiple Components like:
In the menu's button.
In a toolbar.
In a Shortcut Strgp in example.
All of them can have the same instance of Action_startPlayback. The Action_startPlayback is the only source of truth. The components are responsible to respect this source of truth so every Component will ask the AbstractAction to notify them if something has been changed. The AbstractAction will remember all the components and will notify them using the Method firePropertyChange().
But how to repaint all pending components? You must force all pending Components to ask the Action_startPlayback for the actuall enabled-state! Look at this:
#Override public void actionPerformed(ActionEvent e)
{
gizmo_ = new Gizmo();
// now, force the components to get notified.
setEnabled(true);
}
I'm developing an application where I want something to be triggered both by the user updating the contents of a JTextArea, or manually via pressing a JButton.
I have done the first part using a DocumentListener and putting the relevant code in its insertUpdate method.
I haven't used Actions before, but I've heard they are useful for situations where you need something to be triggered by multiple controls. Is it possible to trigger the action from the DocumentListener? Is it a good idea to use Actions at all, or should I just put my code in a normal method?
(in the constructor):
textAreaInput.getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent e) {
// do something
}
public void removeUpdate(DocumentEvent e) {}
public void changedUpdate(DocumentEvent e) {}
});
and the Action, which is a field:
Action doSomething = new AbstractAction("Do Something!") {
#Override
public void actionPerformed(ActionEvent e) {
// do it
}
};
clarification:
The JTextArea will receive text that is pasted in by the user, which I want to parse automatically. The parsing depends on other values set elsewhere in the GUI; if the user changes these other values, he may want to re-parse the text, hence the need to perform the same action by pressing a button.
I want something to be triggered both by the user updating the contents of a JTextArea, or manually via pressing a JButton.
This doesn't make sense to me.
Why would clicking a button invoke the same Action as a user typing text into a text area?
I haven't used Actions before, but I've heard they are useful for situations where you need something to be triggered by multiple controls
That statement is meant for controls that the user clicks, like JMenuItems, JButtons or hitting Enter on a text field. In general they can be used when you use an ActionListner.
A DocumentListener is not an ActionListener so as I stated earlier the use of an Action doesn't seem appropriate.
I think you need to clarify your requirement.
Edit, based on clarification
if the user changes these other values, he may want to re-parse the text
Why does the user have a choice? If you change the font, text, foreground, background of a text area, the component it automatically repainting, you don't have to ask for this to be done. If you look at the code for these methods they always end up invoking the revalidate() and repaint() methods.
The parsing depends on other values set elsewhere in the GUI;
Sounds like you need a custom class. Maybe a ParsedTextArea or ParsedDocument. This class would contain the "properties" that can be set elsewhere in the GUI. It would implmenent the DocumentListener. It would also support your "parseTheText" method. So whenever a property is changed or a DocumentEvent is generated you automatically invoked the "parseTheText" method. This way you don't need a separate button and the component will always be in sync because the parsing is automatic.
You can invoke the actionPerformed() method, whether it's in an Action or not. There's an example here.
I think you need not create the Action object. You can add ActionListener to the Button just like you have added DocumentListener to the Document of the input. If I correctly understand your problem, may be you should do something like this:
textInput.getDocument().addDocumentListener(new DocumentListener(){
#Override
public void insertUpdate(DocumentEvent e) {
doIt();
}
#Override
public void removeUpdate(DocumentEvent e) {}
#Override
public void changedUpdate(DocumentEvent e) {}
});
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
doIt();
}
});
doIt() is a method in which you will do what you wanted to do.
The application i work with override the default JComboBox from swing. Leets Call it MyComboBox. This version of ComboBox implement the FocusListener and contains the two methods focusGained and focusLost.
Now, in one of the panel of the application, the form contains a ComboBox of this type:
MyComboBox aMyComboBox = new MyComboBox();
I want to add a listener on this like that :
aMyComboBox.addFocusListener(new FocusListener() {
public void focusLost(FocusEvent e) {
//Do something here
}
public void focusGained(FocusEvent e) {
//Do something else
}
});
But when i run the code, it never pass into these method but only execute the focusGained/lost from the MyComboBox class.
Is there a way to add a listener on an object that already implements FocusListener?
Additional FocusListener should work unless the instance used in MyComboBox consumes the event (AWT event consumption).
Try making an example with an ordinary JComboBox -- this will help narrowing down the cause of the problem.