All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fathzer.soft.ajlib.swing.dialog.FileChooser Maven / Gradle / Ivy

package com.fathzer.soft.ajlib.swing.dialog;

import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;

import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.plaf.basic.BasicFileChooserUI;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

import com.fathzer.soft.ajlib.swing.framework.Application;
import com.fathzer.soft.ajlib.utilities.FileUtils;
import com.fathzer.soft.ajlib.utilities.NullUtils;

/**
 * A better file chooser. 
* Here are the improvements: *
    *
  • In save mode, a confirm dialog is displayed when the selected file * already exists.
  • *
  • Some classical problem are detected while validating the dialog. *
      *
    • In open mode, the file does not exist.
    • *
    • The application have no right to access the file.
    • *
    *
  • *
  • The getSelectedFile methods returns the selected file, even if the file * name field has been modified (It's strange but that method in JFileChooser * returns the last file selected in the file list until the "ok" button is * pressed. It sounds like a bug of JFileChooser)
  • *
  • setSelectedFile(null) clears the current selection.
  • *
  • The SELECTED_FILE_CHANGED_PROPERTY event is thrown accordingly to the * previous point.
  • *
* Here is the bad news: This component is designed to allow selection of * individual files, not folder, not multiple files :-( *
* @author Jean-Marc Astesana
* License: LGPL v3 */ public class FileChooser extends JFileChooser { private static final long serialVersionUID = 1L; private JTextField fileNameField; private File selectedFile; private Component fileNameLabel; private boolean selectionTestEnabled; public FileChooser() { this(null); } public FileChooser(String path) { super(path); this.selectionTestEnabled = true; this.selectedFile = null; fileNameField = null; try { Field field = getUI().getClass().getDeclaredField("fileNameTextField"); //$NON-NLS-1$ try { field.setAccessible(true); fileNameField = (JTextField) field.get(getUI()); } catch (Exception e) { throw new RuntimeException(e); } } catch (NoSuchFieldException e) { // If there's no fileNameField ... we will suppose there's no // problem with this field updates ! } try { Field field = getUI().getClass().getDeclaredField("fileNameLabel"); //$NON-NLS-1$ try { field.setAccessible(true); fileNameLabel = (JLabel) field.get(getUI()); } catch (Exception e) { throw new RuntimeException(e); } } catch (NoSuchFieldException e) { // If there's no fileNameLabel ... ok, then we will not do anything with it ! } PropertyChangeListener listener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { File old = getSelectedFile(); File superSelected = FileChooser.super.getSelectedFile(); String name = fileNameField != null ? fileNameField.getText() : superSelected==null?"":superSelected.getName(); File selectedFile = buildSelectedFile(name); if (!NullUtils.areEquals(old, selectedFile)) { int pos = fileNameField != null ? fileNameField.getCaretPosition() : 0; firePropertyChange(SELECTED_FILE_CHANGED_PROPERTY, old, selectedFile); if (fileNameField != null) { if (pos > fileNameField.getText().length()) { pos = fileNameField.getText().length(); } fileNameField.setCaretPosition(pos); } } } }; if (fileNameField != null) { new MyDocument(fileNameField); addPropertyChangeListener(MyDocument.TEXT_PROPERTY, listener); } addPropertyChangeListener(DIRECTORY_CHANGED_PROPERTY, listener); } @Override protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { // Update the selectedFile attribute if (SELECTED_FILE_CHANGED_PROPERTY.equals(propertyName)) { selectedFile = (File) newValue; } super.firePropertyChange(propertyName, oldValue, newValue); } /** Tests whether the getSelectedFile method is fixed.
* Unfortunately, the original JFileChooser has a bug: getSelectedFile returns a wrong value when * text is typed in the file name field.
* This class tries to fix it, but the fix is not available under some look and feels (CDE/Motif for example). * @return true if the bug is fixed for the current look and feel. */ public boolean isGetSelectedFileFixed() { return fileNameField!=null; } @Override public File getSelectedFile() { // The JFileChooser getSelectedFile returns a wrong value when the file name field is changed // So, we use an attribute updated when events that modify the selected file occurs ... if we can find it in the UI. if (!isGetSelectedFileFixed()) { // If not, we ask the ui ... if it allows it if (getUI() instanceof BasicFileChooserUI) { String name = ((BasicFileChooserUI)getUI()).getFileName(); selectedFile = buildSelectedFile(name); } else { System.err.println ("Warning, unable to get the current file name. UI is instance of "+ui.getClass()); selectedFile = super.getSelectedFile(); } } return selectedFile; } @Override public void approveSelection() { if (selectionTestEnabled) { // Refuse: // The broken links // The non existing files in OPEN_DIALOG mode // Non readable/writable files // Ask what to do if the file exists and we are in SAVE_DIALOG mode File file = getSelectedFile(); if (file != null) { if (getDialogType() == SAVE_DIALOG) { File canonical; try { canonical = FileUtils.getCanonical(file); if (canonical.exists()) { boolean cancel = showSaveDisplayQuestion(this); if (cancel) { // User doesn't want to overwrite the file return; } } } catch (IOException e) { throw new RuntimeException(e); } } String error = getDisabledCause(); if (error != null) { JOptionPane.showMessageDialog(this, error, get("Generic.error"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ return; } } } super.approveSelection(); } /** * Activate/Deactivate the test made on the selected file during in * approveSelection method.
* The default value is true. * @param enabled true to perform tests, false to skip them. */ public void setSelectionTestEnabled(boolean enabled) { this.selectionTestEnabled = enabled; } public String getDisabledCause() { File file = getSelectedFile(); if (file == null) { return null; } try { if (getDialogType() == OPEN_DIALOG) { if (!file.exists()) { return get("openDialog.fileDoesntExist"); //$NON-NLS-1$ } else { File canonical = FileUtils.getCanonical(file); if (!canonical.exists()) { return get("openDialog.targetDoesntExist"); //$NON-NLS-1$ } else if (!FileUtils.isReadable(file)) { return get("openDialog.fileNotReadable"); //$NON-NLS-1$ } } } else { File canonical = FileUtils.getCanonical(file); if (!FileUtils.isWritable(canonical)) { return get("saveDialog.fileNotWritable"); //$NON-NLS-1$ } } } catch (IOException e) { throw new RuntimeException(e); } return null; } private String get(String key) { return Application.LOCALIZATION.getString(key, getLocale()); } /** * Displays a dialog to inform the user that a file is already existing and it * will be overwritten if the user continue. * * @param parent * determines the Frame in which the dialog is displayed; if null, or * if the parentComponent has no Frame, a default Frame is used * @return true if the user cancels */ public static boolean showSaveDisplayQuestion(Component parent) { String message = Application.LOCALIZATION.getString("saveDialog.FileExist.message", parent.getLocale()); //$NON-NLS-1$ return JOptionPane.showOptionDialog(parent, message, Application.LOCALIZATION.getString("saveDialog.FileExist.title", parent.getLocale()), //$NON-NLS-1$ JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, null, null) == JOptionPane.NO_OPTION; } /* * (non-Javadoc) * * @see javax.swing.JFileChooser#setDialogType(int) */ @Override public void setDialogType(int dialogType) { if ((fileNameField != null) && (fileNameLabel != null)) { boolean visible = dialogType != JFileChooser.OPEN_DIALOG; fileNameField.setVisible(visible); fileNameLabel.setVisible(visible); } super.setDialogType(dialogType); } /* * (non-Javadoc) * * @see javax.swing.JFileChooser#setSelectedFile(java.io.File) */ @Override public void setSelectedFile(File file) { if ((file == null) && (selectedFile != null)) { File parent = selectedFile.getParentFile(); super.setSelectedFile(new File("")); //$NON-NLS-1$ super.setCurrentDirectory(parent); selectedFile = null; } else { super.setSelectedFile(file); } } private File buildSelectedFile(String name) { // We should remove directory because in DIRECTORIES_ONLY selection mode, the directory may be in the file name name = new File(name).getName(); return name.length() == 0 ? null : new File(FileChooser.super.getCurrentDirectory(), name); } private class MyDocument extends PlainDocument { private static final long serialVersionUID = 1L; private static final String TEXT_PROPERTY = "text"; //$NON-NLS-1$ private boolean ignoreEvents = false; private JTextField field; private MyDocument(JTextField field) { this.field = field; field.setDocument(this); } @Override public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException { String oldValue = field.getText(); this.ignoreEvents = true; super.replace(offset, length, text, attrs); this.ignoreEvents = false; String newValue = field.getText(); if (!oldValue.equals(newValue)) { firePropertyChange(TEXT_PROPERTY, oldValue, newValue); } } @Override public void remove(int offs, int len) throws BadLocationException { String oldValue = field.getText(); super.remove(offs, len); String newValue = field.getText(); if (!ignoreEvents && !oldValue.equals(newValue)) { firePropertyChange(TEXT_PROPERTY, oldValue, newValue); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy