JavaFX FileChooser initial directory - java

In Swing, the JFileChooser pointed to the user's default directory which is typically the "My Documents" folder in Windows. The JavaFX FileChooser does not have the same behavior by default. There is a setInitialDirectory method which should be fine, however there are a number of places in the application that we open FileChoosers. Unfortunately the FileChooser class is final, so I cannot simply extend the class and just call the setInitialDirectory once. Is there anything else I could do besides going through the entire application and adding the setInitialDirectory calls?

There's the obvious solution, to just create a static utility method somewhere:
public class MyUtilities {
public static FileChooser createFileChooser() {
FileChooser chooser = new FileChooser();
chooser.setInitialDirectory(new File(System.getProperty("user.home"));
return chooser ;
}
}
Then you can just do
FileChooser chooser = MyUtilities.createFileChooser();
whenever you need one.
I actually prefer, from a user experience perspective, to use a single FileChooser instance for the whole application (or at least for each functional portion of a large application). That way it maintains the last directory the user visited, which is more convenient imho.

Related

PathMustExists and FileMustExists in SWT FileDialog

To date I only used Swing to build graphical user interfaces but now I also want to make myself familiar with the Standard Widget Toolkit.
I already read the documentation and built a simple app.
My problem is now to use the FileDialog component.
I did the following code:
FileDialog openFileDialog = new FileDialog(shell, SWT.OPEN);
openFileDialog.setFilterExtensions(new String[] { "*.txt" });
openFileDialog.setFilterNames(new String[] { "Text files (*.txt)" });
openFileDialog.setText("Open file");
openFileDialog.open();
But I found no methods to set flags like "PathMustExists" or "FileMustExists".
Is this not possible with FileDialog?
Do I have to extend the class to implement that functionality? If so, how I have to proceed?
Or this there a better OpenFileDialog component (maybe in JFace) from which I don't know?
There are no options for this.
Since you are specifying SWT.OPEN you will get a file dialog specialized for opening existing files. Depending on which platform you are running on this dialog may not allow non-existent files to be selected at all (certainly true on Mac OS X). Still you should check the file after the dialog returns.

Create new folder with user-defined name and location in java

I am using JavaFX to write a script, part of which involves the user selecting an existing project, or creating a new one. In the latter case I have a popup window that prompts the user for a name and a place to save the project. What I hope to do is create a folder in the place that the user specifies with the same name as the project, from here I will save different items to it and load them later. However when I use filechooser to have the user pick the location for the folder it requires them to select an item before the chooser actually picks anything. Then the folder that it makes has the name of this item in the beginning. (For example if I picked the Chrome shortcut on my desktop the folder name would be chrome.exeProject1 or something like that)
I know part of the problem is because I reused part of my filechooser code for a different function where I required the user to have an item selected before they could submit it, anyway...
Is there any way for the user to select a folder, or just submit the filechooser without selecting any items and create the new folder here? (Inside the selected folder or in the current folder in the latter case).
relevant code below, any help is appreciated. Thanks!
#FXML
private void submitName(ActionEvent event) {
name = nameOfProject.getText();
if (name != null && location != null)
{
new File(location + name).mkdir();
}
}
#FXML
private void createProjectDirectory(ActionEvent event) {
FileChooser projectDirectory = new FileChooser();
projectDirectory.setTitle("Create Project Directory:");
File refLocation = projectDirectory.showOpenDialog(null);
if (refLocation != null) {
locationOfProject.clear();
location = refLocation.getPath();
locationOfProject.appendText(location);
}
}
You have to use DirectoryChooser instead of FileChooser to let the user select a directory.
You can read the documentation on how to use these classes here (it's from JavaFX 2, but I guess the interface is mostly the same).

JFileChooser in LibGDX

I'm trying to use Javas JFileChooser in my LibGDX scene2d project, but as soon as I launch JFileChooser my program freezes.
Here is the code I use to launch file chooser:
private String getPath(){
String path = "";
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int returnVal = fc.showOpenDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
file = fc.getSelectedFile();
try {
path = file.getAbsolutePath();
} catch (Exception ex) {
System.out.println("problem accessing file" + file.getAbsolutePath() + "\n" + ex.getMessage());
}
} else {
System.out.println("File access cancelled by user.");
}
return path;
}
is it swing and libgdx compability problem or is there something I am missing? Same code works perfectly with nativa java projects.
Except instead of: fc.showOpenDialog(null);
I use: fc.showOpenDialog(button); // button is the JButton that triggers the event.
any idea what am I doing wrong?
EDIT: I don't really mind if it wont work on other platforms than Windows.
BUT if I choose to go with cross platform solution, and use LibGDX's method, do I have to create file chooser class with UI from scratch all by myself?
Ok based on your comments from the answer above I get a sense that what you are trying to do is invoke a swing window INSIDE your LibGDX game window, which is an open GL rendering scene.
Let me stop you right there. The swing toolkit invokes its own rendering engine, because it's not intended for this purpose at all - it's intended for desktop applications. So when you instantiate the dialogue, all sorts of other oracle java stuff gets instantiated along with it, like the Graphics2D class. You can't just add this class to a scene2D stage and expect that it draws. They don't implement the same interfaces or inherit from the same base classes. The draw(Graphics2D graphics) method that your JFileChooser implements is not the same as whatever draw(SomeClass foo) method that your libGDX classes implement.
So if you want to make a file chooser window, you need to start looking at the libGDX widget libraries. There might be something that someone has put together already, but my approach for my next libGDX project is going to be to extend these classes for my own UI libraries. I don't know what your project is, or what your timeline is like, but it's certainly a better approach then trying to adapt the swing toolkit to render in an OpenGL rendering scene.
edit
After some quick reading, I'm going to go one further and hazard a guess that the way the swing toolkit gets rendered is entirely dependent on the implementation of the JVM for a specific platform. Now this is where my CS knowledge starts to be a little limited, but I would hazard another guess that this is way way different than the LWJGL implementation of OpenGl by way of using Java wrappers for C libraries.
Personally I dislike the existing FileChooser UIs inside LibGDX. So I created a solution which works using the JFileChooser. Here is some quick and dirty code:
new Thread(new Runnable() {
#Override
public void run() {
JFileChooser chooser = new JFileChooser();
JFrame f = new JFrame();
f.setVisible(true);
f.toFront();
f.setVisible(false);
int res = chooser.showSaveDialog(f);
f.dispose();
if (res == JFileChooser.APPROVE_OPTION) {
//Do some stuff
}
}
}).start();
This will open the FileChooser in front of the LibGDX window without blocking the main Thread. Just tested this on Windows 7, 8, 10 and it only works in window mode ofc.
Coming late to the party but if the point of the question is to invoke a "native" ie. non-gdx file chooser from a libgdx project I made a library to do so here: https://github.com/spookygames/gdx-nativefilechooser.
Example from the readme:
// Configure
NativeFileChooserConfiguration conf = new NativeFileChooserConfiguration();
// Starting from user's dir
conf.directory = Gdx.files.absolute(System.getProperty("user.home"));
// Filter out all files which do not have the .ogg extension and are not of an audio MIME type - belt and braces
conf.mimeFilter = "audio/*";
conf.nameFilter = new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return name.endsWith("ogg");
}
};
// Add a nice title
conf.title = "Choose audio file";
fileChooser.chooseFile(conf, new NativeFileChooserCallback() {
#Override
public void onFileChosen(FileHandle file) {
// Do stuff with file, yay!
}
#Override
public void onCancellation() {
// Warn user how rude it can be to cancel developer's effort
}
#Override
public void onError(Exception exception) {
// Handle error (hint: use exception type)
}
});
On the desktop, this example will currently launch an AWT FileDialog (not exactly what is asked) yet a Swing version is currently on the master branch and should be incorporated to the next version of the lib.

JFileChooser: Cannot select Desktop when Selection Mode is File and Directories

I ran into an issue with JFileChooser and wanted to see if there is a workaround.
If the JFileChooser is created and the setFileSelectionMode is FILES_AND_DIRECTORIES, when a user clicks a shortcut button on the left (in XP) such as Desktop or My Documents or drop down to Desktop, the field is not placed in the File Name JTextPane. And when clicking the "Select/Accept" button, nothing happens (because isDirectorySelected() returns false for some reason).
Overriding the approveSeletion does not work because the Event Handler function in BasicFileChooser does not call it.
How would I make it so the Desktop can be selected without having to navigate to it manually, but by clicking the shortcut on the left?
Thanks
In Windows, the desktop is not backed by any file in the file system - it's a shell namespace. So there really isn't anything that JFileChooser could return to you. Yes, I know that there is a folder that contains the desktop for the user - but remember that the desktop actually displays as a composite of the user's desktop and the All Users desktop folder - plus other things that are added by the shell but not part of any folder (like the trash bin). So returning a File object that represents the 'desktop' is pretty much a non-starter.
Long and short: Ask yourself why you need to do this - chances are that you are going to wind up deep into native code territory, dealing with namespace PIDLs and all sorts of nastiness that you may not want to get into (for the life of me, I cannot understand why M$ had to make this stuff so amazingly difficult to use)...
Here's an intro to Windows shell namespaces so you'll have a feel for what's involved:
http://msdn.microsoft.com/en-us/library/cc144090%28v=vs.85%29.aspx
Found the following code in the BasicFileChooserUI:
if (fc.getFileSelectionMode() == JFileChooser.FILES_AND_DIRECTORIES
&& fc.getFileSystemView().isFileSystem(dir)) {
setFileName(dir.getAbsolutePath());
}
So it looks like "special folders" are purposely ignored. The code is in a private method so it would be hard to create you own UI.
As a hack you might be able to add a PropertyChangeListener to the file chooser:
public void propertyChange(final PropertyChangeEvent e)
{
String prop = e.getPropertyName();
if (JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals(prop))
{
JFileChooser fileChooser = (JFileChooser)e.getSource();
File currentDirectory = (File)e.getNewValue();
String directory = currentDirectory.toString();
if (directory.endsWith("Desktop")
|| directory.endsWith("My Documents"))
{
File selectedFile = fileChooser.getSelectedFile();
if (selectedFile == null || ! selectedFile.equals(currentDirectory))
{
fileChooser.removePropertyChangeListener( this );
fileChooser.setSelectedFile( currentDirectory );
fileChooser.addPropertyChangeListener( this );
}
}
}
}

altering JFileChooser behaviour : preventing "choose" on enter in file path JTextField

Greetings to Swing Pros, here's a good (I hope) question for you.
The below are the task requirements and possible solutions I see. I would like someone having had such a hardcore experience to share some thoughts about this.
This does not require coding or anything like that, I just need general advice as to which approach is more reliable regarding the fact I need to work with private symbols which reside in sun.swing and/or javax.swing.plaf packages.
The task is to modify/alter JFileChooser behaviour (just a little bit, actually).
when the user presses enter in the file name JTextField, and the field contains a path to a dir, don't "select" the dir, but switch to it instead. Yes, the dialog is configured to accept directories, but we need to accept only clicks on the "Open" button, and (possibly) double-clicks in the file listing table.
prevent user from selecting a dir/file with more than 1GB data via hitting enter in the file name text field
Here're couple of general solution options:
a. listen on the property-based changes that JFileChooser provides (which AFAICS are triggered after-the-fact and won't provide the degree of control we need here).
b. tinker with the javax.swing.plaf.basic.BasicFileChooserUI (via refrection, breaking the private-level encapsulation) and alter the reference to
private Action approveSelectionAction = new ApproveSelectionAction();
so that our custom action does the extra checks for 1 and 2. This approach links with plaf package and may fail in case this action is somehow overridden in some class below this UI class.
c. traverse the JFileChooser component hierarchy, find the JTextField (which apparently should occur only once in the component tree), decorate all the action listeners hanging on that JTextField with our custom checks. My debugging session shows that this JTextField is some anonymous subclass of JTextField living in the sun.swing.FilePane.
This approach seems to be more OO-friendly, but there's a chance that for some OS this text field is absent, or some other JTextField is also present in the hierarchy.
Well, it seems that public JFileChooser API would not suffice to achieve that behaviour, while the other two options are either deep magic or unportable (long-term), or even both.
So, the question is: which approach would you choose and why?
Regarding option2, you don't need to use reflection to customize the accept Action. You can just override the approveSelection() method. Something like:
JFileChooser chooser = new JFileChooser( new File(".") )
{
public void approveSelection()
{
if (getSelectedFile().exists())
{
System.out.println("duplicate");
return;
}
else
super.approveSelection();
}
};
I recently encountered the same requirement, i.e., pressing Enter in the JTextField of a JFileChooser should cause the displayed dialog to traverse a directory instead of returning from the dialog. Only clicking on the Open button should cause the final selection.
The solution was fairly simple (at least for my application) and has two components to it (Pardon the messed up formatting. I'm new to this forum and I'm not sure why the code is not displaying correctly).
1 - Register an AWTListener to keep track of the last event type generated by the user
class MyChooser extends JFileChooser implements java.awt.AWTEventListener {
...
MyChooser(){
Toolkit.getDefaultToolkit().addAWTEventListener(this,
AWTEvent.MOUSE_EVENT_MASK + AWTEvent.KEY_EVENT_MASK);
...
}
int lastEventId;
public void eventDispatched(AWTEvent e) {
lastEventId=e.getID();
}
}
2 - Override the approveSelection() method of JFileChooser and check whether the approval request is a result of a mouse event (likely caused by the user clicking on the Open button) or a key event caused by the user pressing Enter. The 'lastEventId' variable provides access to this information. My own approveSelection then looks as follows:
public void approveSelection() {
File f=getSelectedFile();
if (f.exists() && isTraversable(f) && lastEventId ==
KeyEvent.KEY_PRESSED) {
setCurrentDirectory(f);
return;
}
super.approveSelection(); }

Categories