user2649681
user2649681

Reputation: 848

JFileChooser.showSaveDialog() not showing up

I'm using the Eclipse IDE, and I'm trying to call showSaveDialog(null) from another method which is in turn called from main, but nothing happens when I call it.

Main:

public static void main(String[] args) {
    Main main = new Main();
    main.pt = main.new PlayThread();
    main.generateSound(getSamples(2), 500);
    main.play(main.sound, 1);
    if(new Scanner(System.in).nextLine().equals("save")){
        System.out.println(main.save());
    }
}

All the code before calling main.save() works fine, and main.save() starts like this:

public boolean save(){
    System.out.println("Calling save");
    try{
    String saveName = getSaveTo("C:/");
    System.out.println("I'm here now");

while getSaveTo() looks like:

public String getSaveTo(String def){
    JFileChooser chooser = new JFileChooser();
    System.out.println("Save called");
    //chooser.setCurrentDirectory(new File(def));
    int resp = chooser.showSaveDialog(null);
    if(resp == JFileChooser.APPROVE_OPTION){
        return chooser.getSelectedFile() + ".wav";
    }else return null;
}

After it executes the first few functions in main, I type in 'save', and the console prints:

Calling save
Save called

But the dialog never opens, and it never says "I'm here now." Why's that? Also, when I typed

new JFileChooser().showSaveDialog(null)

That shows up fine, but in my save() method, it won't. Any ideas why?

EDIT:

Here's a condensed program which has the same problem:

package com.funguscow;

import java.util.Scanner;

import javax.swing.JFileChooser;

import com.funguscow.Main.PlayThread;

public class Test {
public static void main(String[] args) {
    Test main = new Test();
    Scanner scan = new Scanner(System.in);
    boolean should = scan.nextLine().equals("save");
    scan.close();
    if(should){
        System.out.println(main.save());
    }
}

public String getSaveTo(String def){
    JFileChooser chooser = new JFileChooser();
    System.out.println("Save called");
    //chooser.setCurrentDirectory(new File(def));
    int resp = chooser.showSaveDialog(null);
    System.out.println("Save called");
    if(resp == JFileChooser.APPROVE_OPTION){
        return chooser.getSelectedFile() + ".wav";
    }else return null;
}

public boolean save(){
    System.out.println("Calling save");
    try{
    String saveName = getSaveTo("C:/");
    System.out.println("I'm here now");
    if(saveName == null)return false;
    }catch(Exception e){}
    return false;
}
}

You can run this yourself if you'd like to examine it more.


This MCVE may be enough to reproduce the problem, and it seems that the Scanner that is initialized with System.in is interfering with the JFileChooser's ability to display an open file dialog, even when care is taken so that the file chooser is run on the Swing event thread:

import java.util.Scanner;    
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;

public class Test3 {
   public static void main(String[] args) {
      Scanner scan = new Scanner(System.in);
      System.out.print("Enter something and press Enter: ");
      scan.nextLine();
      scan.close();

      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            JFileChooser fileChooser = new JFileChooser();
            int result = fileChooser.showOpenDialog(null);
         }
      });
      // JFileChooser fileChooser = new JFileChooser();
      // int result = fileChooser.showOpenDialog(null);
   }
}

Upvotes: 7

Views: 4359

Answers (2)

J Richard Snape
J Richard Snape

Reputation: 20344

I believe the answer by vaxquis to be quite correct in that the chooser is being shown, just behind the current window as it retains focus. It's all to do with Dialog modality and which windows take precedence over others on the various OS's (possibly in combination with some IDE's).

I have found the following workaround - keeping all your other code exactly the same, extend JFileChooser and then use MyFileChooser instead of JFileChooser in the getSaveTo() method. I've tested on my setup (Windows 8, Java 7). YMMV

class MyFileChooser extends JFileChooser {
    protected JDialog createDialog(Component parent) throws HeadlessException {
        JDialog dialog = super.createDialog(parent);
        dialog.setAlwaysOnTop(true);
        return dialog;
    }
}

This allows you to use the original construction (.openSaveDialog(null)) and has the nice added advantage that you can set up other features of the dialog box in your overridden createDialog() method should you wish.

EDIT

I see vaxquis has appended this method to their answer now too, so that answer is, I would say, canonical.

Upvotes: 2

user719662
user719662

Reputation:

On Windows, Scanner interferes w/ JFileChooser -- why?

tl;dr strictly speaking, it's the user that interferes with JFileChooser by using a Scanner expecting input from system console (System.in). Either way, it's just about the windowing focus and Java's Dialog modality.

Explanation

The bug happens because the dialog appears in the background because requiring nextLine() on the Scanner on System.in essentially forces the user to switch the focus to the console. The application loses the focus, so the Dialog appears in the background. The code doesn't "hang" by itself, it just waits until the user selects a file or does any other dialog option - until he does, it does nothing. If there's some kind of OS issue blocking the background window from displaying/working properly (e.g. some "always in foreground" window obstructing it) - well, then your app is hanging around, waiting for input event that's not likely to happen due to the fact nobody can use the input dialog itself.

For anybody affected - the Dialog is there. I've tested this code on Java 8 on Windows XP, Java 8 on Windows 7 and some other configurations - in all of the cases the Dialog was created, possibly hidden in the background of the desktop due to focus loss. It's not shown in the taskbar, but it does show in the Alt+Tab list. Try running it outside of IDE, they tend to do strange things with app focus when console input is required (due to "real" console getting redirected to IDE output window etc). Feeding it data through the < pipe instead of using "real" console will propably prevent this behaviour from happening too.

As far as my imagination goes, workarounds would require either forcing the focus to the app's Frame or creating an on-the-fly always-on-top Frame to keep the focus where it should be (on the Dialog). Since the Dialog of JFileChooser (or any other similar Dialog to speak of) is itself a fire-and-forget object, it's kind of difficult to force it to foreground without specifying a parent. Therefore, I think that the easiest way to go, for parent-less Dialogs is to just use code like:

Exemplary solution

    Scanner scan = new Scanner( System.in );
    System.out.print( "Enter something and press Enter: " );
    scan.nextLine();
    scan.close();

    SwingUtilities.invokeLater( new Runnable() {
        public void run() {
            JFrame jf = new JFrame( "Dialog" ); // added
            jf.setAlwaysOnTop( true ); // added
            JFileChooser fileChooser = new JFileChooser();
            int result = fileChooser.showOpenDialog( jf );  // changed
            //System.out.print( "result: " + result );
            jf.dispose(); // added
        }
    } );

or, if you prefer to interfere with the Dialog itself, by subclassing it and applying the aforementioned setAlwaysOnTop(true) in the createDialog() call (note that createDialog() has protected access in JFileChooser, making it impossible to change the behaviour without extending the class, similarly to all the Dialog related stuff there), e.g.

int result = new JFileChooser() {
  @Override
  protected JDialog createDialog( Component parent ) throws HeadlessException {
    JDialog jDialog = super.createDialog( parent );
    jDialog.setAlwaysOnTop( true );
    return jDialog;
  }
}.showOpenDialog( null );

or even by creating a regular utility class extending JFileChooser doing just that.

NB switching to/fro EDT does nothing here, as this is not threading related by itself (though the multithreading of UI is the source of the problem, kind of).

Upvotes: 5

Related Questions