I know that there is another way to do this, but I prefer to see if anyone has an answer for doing it this way. The original way is to get the integer return value after calling the ShowDialog() function; I want to grab it from within the Event (shown below) itself. I've thrown some code in there to check if a file is selected, but I need to check whether the approval button (the OK button) has been selected before it gets there. Does anyone know how to check which button is pressed in a FileChooser dialog, using a setup like below?
Here's my code:
private void FileChooser_OpenMouseClicked(java.awt.event.MouseEvent evt) {
if(!FileChooser_Open.getSelectedFile().equals(null))
{
}
}
Original method:
//In response to a button click:
int returnVal = fc.showOpenDialog(aComponent);
The difference is that the original method gets a return value when the dialog box is closed, while in this method, I do not know of a way to get that return value (I believe the box has already closed, but there is nothing there to catch the return value). I apologize if I do not appear to be making a lot of sense.
edit: More information
So this is how I am creating the dialogue (a menu item, "Open" raises an event, which calls the showOpenDialog() method. As you can see, it is not capturing the return value (bear with me). Is it possible then to get the return value or at design a method to figure out whether the OK button is then pressed in the MouseClicked event? I guess I am trying to focus on an event-based programming style, where the code reacts according to the event raised, and grabbing the checking for the OK / approval button click inside the Open Menu event seems a little...disorganized from my perspective. Perhaps I am too used to C# / WinForm's approach to this problem?
private void MenuItem_OpenActionPerformed(java.awt.event.ActionEvent evt) {
FileChooser_Open.showOpenDialog(this);
}
private void FileChooser_OpenMouseClicked(java.awt.event.MouseEvent evt) {
if(!FileChooser_Open.getSelectedFile().equals(null))
{
}
}
If I understand the question, you might be able to use a JFileChooser#approveSelection() method:
OTN Discussion Forums : How to react on events fired by a JFileChooser?
java - JFileChooser with confirmation dialog - Stack Overflow
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
public class ApproveSelectionDemo {
public JComponent makeUI() {
final JPanel p = new JPanel();
final JFileChooser fileChooser = new JFileChooser() {
#Override public void approveSelection() {
if(!getSelectedFile().exists()) {
int returnVal = JOptionPane.showConfirmDialog(
this, "message", "title", JOptionPane.YES_NO_OPTION);
if(returnVal!=JOptionPane.YES_OPTION) {
return;
}
}
super.approveSelection();
}
};
p.add(new JButton(new AbstractAction("Open") {
#Override public void actionPerformed(ActionEvent e) {
int retvalue = fileChooser.showOpenDialog(p);
if(retvalue==JFileChooser.APPROVE_OPTION) {
System.out.println(fileChooser.getSelectedFile());
}
}
}));
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new ApproveSelectionDemo().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Related
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
class Demo implements ActionListener
{
JFrame f;
JButton b;
DisplayDialog dialog;
public Demo()
{
f = new JFrame();
f.setSize(200,200);
b = new JButton("Click me");
f.add(b);
dialog = new DisplayDialog();
b.addActionListener(this);
f.setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
Object o = e.getSource();
if(o==b)
{
dialog.display("Hello");
dialog.display("Hello");
dialog.display("Hello");
dialog.display("Hello");
dialog.display("Hello");
}
}
public static void main(String args[])
{
Demo d = new Demo();
}
class DisplayDialog implements ActionListener
{
JDialog dg;
JLabel l;
JButton b;
Font myfont;
public DisplayDialog()
{
dg = new JDialog(f,"Alert!",true);
dg.setSize(300,150);
l = new JLabel("Message");
b = new JButton("OK");
myfont = new Font("Serif",Font.BOLD,12);
l.setFont(myfont);
dg.add(l);
dg.add(b,"South");
dg.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
b.addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{
Object o = e.getSource();
if(o==b)
{
dg.setVisible(false);
}
}
public void display(String str)
{
if(dg.isVisible())
dg.setVisible(false);
l.setText(str);
dg.setVisible(true);
}
}
}
When the button is clicked, 5 Dialog box appears. Now I want to make such a change in the DisplayDialog class so that every time display method is called,the previously opened dialogs are closed and the latest one is the only one that is visible.
I tried doing this :
public void display(String str)
{
if(dg.isVisible())
dg.setVisible(false);
l.setText(str);
dg.setVisible(true);
}
but it didn't do anything.
Also,I am very surprised that multiple dialog boxes are appearing even though there is only one instance of the object. From my understanding, setVisible(true) should not do anything if the dialog is already visible.
This whole situation is very confusing to me.
The thing is that you cannot do this in single thread (like you are preseting it in your demo) - setVisible will always block (wait till dialog closes) and another display call will not be invoked till then.
Try to run it in multi frame env (like you have stated that your app is) but change display to
public void display(String str){
l.setText(str);
if(dg.isVisible()){
dg.setVisible(true);
}
}
This way dialog will not be displayed if it is already visible, but text will be changed. The condition for it to work is that is is not consecutinve calls to display like in demo, but from different threads. There is no other way around. setVisible will alwys block.
Okay, so here's the deal. Currently, I am using this:
String[] choices = {"Rock", "Paper", "Scissors"};
String input = (String) JOptionPane.showInputDialog(null, "Please, make your choice", "Rock Paper Scissors!", JOptionPane.QUESTION_MESSAGE, null, choices, choices[0]);
Which is what I need. It creates a drop down menu that allows the user to select Rock, Paper, or Scissors and then outputs it into a String. The problem is, the window that it pops in is REALLY small, and is in the center of the screen. I want to re-size it to be 970 pixels by 300 pixels, and to appear at the location of 950 pixels and 0 pixels.
Now, before you say it, I HAVE tried to use JFrames for this, because I know how to get it the size and at the location I want it. However, I can't get the ActionListener to behave in the way that I want it to.
public static void main(String args[]) throws IOException
{
JFrame hi = new JFrame("Hi");
hi.setSize(970, 300);
hi.setLocation(950, 0);
System.out.println("Hi");
Picture Hi = new Picture("c:/The Game/Cool.png");
Hi.display();
JButton B = new JButton("Hey There!");
hi.add(B);
int c = Output(hi);
}
public int Output(JFrame b)
{
int j = 0;
j = //CODE NEEDED HERE
return j;
}
#Override
public void actionPerformed(ActionEvent arg0) {
}
So, the problem with this is that I need the JFrame to pop up in then "CODE NEEDED HERE" section, and then, upon clicking the button, to return a certain value, and then to close out of the JFrame. However, the JFrame doesn't wait for the Output() function, and it immediately returns j, which is equal to 0. Instead, it just does whatever is in the actionPerformed function.
So, I am asking for a solution to either one of these problems. How to either re-size the JOptionPane.showInputDialog() or to get the JFrame to return an int value upon clicking a button.
Sorry if this is really poorly explained, I'm really new to JOptionPane and JFrames.
JOptionPane is quite configurable, it's also nicely self contained, taking a lot of the repetitive, boil plate code and hiding it away in an easy to use package.
But that doesn't mean you have to use it that way, you can simply create an instance of JOptionPane, which is just an ancestor of JComponent and add it to what ever you want.
The difficulty is plumbing all the functionality back together, so you can respond to the buttons, for example.
Just beware, your example places the dialog under my task bar (yes, mine is at the top of the screen), so I can tell you, as a user, that will annoy me!
So, this example basically wraps up all the boiler plate code into a simple class/method, which makes it easy to repeatedly prompt the user the same question, over and over again...
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import static javax.swing.JOptionPane.OK_CANCEL_OPTION;
import static javax.swing.JOptionPane.UNINITIALIZED_VALUE;
import static javax.swing.JOptionPane.VALUE_PROPERTY;
import javax.swing.border.EmptyBorder;
public class Test {
public static void main(String[] args) {
String pick = Picker.pick();
System.out.println("You picked " + pick);
System.exit(0);
}
public static class Picker {
public static String pick() {
String[] choices = {"Rock", "Paper", "Scissors"};
JOptionPane pane = new JOptionPane("Please, make your choice", JOptionPane.QUESTION_MESSAGE,
OK_CANCEL_OPTION, null, null, null);
pane.setWantsInput(true);
pane.setSelectionValues(choices);
pane.setInitialSelectionValue(choices[0]);
JDialog dialog = new JDialog();
dialog.setModal(true);
PropertyChangeListener listener = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
// Let the defaultCloseOperation handle the closing
// if the user closed the window without selecting a button
// (newValue = null in that case). Otherwise, close the dialog.
if (dialog.isVisible()
&& (event.getPropertyName().equals(VALUE_PROPERTY))
&& event.getNewValue() != null
&& event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE) {
dialog.setVisible(false);
}
}
};
WindowAdapter adapter = new WindowAdapter() {
private boolean gotFocus = false;
public void windowClosing(WindowEvent we) {
pane.setValue(null);
}
public void windowClosed(WindowEvent e) {
dialog.removePropertyChangeListener(listener);
dialog.getContentPane().removeAll();
}
public void windowGainedFocus(WindowEvent we) {
// Once window gets focus, set initial focus
if (!gotFocus) {
pane.selectInitialValue();
gotFocus = true;
}
}
};
dialog.addWindowListener(adapter);
dialog.addWindowFocusListener(adapter);
dialog.addComponentListener(new ComponentAdapter() {
public void componentShown(ComponentEvent ce) {
// reset value to ensure closing works properly
pane.setValue(JOptionPane.UNINITIALIZED_VALUE);
}
});
pane.addPropertyChangeListener(listener);
dialog.add(pane);
//dialog.pack();
//dialog.setLocationRelativeTo(null);
dialog.setSize(970, 300); // This is bad idea, use an EmptyBorder instead
dialog.setLocation(950, 0);
dialog.setVisible(true);
String pick = null;
Object value = pane.getInputValue();
if (value != UNINITIALIZED_VALUE) {
pick = value.toString();
}
return pick;
}
}
}
The reason you're having problems with JFrame is because it's not designed to block the code execution when displayed, a JDialog can, see How to Make Dialogs for more details
I have figured out how to filter by file-type, but I cannot figure out how to filter out files as I type on my keyboard in the file-name textfield. If I have 1000 files in my directory and two of them start with the letter q then I expect that when I type the word q in my JFileChooser it will filter out all files that do not start with q and this should continue as I type and the best possible match should be selected. Is there no exemplar for this? It seems like every JFileChooser question and tutorial out there is only concerned with filtering by file-type and not by name.
This works (for Metal LAF). Although it is a terrible solution in itself, the other options are not more appetizing.
public class FilterChooser {
JFileChooser chooser = new JFileChooser();
JTextField tf = (JTextField) ((JPanel) ((JPanel) chooser.getComponent(3)).getComponent(0)).getComponent(1);
FilterChooser() {
tf.getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent e) {
modifyFilter();
}
public void removeUpdate(DocumentEvent e) {
modifyFilter();
}
public void changedUpdate(DocumentEvent e) {
modifyFilter();
}
});
JFrame f = new JFrame();
chooser.showOpenDialog(f);
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
void modifyFilter() {
final String text = tf.getText();
chooser.setFileFilter(new FileFilter() {
#Override
public String getDescription() {
return null;
}
#Override
public boolean accept(File f) {
return (f.isDirectory() || f.getName().startsWith(text));
}
});
}
public static void main(String[] args) {
new FilterChooser();
}
}
Notes and explanations:
The text field in a JFileChooser is declared in the MetalFileChooserUI class as a private field, hence no easy way to reach it. I fetch it into tf the way I do because I did my homework and checked where it is located - if the layout changes (won't happen in the next many years due to backwards compatibility) this will break. You can alternatively recursively iterate through child components.
I set the filter for files only. If you want this to apply to directories, change the accept method of the FileFilter.
The filter is case-sensitive as it is currently implemented. You can modify this behavior by changing the accept method of the FileFilter.
I added the empty frame just so the JVM will exit when you close it (the file chooser, being a dialog, does not do this).
Depending on the bigger picture, set the modifiers of the fields and methods to your needs.
I imagine that implementing a full-scale auto-complete for a JFileChooser might not be trivial. You would need access to the JTextField of the chooser, but that isn't readily available (though not impossible). You could then apply a custom DocumentFilter to that field, and then auto-populate (and partial select the back end of the word) as the user types.
Further reading:
http://docs.oracle.com/javase/tutorial/uiswing/components/generaltext.html#filter
However, that seems like a lot of work.
Instead, are you aware that JFileChooser allows you to create a filter on-the-fly? Just bring up the chooser, type in 'q*', hit ENTER, and the chooser will show only the files that begin with 'q'.
I have the behavior that I want now. I still cannot believe what a struggle this was, but I guess nobody in the Swing community ever saw it fit to have the JFileChooser have this behavior or have better hooks for implementing it. Thank you user1803551 for your answer, I am accepting your answer and I posted mine just to show how I tweaked yours to get my exact desired behavior.
public void setupListeners()
{
JFileChooser chooser = new JFileChooser();
JTextField fileChooserTextField = (JTextField) ((JPanel) ((JPanel) chooser.getComponent(3)).getComponent(0)).getComponent(1);
chooser.addKeyListener(new KeyListener()
{
#Override
public void keyTyped(KeyEvent e)
{
}
#Override
public void keyReleased(KeyEvent e)
{
filterAsYouType(fileChooserTextField, chooser);
}
#Override
public void keyPressed(KeyEvent e)
{
}
});
}
private void filterAsYouType(final JTextField tf, final JFileChooser chooser)
{
final String text = tf.getText();
chooser.setFileFilter(new FileFilter()
{
#Override
public boolean accept(File f)
{
if(text.equals("")
{
return true;
}
if(f.getName().equals(text))
{
chooser.setSelectedFile(f);
tf.setCaretPosition(text.length());
}
if(f.getName().startsWith(text))
{
return true;
}
return false;
}
#Override
public String getDescription()
{
return null;
}
});
}
}
Basically, I have a dropdown menu containing templates. For example:
apple( )
banana( )
Once one of them is selected, it pastes onto a JTextArea. My problem is if "apple( )" is selected, I want "apple" and the two brackets non-deletable in the TextArea, and user can enter anything inside the brackets.
Can anyone give me any direction/ideas here? I have been searching on the internet and found very little about this.
Check out the Proctected Text Component. It allows you to mark individual pieces of text as protected so that it can't be changed or deleted.
It uses a DocumentFilter as well as a NavigationFilter.
For a simpler solution you might be able to just use a NavigationFilter. The example below shows how you can prevent the selection of text at the beginning of the Document. You should be able to customize it to also prevent selection of text at the end of the document as well.
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
public class NavigationFilterPrefixWithBackspace extends NavigationFilter
{
private int prefixLength;
private Action deletePrevious;
public NavigationFilterPrefixWithBackspace(int prefixLength, JTextComponent component)
{
this.prefixLength = prefixLength;
deletePrevious = component.getActionMap().get("delete-previous");
component.getActionMap().put("delete-previous", new BackspaceAction());
component.setCaretPosition(prefixLength);
}
#Override
public void setDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
{
fb.setDot(Math.max(dot, prefixLength), bias);
}
#Override
public void moveDot(NavigationFilter.FilterBypass fb, int dot, Position.Bias bias)
{
fb.moveDot(Math.max(dot, prefixLength), bias);
}
class BackspaceAction extends AbstractAction
{
#Override
public void actionPerformed(ActionEvent e)
{
JTextComponent component = (JTextComponent)e.getSource();
if (component.getCaretPosition() > prefixLength)
{
deletePrevious.actionPerformed( null );
}
}
}
private static void createAndShowUI()
{
JTextField textField = new JTextField("Prefix_", 20);
textField.setNavigationFilter( new NavigationFilterPrefixWithBackspace(7, textField) );
JFrame frame = new JFrame("Navigation Filter Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(textField);
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowUI();
}
});
}
}
You'll have to do this yourself. I'd suggest you to make an event handler that fires every time the text changes (Click here to find out how). And inside that handler check if the JTextArea still starts with "apple(" and ends with ")".
There's a text field and when lost focus it will validate the inputs, if not passed, print out the error message (to be simple here just has an empty check). And there's a button next to the text field, it will print out the text once click on it.
As I tried, when input some text and then click the button it will trigger both the focus lost event of text field and the event of button. In a other word, it will do the validation first and then print out the input text.
Here comes my question, what is the good approach to prevent printing out the text if the validation not passed? Or is there a way to "ignore" the click event on button if validation not passed?
I tried to use a boolean flag which indicate the validation result and check the flag when perform the action for button, but I do not think it is a good approach. As I know there's an event dispatcher thread in Swing which deal with the events, is it possible I can cancel the events from here?
Below is a piece of code which explain the question:
public class SimpleDemo
{
public static void main(String[] args)
{
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel content = new JPanel(new FlowLayout());
frame.setContentPane(content);
final JTextField textField = new JTextField(10);
textField.addFocusListener(new FocusAdapter()
{
#Override
public void focusLost(FocusEvent e)
{
String text = textField.getText();
// do some validation here, if not validated
// do not trigger the event on button.
if ("".equals(text))
{
System.out.print("please input a text!");
}
}
});
content.add(textField);
JButton button = new JButton("Print Text");
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
// action performed for button
String text = textField.getText();
System.out.println(text);
}
});
content.add(button);
frame.setVisible(true);
frame.pack();
}
}
I faces similar issue while working on an application. I solved it like below
I created a abstract class ApplicationFrame which every frame in the application extends
public abstract class ApplicationFrame extends JFrame implements ActionListener {
#Override
final public void actionPerformed(ActionEvent event) {
if(validateInput()){
performAction(event);
}
}
/*
* Sub class should override this method to receive any action
*/
protected void performAction(ActionEvent event) {};
/*
* Sub class should override this method to perform validation
*/
abstract protected boolean validateInput();
}
All Frames will now extend this base frame, as below:
public class Frame1 extends ApplicationFrame{
#Override
protected void performAction(ActionEvent event) {
// perform action
}
#Override
protected boolean validateInput() {
// return true or false depending upon the validation results
}
// if you want to add Action Listener, you need to add like this:
btnSomeButton.addActionListener(this);
}
If you need to handle Focus events, you can make ApplicationFrame or the base frame implement FocusListener.
This is my custom implementation to solve the problem, hope this helps.
Make the button disabled on start-up
Upon lost focus, validate the text & enable button only when the input passes validation.
Upon start of text change, disable the button
It's always makes sense to make ui to communicate with user. So you can show "please input a text" as the default text of the textField when nothing is entered by user.
Here is the code for such custom textField:
public class TextFieldWithDefaultText extends JTextField implements FocusListener{
private final String hint;
public TextFieldWithDefaultText (String $text)
{
super($text);
this.hint = $text;
addFocusListener(this);
}
#Override
public void focusGained (FocusEvent $e)
{
if (this.getText().isEmpty())
{
super.setText("");
}
}
#Override
public void focusLost (FocusEvent $e)
{
if (this.getText().isEmpty())
{
super.setText(hint);
}
}
#Override
public String getText ()
{
String typed = super.getText();
return typed.equals(hint) ? "" : typed;
}
}
Write the acttionListerner for your button like this:
JButton button = new JButton("Print Text");
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
if(!textField.getText().isEmpty())
System.out.println(textField.getText());
}
});
And ur textField implementation should be :
final TextFieldWithDefaultText textField = new TextFieldWithDefaultText ("please input a text");
Hope this helps :)