Repurposing JFileChooser - java

I need to implement file browsing feature in my app and while I am aware of possibility of making a JList item and doing it manually I had an idea to just implement JFileChooser for this. I managed to reduce the JFileChooser just to list of directories and files but I wasn't able to override some of it's functionalities. I have been going through source code but no luck. My idea is for it to handle as following: On the top of the list to have a /... directory so when clicked on it it returns to parent folder. Also when double clicked on directory it sets it as current directory. When double clicked on file it returns the file as selected.
This is the code I used so far:
final JFileChooser fc = new JFileChooser();
fc.setControlButtonsAreShown(false);
fc.setCurrentDirectory(paths[list.getSelectedIndex()]);
/*remove unwanted components*/
for(int i = 0; i < fc.getComponentCount(); i++) {
fc.getComponent(0).setVisible(false);
fc.getComponent(1).setVisible(false);
fc.getComponent(3).setVisible(false);
}
add(fc, BorderLayout.CENTER);
I tried adding custom MouseListener to the JFileChooser but it didn't work.
This is the result I have so far:
Any idea which classes or listeners to overwrite/replace so I can achieve 2 desired effects?
This is what I am looking for in visual terms:

On the top of the list to have a /... directory so when clicked on i
it returns to parent folder.
JFileChooser has method with name changeToParentDirectory(). So you could simply add a Button and call that method.
JButton toParent = new JButton("/..");
toParent.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent a) {
fc.changeToParentDirectory();
}
});
fc.add(toParent, BorderLayout.NORTH);
Also when double clicked on directory it sets it as current directory.
You can set a PropertyChangeListener to listen for JFileChooser.DIRECTORY_CHANGED_PROPERTY property that is fired whenever the current directory has changed using double click or the internal commands.
fc.addPropertyChangeListener(new PropertyChangeListener(){
#Override
public void propertyChange(PropertyChangeEvent e) {
String command = e.getPropertyName();
if (command.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)) {
File currentDir = fc.getCurrentDirectory();
System.out.println(currentDir.getAbsolutePath());
}
}
});
When double clicked on file it returns the file as selected.
You can set an ActionListener to listen for JFileChooser.APPROVE_SELECTION action that is fired whenever a file is chosen by double click.
fc.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals(JFileChooser.APPROVE_SELECTION)) {
File selectedFile = fc.getSelectedFile();
System.out.println(selectedFile.getAbsolutePath());
}
}
});
EDIT:
you misunderstood me for the parent folder action. Here is an image
to describe it.
This could be achieved by using a manipulated FileSystemView with the JFileChooser that is responsible for interacting with the content of the filesystem. My implementation manipulates the getFiles method to smuggle a special File in the list that has a defined name and points to the parent directory.
I'm not quite sure if this is a very good idea, since this is really not meant to be in the JFileChooser Code but here we go.
fc.setFileSystemView(new FileSystemView(){
// this method is abstract but since you don't
// want to create directories here you don't
// need to implement it.
#Override
public File createNewFolder(File f) throws IOException {
return null;
}
// manipulate the default getFiles method that creates
// the list of files in the current directory
#Override
public File[] getFiles(File dir, boolean useFileHiding){
// get the list of files from default implementation
File[] files = super.getFiles(dir,useFileHiding);
// get the parent directory of current
File parent = getParentDirectory(dir);
// skip the next for problematic folders with
// empty names and root folders
if(!dir.getName().isEmpty() && !isRoot(dir)){
// create a new list of files with one extra place
File[] nfiles = new File[files.length + 1];
// add a special file to list that points to parent directory
nfiles[0] = new File(parent.getAbsolutePath()){
// set a special name for that file
#Override
public String getName(){return "...";}
};
// add the rest of files to list
for(int i = 0; i < files.length; i++)
nfiles[i+1] = files[i];
// use the new list
files = nfiles;
}
// return list of files
return files;
}
// some special folders like "c:" gets converted
// in shellfolders.Then our setted name "..." would
// get converted to "local drive (c:)". This garantees
// that our setted name will be used.
#Override
public String getSystemDisplayName(File f) {
return f.getName();
}
});
EDIT2:
is there a quick solution to set scroll from horizontal to vertical
for JFileChooser?
There are two possibilities. The simple one is to change the style of your JFileChooser to the Details View by adding this code:
Action details = fc.getActionMap().get("viewTypeDetails");
details.actionPerformed(null);
The more complex one is to change the LayoutOrientation of the file view JList that is a component of sun.swing.FilePane that is the Component with ID:2 in the JFileChooser.
One problem here is that FilePane is not part of the Java Library but part of the Core Library and not accessible by default. But you can use Reflection to get the field private JList list; in the FilePane and change its LayoutOrientation to JList.VERTICAL with this code:
// get the FilePane Component of JFileChooser
Object filepane = fc.getComponent(2);
// get the list field with reflection
Field field_list = filepane.getClass().getDeclaredField("list");
// get access to this private field
field_list.setAccessible(true);
// read the value of the field
JList<?> list = (JList<?>)field_list.get(filepane);
// change the layout orientation
list.setLayoutOrientation(JList.VERTICAL);

Related

how to access a image variable in another button method in java fx

openButton.setOnAction((final ActionEvent X) -> {
File newfile = fileChooser.showOpenDialog(stage);
Image userselectedimage = new Image(file.toURI().toString());
more code
newactionbutton.setOnAction((final ActionEvent X) ->{
* i need access to the user selected image variable here *
}
I need to access the variable userselectedimage (it is a image selected by the user for the background) in another button. is it possible since the scope is defined in that onclick action to access that variable in another button? I need it since its the users selected picture (it is a file that the user selects). I tried defining it outside of the method wiht a static variable but that didn't work either, so im out of ideas.
thanks.
Image userselectedimage = null;
openButton.setOnAction((final ActionEvent X) -> {
File newfile = fileChooser.showOpenDialog(stage);
userselectedimage = new Image(file.toURI().toString());
}
newactionbutton.setOnAction((final ActionEvent X) ->{
userselectedimage.doSomething();
}

Set a new JFileChooser FileFilter and reset previous ones

I want a file filter with 2 options, one to show all files and the other to show a specific extension.
The specific extension should be the one selected by default.
I'm using the same JFileChooser twice, the first time to show .fas files, and the second to show .xls files.
Right now I'm using this code, but for some reasons it does not overwrite previous file extensions. Many answers here on SO have similar code and exactly the same problem, if you recycle your JFileChooser.
First portion, everything works
fileChooser.setFileFilter(new javax.swing.filechooser.FileFilter() {
#Override
public boolean accept(File file) {
return file.isDirectory() || file.getAbsolutePath().endsWith(".fas");
}
#Override
public String getDescription() {
return "Fasta";
}
});
// more modern API, same result
// fileChooser.setFileFilter(new FileNameExtensionFilter("File fasta", "fas"));
Second portion. This is used in a second "opening" of the same JFileChooser. It seems like it just adds a filter (the option to select .fas files remains).
// delete name of previously selected file
File currentDirectory = fileChooser.getCurrentDirectory();
fileChooser.setSelectedFile(new File(""));
fileChooser.setCurrentDirectory(currentDirectory);
// set new file filter
fileChooser.setFileFilter(new javax.swing.filechooser.FileFilter() {
#Override
public boolean accept(File file) {
return file.isDirectory() || file.getAbsolutePath().endsWith(".xls");
}
#Override
public String getDescription() {
return "Excel 97";
}
});
// more modern API, same result
// fileChooser.setFileFilter(new FileNameExtensionFilter("Excel 97", "xls"));
I guess the name setFileFilter can be misleading, because what it does in reality is set the selected file filter (without replacing the other filters).
If you want to recycle your JFileChooser, the simplest solution is to make a call to resetChoosableFileFilters() before setting the new filters.
Code for the first selection
fileChooser.setFileFilter(new FileNameExtensionFilter("File fasta", "fas"));
Code for the second selection
// delete name of previously selected file, but stay in the same directory
File currentDirectory = fileChooser.getCurrentDirectory();
fileChooser.setSelectedFile(new File(""));
fileChooser.setCurrentDirectory(currentDirectory);
// reset current file filters
fileChooser.resetChoosableFileFilters();
// set new file filter
fileChooser.setFileFilter(new FileNameExtensionFilter("Excel 97", "xls"));
// the "All files" filter will be present too, unless you uncomment this
// fileChooser.setAcceptAllFileFilterUsed(false);
Try using the xxxChoosableFileFilter() methods which seem to be the ones driving the combobox model.
setFileFilter() seems to set the basic filter that is used for the combobox if there are no choosable filters. However, if there are choosable filters the basic filter would be ignored. So I assume that somewhere in the code (yours or Swing) the basic filter is added to the choosables.
Here's a snippet of JDK 8 source for one of the combobox models which seems to do what I mentioned above:
public Object getSelectedItem() {
// Ensure that the current filter is in the list.
// NOTE: we shouldnt' have to do this, since JFileChooser adds
// the filter to the choosable filters list when the filter
// is set. Lets be paranoid just in case someone overrides
// setFileFilter in JFileChooser.
FileFilter currentFilter = getFileChooser().getFileFilter();
boolean found = false;
if(currentFilter != null) {
for (FileFilter filter : filters) {
if (filter == currentFilter) {
found = true;
}
}
if(found == false) {
getFileChooser().addChoosableFileFilter(currentFilter);
}
}
return getFileChooser().getFileFilter();
}
As you can see the problem is that if the current filter is not part of the model's filters array, it is added to the choosable filters and thus keeps being displayed.

Drag and Drop Java with Swing Worker

I want to create a DND action from a JList to the OS. My solution for now is to use a TransferHandler. In the method createTransferable I create the Transferable with the files I want to copy. But now there is my Problem: in some cases I have to download the files from a FTP-Server before I can copy the files. The very heavy download operation runs in a JavaSwingWorker (hidden behind the statement d.download(tmpDir);). Now the system trys to copy files which are not downloaded already.
Now i need a mechanism that allows me to create the transferable after I have downloaded the files. Is there a solution for my problem? Please help me!
Thanks!
Here is my method:
public Transferable createTransferable(JComponent c) {
JList list = (JList) c; // we know it's a JList
List<PictureDecorator> selectedPictures = getSelectedValues(list.getModel());
Vector cpFiles = new Vector();
List<Picture> donePictures = new ArrayList<Picture>();
List<Picture> notDonePictures = new ArrayList<Picture>();
String tmpDir = System.getProperty("java.io.tmpdir");
for(PictureDecorator pd : selectedPictures){
if(pd.getPic().getStatus() == PictureStatus.DONE)
donePictures.add(pd.getPic());
else
notDonePictures.add(pd.getPic());
}
Downloader d = new Downloader(parent, loginInformation, sced, donePictures, order);
d.download(tmpDir);
for(Picture p : donePictures){
cpFiles.add(new File(tmpDir + File.separator + p.getPicture().getName()));
}
for(Picture p : notDonePictures) {
cpFiles.add(p.getPicture());
}
TransferableFile tf = new TransferableFile(cpFiles);
return tf;
}
I need something that initiates the drag procedure then I get the path where the drag goes and then I can download the pictures and copy it to the destination path.
EDIT: Or another formulation: How I can find out the drop destination when I drop into the operating system?
To start the drag you need either a TransferHandler on the JList or alternatively a DragSource in combination with a DragGestureListener. Below you can see an example for doing that with a JTextField:
final JTextField textField = new JTextField(50);
DragGestureListener dragListener = new DragGestureListener() {
#Override
public void dragGestureRecognized(DragGestureEvent dge) {
// how the drag cursor should look like
Cursor cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
// the component being dragged
JTextField tf = (JTextField) dge.getComponent();
// Here a Transferable is created directly for a single file name
dge.startDrag(cursor, new TransferableFile(tf.getText()));
}
};
final DragSource ds = new DragSource();
ds.createDefaultDragGestureRecognizer(textField, DnDConstants.ACTION_COPY, dragListener);
You can put the above code inside your window creation procedure.
Your resulting transferable (TransferableFile in your case) should support the DataFlavor.javaFileListFlavor and you should return a List of Files from the getTransferData(DataFlavor flavor) method.
I believe this is also the method where the downloading should take place because that's the last point under your control before JVM-OS take over.
Now regarding the SwingWorker problem you can wait inside the method until the download completes. Perhaps modify your Downloader class to expose a boolean flag so you would be able to do something like while (!downloader.isDone()) { Thread.sleep(millisToSleep) };
[Edit: I must admit I don't like the idea of keeping the EventDispath thread busy but if this solves your current problem perhaps you can investigate later a more elegant solution]
A little warning: Since you don't have access to the drop location you cannot know how many times the getTransferData will be called. It is better to take this into account and create a simple cache (a Map sounds reasonable) with the temp files you have downloaded so far. In case you find the file in the cache you return its corresponding temp file directly and don't download it again.
Hope that helps

Action listener/Throws IOException Conflict

I am attempting to make a GUI for a program that reads and writes information from a random access file and display it at will. I cannot manipulate anything but the GUI and must refer to prebuilt methods( if the methods truly are unusable then I can make an exception).
The Part I am having issues with is dealing with an IOException while writing to a file.
public static class EdtButtonHandler implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if (e.getActionCommand().equals("Confirm")) //if the confirm button is pressed
{
writeAll(); //runs all write methods using the strings from the text field
frameAddMovie.setVisible(false);
}
The writeAll refers to a series of methods that write from a file based on the string it is passed, these strings come from textfields on my GUI Window. They all look something like this;
public static void writeDirector(int recordNum, String directorIn)throws IOException
{
int position;
RandomAccessFile recordFile = new RandomAccessFile("RecordFile", "rw");
position = recSize * (recordNum-1)+32; // read 32 spaces into the record to the start of the director line
recordFile.seek(position);
recordFile.writeBytes(directorIn);
}
At the point were the Confirm button is pressed and writeAll() is run there is an IOException thrown that cannot be caught with the method or the Class.
if (e.getActionCommand().equals("Confirm")) //if the confirm button is pressed
wouldn't you want that to be
if (e.getSource() = buttonname)

JList only updating on first insertion

This is my first attempt at a decent GUI for a Java app and I needed to use JLists with custom ListModels in order to represent certain structures.
//The 2 below structures implement the ListModel interface, using an internal
//ArrayList, in order to be used as
//a model for 2 different JLists in my GUI.
private PropertyList propertiesList = new PropertyList();
private SelectedProperties selProperties = new SelectedProperties();
//and these are the two JLists they are the models for
private javax.swing.JList Properties_JList;
private javax.swing.JList SelectedProperties_JList;
Here I populate my first JList via a stream:
private void OpenFile_MenuItemActionPerformed(java.awt.event.ActionEvent evt) {
final JFileChooser fc = new JFileChooser();
fc.setCurrentDirectory(null);
int returnVal = fc.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
this.Properties_JList.setModel(propertiesList);
this.propertiesList.AddFromFile(file);
} else {
//...
}
}
which happens to be working perfectly fine. I import a few entries by reading the file and they are all displayed as expected in a .toString() representation.
The problem is the second JList:
private void AddToSelected_JButtonActionPerformed(java.awt.event.ActionEvent evt) {
Property p = (Property) this.Properties_JList.getSelectedValue();
this.SelectedProperties_JList.setModel(selProperties);
this.selProperties.InsertProperty(p);
this.SelectedProperties_JList.revalidate();
}
Which appears to be displaying only the very first item I attempt to add to it through the above button event, and I have no idea why. I considered moving both .setModel(...) calls right after the form's initComponents() call but if I do that none of the lists gets populated, at all.
Logging messages made it clear that the internal structures are getting populated, but even though they are both respective ListModels for my JLists, one of them isn't working as expected.
A sufficient portion of the code is generated by Netbeans and I have spent hours looking up the API but still have trouble finding out what I'm doing wrong. Any ideas?

Categories

Resources