SWT remove all listener from StyledText - java

How do I remove all listener from a SWT StyledText if I'm missing the instances? I tried to use the StyledText.getListeners(int) method to get all the instances, being able to remove them afterwards. But it's exhausting to find all the required int values. Is this the most straightforward way? Thank you!
Here is my temporary solution:
public void removeAllListener(StyledText st) {
int[] eventTypes = { 3007, 3011, SWT.Resize, SWT.Move, SWT.Dispose,
SWT.DragDetect, 3000, SWT.FocusIn, SWT.FocusOut, SWT.Gesture,
SWT.Help, SWT.KeyUp, SWT.KeyDown, 3001, 3002, SWT.MenuDetect,
SWT.Modify, SWT.MouseDown, SWT.MouseUp, SWT.MouseDoubleClick,
SWT.MouseMove, SWT.MouseEnter, SWT.MouseExit, SWT.MouseHover,
SWT.MouseWheel, SWT.Paint, 3008, SWT.Selection, SWT.Touch,
SWT.Traverse, 3005, SWT.Verify, 3009, 3010 };
for (int eventType : eventTypes) {
Listener[] listeners = st.getListeners(eventType);
for (Listener listener : listeners) {
st.removeListener(eventType, listener);
}
}
}
I had to copy some of the values since they are part of StyledText class declared with the default modifier. So I cannot access them.
I hope, I didn't miss any int values ;)

In general, there is no mechanism to do this. However, I managed to get it working by subclassing StyledText. The new class is called StyledTextWithListeners. You can just rename it if you want ;) . However, you will have to use
styledText.addListenerByUser(int eventType, Listener listener);
to add you Listeners rather than
styledText.addListener(int eventTyle, Listener listener);
This is necessary to discriminate between Listeners added by you and those added by SWT upon creation.
To remove all listeners added by the user (you) just call
styledText.removeAllListeners();
Here is the code:
import java.util.HashMap;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
public class MiniExample {
public static void main(String[] args) {
Display display = Display.getDefault();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout(1, false));
StyledTextWithListeners text = new StyledTextWithListeners(shell, SWT.BORDER);
text.setText("TEXT");
text.addListenerByUser(SWT.Verify, new Listener() {
#Override
public void handleEvent(Event arg0) {
}
});
text.removeAllListeners();
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
}
public static class StyledTextWithListeners extends StyledText
{
HashMap<Integer, Listener> listeners;
public StyledTextWithListeners(Composite parent, int style) {
super(parent, style);
}
public void addListenerByUser(int eventType, Listener listener)
{
addListener(eventType, listener);
System.out.println("Adding: " + listener.getClass().toString());
if(listeners == null)
listeners = new HashMap<Integer, Listener>();
listeners.put(eventType, listener);
}
public void removeAllListeners()
{
for(Integer type : listeners.keySet())
{
System.out.println("Removing: " + listeners.get(type).getClass().toString());
removeListener(type.intValue(), listeners.get(type));
}
listeners = new HashMap<Integer, Listener>();
}
}
}
It basically saves all the Listeners you add in a HashMap and removes them when you call removeAllListeners.
Keep in mind that I didn't take care of the case where you add a second Listener with the same eventType to the StyledText as done before. In this case, the old Listener will be replaced in the HashMap, but not removed from the StyledText. If this case can occur in your scenario, just add some code yourself.

Related

In GTK, SWT.ON_TOP causes unpredictable behaviour on .setfocus()

Here's what I want to achieve:
On some trigger (here: click of a button), a floating toolbar should appear. The focus should still be on the text input field after this.
When a button in the floating toolbar is clicked, the focus should remain with the text input field.
The floating toolbar should realised by a Shell that is always-on-top.
I have achieved (1) by introducing a ShellListener on the overlay shell that calls text.setFocus (based on this question).
I have achieved (2) by keeping a MouseListener on the button and calling text.setFocus on click.
Now, my problem is that each solution works fine on its own. However, when I use both listeners (as in the MWE below), I get weird behaviour:
The text field is correctly focussed after the overlay shell appears
Clicking the button in the overlay shell does not return the focus to the text field unless
You manually focus the main shell by clicking anywhere in it
You spam clicks on the button in the overlay shell, after some random amount of clicks, the text input is correctly focussed, and then also for each successive click.
This seems to a problem specific to Ubuntu (GTK). I am using SWT 4.16.
Consider the following MWE.
Note that everything works fine if we omit SWT.ON_TOP and replace it with SWT.SHELL_TRIM.
Weirdly enough, with SWT.ON_TOP, shellActivated on overlayShell does not always trigger when the button in the overlay shell is clicked, and when it does, it does not coincide with the focus being correctly transferred.
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class ShellWithoutTitleBarSO2 {
public static void main(String[] args) {
final Display display = new Display();
final Shell mainShell = new Shell(display, SWT.SHELL_TRIM);
mainShell.setSize(300, 200);
mainShell.setLayout(new GridLayout());
mainShell.open();
Text text = new Text(mainShell, SWT.BORDER);
final Shell overlayShell = new Shell(display, SWT.ON_TOP);
overlayShell.setSize(150, 150);
overlayShell.setLocation(400, 200);
overlayShell.setLayout(new GridLayout());
// (1) give back focus when overlay shell is first opened
overlayShell.addShellListener(new ShellListener() {
#Override public void shellActivated(ShellEvent arg0) {
text.setFocus();
}
#Override public void shellClosed(ShellEvent arg0) { }
#Override public void shellDeactivated(ShellEvent arg0) { }
#Override public void shellDeiconified(ShellEvent arg0) { }
#Override public void shellIconified(ShellEvent arg0) { }
});
Button button = new Button(mainShell, SWT.PUSH);
button.setText("open overlayShell");
button.addMouseListener(new MouseListener() {
#Override public void mouseDoubleClick(MouseEvent e) { }
#Override public void mouseDown(MouseEvent e) { }
#Override public void mouseUp(MouseEvent e) {
overlayShell.setVisible(true);
}
});
Button buttonInOverlay = new Button(overlayShell, SWT.PUSH | SWT.BORDER);
buttonInOverlay.setText("foo");
// (2) give back focus when button in overlay is clicked
// this seems to only work (randomly, race-condition like) when overlay shell
// has SWT.ON_TOP. If not, then no effect.
buttonInOverlay.addMouseListener(new MouseListener() {
#Override public void mouseDoubleClick(MouseEvent arg0) { }
#Override public void mouseDown(MouseEvent arg0) { }
#Override public void mouseUp(MouseEvent arg0) {
boolean didSetFocus = text.setFocus();
// notably, this is true even when focus is effectively not set
}
});
// Set up the event loop.
while (!mainShell.isDisposed()) {
if (!display.readAndDispatch()) {
// If no more entries in event queue
display.sleep();
}
}
display.dispose();
}
}
I've begun stepping through the SWT source code but I'm quite puzzled by it. Does anyone have any ideas what might cause this weird issue?

How can I convert a swing JTextField component in to an object of a type that extends it

I have an application using a third party package that has a factory that returns to me JTextField objects that are then added to a GUI. This makes up about 10% of the JTextFields used.
I can't change the third party package but have a requirement to add right click (cut, copy and paste) options in to all ofthe fields.
Now I have a RightClickTextField that extends JTextField and has all the functionality built in to it, this serves to solve my issue for 90% of the application.
However for the 10% that's using the third party package to get JTextFields I cannot think of a solution that will allow me to declare the fields as RightClickTextFields yet use the factory I have to get back the Boxes. I know I cannot cast the result as the objects returned are not of a type that high up in the hierarchy, and a copy constructor won't work since I cannot copy every property being set by the factory, but I don't know of a way to upcast the JTextField in to my type. Is there one?
Rather than subclassing or trying to cast it, can you put your right-click functionality into its own class which implements the MouseInputListener interface, and simply add an instance of your right-click handler to the JTextField objects in question?
Maybe use the Decorator Pattern. This way you can stop using RightClickTextField at all - start using RightClickTextFieldDecorator and supply it either with your own JTextFields or the ones you get from 3rd party thingy.
Thanks for all the comments. I think the actual answer to my question is:
You can't.
Whilst all of the suggestions are valid, I knew it was possible to do all those things, I just wanted to know if I could do it my way first.
My solution (based on feedback here and my own preference) was to create this class below, and manage and expose a single instance of it from a singleton.
I'd appreciate thoughts on this solution?
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.text.JTextComponent;
public class CopyPasteTextComponentPopoupMenu extends JPopupMenu implements
ActionListener {
private JTextComponent lastInvoker;
private JMenuItem cutMenuItem;
private JMenuItem copyMenuItem;
private JMenuItem pasteMenuItem;
private Map<JTextComponent, JTCProperties> managedComponents;
private MouseListener heyListen;
public CopyPasteTextComponentPopoupMenu() {
super();
init();
}
public CopyPasteTextComponentPopoupMenu(String label) {
super(label);
init();
}
#Override
public void show(Component invoker, int x, int y) {
JTCProperties props = managedComponents.get(invoker);
if(props!=null) {
this.lastInvoker = (JTextComponent) invoker;
setEnabled(props);
super.show(invoker, x, y);
} else {
this.lastInvoker = null;
}
}
public void manageTextComponent(JTextComponent jtc, boolean canCut,
boolean canCopy, boolean canPaste) {
jtc.addMouseListener(heyListen);
JTCProperties props = new JTCProperties(canCut,canCopy,canPaste);
managedComponents.put(jtc,props);
}
public void dispose() {
for (JTextComponent component : managedComponents.keySet()) {
component.removeMouseListener(heyListen);
managedComponents.remove(component);
}
}
#Override
public void actionPerformed(ActionEvent e) {
if (lastInvoker != null) {
if (e.getSource() == cutMenuItem) {
lastInvoker.cut();
} else if (e.getSource() == copyMenuItem) {
lastInvoker.copy();
} else if (e.getSource() == pasteMenuItem) {
lastInvoker.paste();
}
}
}
private void setEnabled(JTCProperties props) {
cutMenuItem.setEnabled(props.canCut);
copyMenuItem.setEnabled(props.canCopy);
pasteMenuItem.setEnabled(props.canPaste);
}
private void init() {
this.managedComponents = new HashMap<JTextComponent, JTCProperties>();
this.setPreferredSize(new Dimension(100, 70));
cutMenuItem = new JMenuItem("Cut");
copyMenuItem = new JMenuItem("Copy");
pasteMenuItem = new JMenuItem("Paste");
cutMenuItem.addActionListener(this);
copyMenuItem.addActionListener(this);
pasteMenuItem.addActionListener(this);
this.add(cutMenuItem);
this.add(copyMenuItem);
this.add(pasteMenuItem);
heyListen = new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
show(e.getComponent(), e.getX(), e.getY());
}
}
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
show(e.getComponent(), e.getX(), e.getY());
}
}
};
}
private class JTCProperties {
public boolean canCut, canCopy, canPaste;
public JTCProperties(boolean canCut, boolean canCopy, boolean canPaste) {
this.canCut = canCut;
this.canCopy = canCopy;
this.canPaste = canPaste;
}
}
}

How to be notified that a composite's child received/lost focus?

I have an SWT Composite that I need to pass to some other code which will add children to it at will. Is there any way to be notified that a child of the composite received and lost focus?
Just to make sure it's clear, I cannot add listeners to each child, because I'm not in charge of creating those controls. A child could be added at any time.
As noted by Favonius, you can hook layout events like SWT.Resize to determine when you're being painted and recompute your child hierarchy, adding listeners as appropriate. Another option is simply to listen for all focus events and only pay attention to those that are for controls that you're interested in.
Displays have filters which, like listeners, are notified of events, however filters differ in that they are run before listeners, they have the opportunity to cancel events, and they are notified for all of a type of event on the entire Display.
You could thus use a Filter to examine all focus events and determine if it's one that you're interested in. For example:
public class MyControl extends Composite
{
private final Listener focusListener;
public MyControl(final Composite parent, final int style)
{
/* initialize the control... */
focusListener = new Listener()
{
public void handleEvent(Event event)
{
if (!(event.widget instanceof Control))
{
return;
}
boolean isOurChild = false;
for (Control c = (Control) event.widget; c != null; c = c.getParent())
{
if (c == container)
{
isOurChild = true;
break;
}
}
if (isOurChild)
{
System.out.println("Our child is " + (event.type == SWT.FocusIn ? "focused" : "unfocused"));
}
}
};
getDisplay().addFilter(SWT.FocusIn, focusListener);
getDisplay().addFilter(SWT.FocusOut, focusListener);
addDisposeListener(new DisposeListener()
{
public void widgetDisposed(DisposeEvent e)
{
getDisplay().removeFilter(SWT.FocusIn, focusListener);
getDisplay().removeFilter(SWT.FocusOut, focusListener);
}
});
}
}
Do note the javadoc for Display's warnings about using filters:
They should generally be avoided for performance, debugging and code maintenance reasons.
Obviously you're looking at performance trade-offs in either solution - depending on what type of application you're delivering and your users' workflow, it may make more sense to add focus listeners when you resize, or it may make more sense to simply listen to all focus events and ignore the ones you're not interested in.
Have you checked this link: SWT: notify a composite that it has a new child
As per the proposed solution in the above link the only possible solution is to use the resize event. Based on that see the following code, which adds a focus listener on all the immediate child nodes. Though the solution itself is not very elegant.
Test Code
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class SWTApplication
{
public static void main(String[] args) {
new SWTApplication().initSystem("Children Notification");
}
private Display display;
private Shell shell;
public void initSystem(String windowName)
{
display = new Display();
shell = new Shell(display);
shell.setText(windowName);
shell.setLayout(new GridLayout(6, true));
shell.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
final TestFocusListener listener = new TestFocusListener();
shell.addControlListener(new ControlListener() {
public void controlResized(ControlEvent e) {
if(e.getSource() instanceof Shell)
{
Shell s = (Shell)e.getSource();
Control[] children = s.getChildren();
for (int i = 0; i < children.length; i++)
{
Control c = children[i];
c.removeFocusListener(listener);
c.addFocusListener(listener);
}
}
}
public void controlMoved(ControlEvent e) {
}
});
createControls();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
private void createControls()
{
String[] name = {"a", "b", "c", "d", "e", "f"};
for(int i=0; i<6; i++)
{
Button button = new Button(shell, SWT.PUSH);
button.setText(name[i] + " button");
button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
shell.setSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
}
}
class TestFocusListener implements FocusListener
{
public void focusGained(FocusEvent e) {
Object src = e.getSource();
if(src instanceof Button)
{
System.out.println("Focus gained: " + ((Button)src).getText());
}
}
public void focusLost(FocusEvent e) {
Object src = e.getSource();
if(src instanceof Button)
{
System.out.println("Focus lost: " + ((Button)src).getText());
}
}
}
}

Do I need to explicitly dispose SWT Shells?

I have been told and have read that SWT Objects must be explicitly disposed by calling their dispose method. However, in my own testing with the following code, I have noted that at least Shells report themselves as disposed even though the dispose method is never called (nor appears) anywhere in my code.
import java.util.ArrayList;
import java.util.List;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class Test {
private static int numDisposals = 0;
private static List<Shell> shells = new ArrayList<Shell>();
public static void main(String[] args) {
Display d = Display.getDefault();
for (int i = 0; i < 3; i++) {
Shell s = new Shell(d);
shells.add(s);
s.setText(String.valueOf(i));
s.open();
s.addDisposeListener(new DisposeListener() {
#Override
public void widgetDisposed(DisposeEvent notUsed) {
numDisposals++;
printShellStatus();
}
});
}
while (numDisposals < 3) {
while (!d.readAndDispatch()) {
d.sleep();
}
}
printShellStatus();
}
public static void printShellStatus() {
System.out.println("Which shells are disposed?");
for (Shell shell : shells) {
if (shell.isDisposed()) {
System.out.println("I am disposed.");
} else if (!shell.isDisposed()) {
System.out.println("I am NOT disposed.");
}
}
}
}
So does Shell really need to be explicitly disposed? If so, how do you know when to dispose a Shell, and where should the dispose method appear?
The paper that you cite makes this clear:
Widgets themselves do not usually need to be disposed
programmatically. A shell and its children are disposed when the user
closes its window.
So while a shell does need to be disposed, the burden is not on you to do so. Nor do you need to call dispose on any of the children, as disposing a parent will do that for you. Again, from the link you cite:
When you dispose a Shell, its children are disposed. In fact,
disposing any Composite will dispose all of the Composite's children.
However, you do have to make sure that you dispose resources you create that are not children. For example: colors and fonts. You do explicitly need to call their dispose method. It's best to hook a dispose listener to the Composite you're using them in to do this. For example:
public class MyComposite extends Composite
{
private final Color color;
public MyComposite(Composite parent, int style)
{
super(parent, style);
color = new Color(getShell().getDisplay(), 255, 255, 255);
addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e)
{
color.dispose();
}
});
}
}
It's important to note, however, that you should not dispose Colors that you use but do not create. For example, do not dispose the system colors available from Display#getSystemColor().

Communication between JOptionPane buttons and a custom panel

I have made a multiple input dialog by building a JPanel with the fields I want and adding it to a JOption pane
JMainPanel mainPanel = new JMainPanel(mensaje, parametros, mgr);
int i = JOptionPane.showOptionDialog(null, mainPanel, "Sirena",
JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null,
new String[] {"Aceptar", "Cancelar"}, "Aceptar");
However I'm having trouble with the buttons, because some of the fields are required. How can I make the "Ok" button to be enabled once every required field is up, or making the click on the button to make the validations and do not close the pane until every required field is filled?
From the Java API, I found this:
options - an array of objects indicating the possible choices the user
can make; if the objects are components, they are rendered properly;
non-String objects are rendered using their toString methods; if this
parameter is null, the options are determined by the Look and Feel
So, can't I pass custom buttons as parameter?
Looks like I will have to make my own JDialog? for which case, I don't know how to make it return an int just like JOptionPane does, any recommended tutorial?
In the example options is {"Aceptar", "Cancelar"} which are the displayed buttons,
PS. I have full controll over the fields I added to the JPanel.
This is a screenshot of the JOptionPane:
I don't think that you can de-activate a JOptionPane's selections buttons, but one way to still use the JOptionPane is to simply re-display it if the required fields have not been set. You could display an error message JOptionPane first describing the error, and then display a new JOptionPane that holds the same JPanel as its second parameter -- so that the data already entered has not been lost. Otherwise, you may want to create your own JDialog which by the way isn't that hard to do.
Edit
I'm wrong. You can enable and disable the dialog buttons if you use a little recursion.
For example:
import java.awt.Component;
import java.awt.Container;
import java.awt.event.*;
import java.util.HashSet;
import java.util.Set;
import javax.swing.*;
public class Foo extends JPanel {
private static final String[] DIALOG_BUTTON_TITLES = new String[] { "Aceptar", "Cancelar" };
private JCheckBox checkBox = new JCheckBox("Buttons Enabled", true);
private Set<AbstractButton> exemptButtons = new HashSet<AbstractButton>();
public Foo() {
JButton exemptBtn = new JButton("Exempt Button");
JButton nonExemptBtn = new JButton("Non-Exempt Button");
add(checkBox);
add(exemptBtn);
add(nonExemptBtn);
exemptButtons.add(checkBox);
exemptButtons.add(exemptBtn);
checkBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
allBtnsSetEnabled(checkBox.isSelected());
}
});
}
private void allBtnsSetEnabled(boolean enabled) {
JRootPane rootPane = SwingUtilities.getRootPane(checkBox);
if (rootPane != null) {
Container container = rootPane.getContentPane();
recursiveBtnEnable(enabled, container);
}
}
private void recursiveBtnEnable(boolean enabled, Container container) {
Component[] components = container.getComponents();
for (Component component : components) {
if (component instanceof AbstractButton && !exemptButtons.contains(component)) {
((AbstractButton) component).setEnabled(enabled);
} else if (component instanceof Container) {
recursiveBtnEnable(enabled, (Container) component);
}
}
}
public int showDialog() {
return JOptionPane.showOptionDialog(null, this, "Sirena",
JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null,
DIALOG_BUTTON_TITLES, "Aceptar");
}
private static void createAndShowGui() {
Foo foo = new Foo();
int result = foo.showDialog();
System.out.println(DIALOG_BUTTON_TITLES[result]);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
This code uses listeners to check the state of a JCheckBox, but you can have listeners (DocumentListeners) listening to text field documents if you desire to know if they have data or not. The code then gets the JRootPane that holds the JCheckBox, then the root pane's contentPane, and all components of the dialog are held by this. It then recurses through all the components held by the dialog. If a component is a Container, it recurses through that container. If the component is an AbstractButton (such any JButton or checkbox), it enables or disables -- except for buttons held in the exempt buttons set.
A better example with document listeners
import java.awt.*;
import java.awt.event.*;
import java.util.HashSet;
import java.util.Set;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
public class Foo2 extends JPanel {
private static final String[] DIALOG_BUTTON_TITLES = new String[] {
"Aceptar", "Cancelar" };
private static final int FIELD_COUNT = 10;
private Set<AbstractButton> exemptButtons = new HashSet<AbstractButton>();
private JTextField[] fields = new JTextField[FIELD_COUNT];
public Foo2() {
setLayout(new GridLayout(0, 5, 5, 5));
DocumentListener myDocListener = new MyDocumentListener();
for (int i = 0; i < fields.length; i++) {
fields[i] = new JTextField(10);
add(fields[i]);
fields[i].getDocument().addDocumentListener(myDocListener);
}
// cheating here
int timerDelay = 200;
Timer timer = new Timer(timerDelay , new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
checkDocsForText();
}
});
timer.setRepeats(false);
timer.setInitialDelay(timerDelay);
timer.start();
}
private void checkDocsForText() {
for (JTextField field : fields) {
if (field.getText().trim().isEmpty()) {
allBtnsSetEnabled(false);
return;
}
}
allBtnsSetEnabled(true);
}
private void allBtnsSetEnabled(boolean enabled) {
JRootPane rootPane = SwingUtilities.getRootPane(this);
if (rootPane != null) {
Container container = rootPane.getContentPane();
recursiveBtnEnable(enabled, container);
}
}
private void recursiveBtnEnable(boolean enabled, Container container) {
Component[] components = container.getComponents();
for (Component component : components) {
if (component instanceof AbstractButton && !exemptButtons.contains(component)) {
((AbstractButton) component).setEnabled(enabled);
} else if (component instanceof Container) {
recursiveBtnEnable(enabled, (Container) component);
}
}
}
public int showDialog() {
return JOptionPane.showOptionDialog(null, this, "Sirena",
JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null,
DIALOG_BUTTON_TITLES, "Aceptar");
}
private class MyDocumentListener implements DocumentListener {
public void removeUpdate(DocumentEvent arg0) {
checkDocsForText();
}
public void insertUpdate(DocumentEvent arg0) {
checkDocsForText();
}
public void changedUpdate(DocumentEvent arg0) {
checkDocsForText();
}
}
private static void createAndShowGui() {
Foo2 foo = new Foo2();
int result = foo.showDialog();
if (result >= 0) {
System.out.println(DIALOG_BUTTON_TITLES[result]);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I suggest you to define some properties into your JPanel extended class, and use PropertyChangeListener to listen the occured changes and enable/disable relative buttons.
Here's an article.
Another issue maybe finding the ok/cancel buttons in the hierarchy of components, since the JDialog is created through JOptionPane and you haven't a reference to the buttons. Here's a useful thread .
You can add a property to a JComponent using putClientProperty method.
When changes occurs to a given property a PropertyChanged event is raised.
So in your example you can define a boolean property indicating that required that are inserted into the JDialog. Then add a PropertyChangeListener that when is notified enable/disable the ok button.

Categories

Resources