//===================================================================== // File: MultiFileDialog.java // Class: MultiFileDialog // Package: AFLPgui // // Author: James J. Benham // Date: January 4, 1999 // Contact: james_benham@hmc.edu // // Genographer v1.0 - Computer assisted scoring of gels. // Copyright (C) 1998 Montana State University // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; version 2 // of the License. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // The GNU General Public License is distributed in the file GPL //===================================================================== package AFLPgui; import java.awt.Button; import java.awt.Choice; import java.awt.Dialog; import java.awt.FileDialog; import java.awt.Frame; import java.awt.Label; import java.awt.List; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.File; import java.util.Vector; import AFLPcore.FeatureList; import AFLPcore.ImportFilter; /** * This class allows the user to select multiple files. It provides * a directory strucute which can be navigated on one side and a list * of files to open on the other. * * @author James J. Benham * @version 1.1.0 * @date January 4, 1999 */ public class MultiFileDialog extends Dialog implements ActionListener, WindowListener { // Layout Constants private static final int BUTTON_WIDTH = 70; private static final int BUTTON_HEIGHT = 22; private static final int LIST_HEIGHT = 250; private static final int DIRECTORY_WIDTH = 180; private static final int IMPORT_WIDTH = 180; private static final int COMPONENT_HSPACE = 10; private static final int COMPONENT_VSPACE = 5; private static final int H_INSET = 5; private static final int V_INSET = 25; private static final int PATH_HEIGHT = 16; private static final int TYPE_LABEL_WIDTH = 80; private static final int FILE_TYPE_WIDTH = 200; private static final int FILE_TYPE_HEIGHT = 22; private static final int PATH_WIDTH = (IMPORT_WIDTH + DIRECTORY_WIDTH + BUTTON_WIDTH); private static final int WIDTH = (H_INSET + DIRECTORY_WIDTH + BUTTON_WIDTH + IMPORT_WIDTH + BUTTON_WIDTH + 5*COMPONENT_HSPACE); private static final int HEIGHT = (V_INSET + 2*PATH_HEIGHT + 3*COMPONENT_VSPACE + LIST_HEIGHT + FILE_TYPE_HEIGHT + 5); // GUI components private Label pathLabel; private List directoryList; private List openList; private Choice fileType; private Button addButton; private Button removeButton; private Button addAllButton; private Button clearButton; private Button importButton; private Button cancelButton; private String parentItem; private String directory; private String file; private File constantFiles[]; // files to always display, like hard drives private File currentFile; private File fileList[]; private Vector importFiles; private File importList[]; private ImportFilter filter; /** * Creates a new dialog with the specified parameters * * @param parent the owner of the dialog */ public MultiFileDialog(Frame parent) { this(parent, ""); } /** * Creates a new dialog with the specified parameters * * @param parent the owner of the dialog * @param title the name of the dialog box */ public MultiFileDialog(Frame parent, String title) { super(parent, title, true); importFiles = new Vector(); // find the constantFiles. For Windows/DOS drives try { File [] fileRoots = File.listRoots(); if(fileRoots.length > 1) { constantFiles = fileRoots; } else { // Don't bother if there is only one root constantFiles = new File[0]; } } catch(NoSuchMethodError e) { // Ok, we're on an older version of Java, the listRoots method // was added for Java version 1.2. Just go back to the old // way of doing things. This may cause Windows to bring up annoying // dialogs, but deal. System.out.println("Older version of Java detected. Upgrading to" + " Java 2 or later is recommended."); Vector tempFileList = new Vector(); File tempFile; for(char letter = 'A'; letter <= 'Z'; letter++) { tempFile = new File("" + letter + ":\\"); if(tempFile.exists()) tempFileList.addElement(tempFile); } constantFiles = new File[tempFileList.size()]; for(int i=0; i < tempFileList.size(); i++) constantFiles[i] = (File) tempFileList.elementAt(i); } // set the directory by using a normal file dialog and getting // its directory; directory = "."; // Find the real name of "." File dirFile = new File("."); directory = dirFile.getAbsolutePath(); if(directory.endsWith(".")) directory = directory.substring(0, directory.length() - 1); parentItem = ".. / (move up a directory)"; addWindowListener(this); componentLayout(); } /** * Gives a list of files selected by the user. * * @return the files to use. */ public File[] getFiles() { return importList; } /** * Gives the filter selected in the dialog box. * * @return the filter to read in the files selected. */ public ImportFilter getFilter() { return filter; } /** * Controls the visibility of this dialog. If it is set to be visible, * it will perform some initialization stuff, like clearing the old * list of files. * * @param b true to display the dialog. */ public void setVisible(boolean b) { if(b) { // initialize some of the stuff openList.removeAll(); importFiles = new Vector(); currentFile = new File(directory); String tempSt = currentFile.getAbsolutePath(); if(tempSt.endsWith(".")) tempSt = tempSt.substring(0, tempSt.length() - 1); directory = tempSt; currentFile = new File(directory); displayDirectory(currentFile); } // let the superclass do the rest super.setVisible(b); } /** * Displays the specified directory on the left side of the dialog. All of * the files and subdirectories in the directory will be entered in the * list. It will also enter all of the files/subdirectories into an * array so they can be accessed by looking at the index of a selected * item in the list. * * @param dir the directory to display. */ protected void displayDirectory(File dir) { File temp; if(!currentFile.isDirectory()) System.err.println("Invalid directory specification."); directoryList.removeAll(); directoryList.add(parentItem); String contents[] = dir.list(); fileList = new File[contents.length + constantFiles.length]; try { directory = dir.getCanonicalPath(); } catch(java.io.IOException e) { directory = "IO Error"; } pathLabel.setText("Current Path: " + directory); directoryList.setVisible(false); for(int i=0; i < constantFiles.length; i++) { fileList[i] = constantFiles[i]; directoryList.add("[" + constantFiles[i].getPath() + "]"); } File listing[] = new File[contents.length]; for(int i=0; i < contents.length; i++) { listing[i] = new File(directory + File.separator + contents[i]); if(listing[i].isDirectory()) contents[i] += "/"; } quicksort(contents, listing, 0, contents.length - 1); for(int i=0; i < contents.length; i++) { fileList[i + constantFiles.length] = listing[i]; directoryList.add(contents[i]); } directoryList.setVisible(true); } /** * This will sort the file listing. It uses the compare method to * compare two entries, taking into account directories, which are * placed first. It uses the standard quicksort algorithm. The code * was copied from an example of Robert Keller. The only difference * is that is sorts two arrays which are kept in parrallel and * comparisons occur between arrays as well as within the arrays. * * @param str the names of all of the files, as they will appear in * the list * @param files a list of file objects that represents the names in * str Used to see if a name represents * a directory or not. * @param low the bottom index for the portion of the array to be sorted. * @param high the upper index for the portion of the array to be sorted. */ private static void quicksort(String str[], File files[], int low, int high) { String tempStr; File tempFile; if( low >= high ) return; // basis case, <= 1 element String pivotStr = str[(low + high)/2]; // select pivot File pivotFile = files[(low + high)/2]; // shift elements <= pivot to bottom // shift elements >= pivot to top int i = low-1; int j = high+1; for( ; ; ) { // find two elements to exchange do { i++; } // slide i up while( compare(str[i], files[i], pivotStr, pivotFile) < 0); do { j--; } // slide j down while( compare(str[j], files[j], pivotStr, pivotFile) > 0); if( i >= j ) // if sliders meet or cross break; // swap the two elementes in both arrays tempStr = str[i]; str[i] = str[j]; str[j] = tempStr; tempFile = files[i]; files[i] = files[j]; files[j] = tempFile; } quicksort(str, files, low, i-1); // sort lower half quicksort(str, files, j+1, high); // sort upper half } /** * Compare two string/file pairs. This is a standard ordering comparison * used by the sorting function. Directories are "less than" standard * files. If the two pairs are of the same type (both directories or * both files) then their names are compared using standard string * comparisons. File objects are used to determine if a file is a standard * file or a directroy. * *@param str1 the name of the first file *@param f1 a file or directory whose name is str1 *@param str2 the name of the second file/direcotry *@param f2 the second file. * *@return a negative value if the first pair is less than the second, * a positive value if it is greater, and zero if they are equal. */ private static int compare(String str1, File f1, String str2, File f2) { if(!f1.isDirectory() && !f2.isDirectory()) return str1.compareTo(str2); else if(!f1.isDirectory()) // but j.isDirecory return 1; else if(!f2.isDirectory()) return -1; else return str1.compareTo(str2); } /** * Adds an entry to the list of files to be opened/imported. The entry * is specified by the index. It will be shown in the list and in * an internal list which will contain the actual file reference. * * @param selection the index of the file in the array of currently * displayed files. */ protected void addFile(int selection) { if(selection >= 0) // make sure something is actually selected { openList.add(fileList[selection - 1].getName()); importFiles.addElement(fileList[selection - 1]); } } /** * Adds entries to the list of files to be opened/imported. The entry * is specified by the index. It will be shown in the list and in * an internal list which will contain the actual file reference. * * @param selection a list of indicies of files in the array of currently * displayed files. */ protected void addFile(int selection[]) { openList.setVisible(false); for(int i=0; i < selection.length; i++) { addFile(selection[i]); } openList.setVisible(true); } /** * Removes the specified file from the list of files to open/import. * * @param index the index of the file in the list. */ protected void removeFile(int index) { if(index >= 0) // make sure something is actually selected { openList.remove(index); importFiles.removeElementAt(index); } } /** * Handle the button clicks as well as double clicks on the list. * Double clicking on a directory will open it and double-clicking on * a file will add it to the list to import. */ public void actionPerformed(ActionEvent e) { if(e.getSource() == directoryList) { int index = directoryList.getSelectedIndex(); // look for moving up to the parent first if(index == 0) { if(currentFile.getParent() != null) currentFile = new File(currentFile.getParent()); displayDirectory(currentFile); } else { // see what we selected, subtract 1 form the index since the // file list does not include the parent ".." if(fileList[index - 1].isDirectory()) { currentFile = fileList[index - 1]; displayDirectory(currentFile); } else { // it must be a file to read in, so add it to the list addFile(index); } } } else if(e.getSource() == addButton) { addFile(directoryList.getSelectedIndex()); } else if(e.getSource() == removeButton) { removeFile(openList.getSelectedIndex()); } else if(e.getSource() == addAllButton) { openList.setVisible(false); for(int i=(1 + constantFiles.length); i <= fileList.length; i++) addFile(i); openList.setVisible(true); } else if(e.getSource() == clearButton) { importFiles = new Vector(); openList.removeAll(); } else if(e.getSource() == importButton) { // get the files importList = new File[importFiles.size()]; for(int i=0; i < importList.length; i++) importList[i] = (File) importFiles.elementAt(i); // set the filter filter = (ImportFilter) FeatureList.getImportMgr().get(fileType.getSelectedItem()); dispose(); setVisible(false); } else if(e.getSource() == cancelButton) { importList = null; dispose(); setVisible(false); } } /** * Closes the dialog and is equivalent to clicking cancel. */ public void windowClosing(WindowEvent e) { importList = null; dispose(); setVisible(false); } /** * Lays out all of the components for the dialog box */ private void componentLayout() { setLayout(null); setSize(WIDTH, HEIGHT); setResizable(false); int x = H_INSET; int y = V_INSET; pathLabel = new Label("Current Path: "); add(pathLabel); pathLabel.setBounds(x + COMPONENT_HSPACE, y, PATH_WIDTH, PATH_HEIGHT); x += COMPONENT_HSPACE; y += PATH_HEIGHT + COMPONENT_VSPACE; Label filesLabel = new Label("Files: "); add(filesLabel); filesLabel.setBounds(x, y, DIRECTORY_WIDTH, PATH_HEIGHT); y += PATH_HEIGHT; directoryList = new List(); add(directoryList); directoryList.addActionListener(this); directoryList.setBounds(x, y, DIRECTORY_WIDTH, LIST_HEIGHT); y += LIST_HEIGHT + COMPONENT_VSPACE; Label fileTypeLabel = new Label("Files of Type:"); add(fileTypeLabel); fileTypeLabel.setBounds(x, y, TYPE_LABEL_WIDTH, FILE_TYPE_HEIGHT); //=============== File type Choice ==================== fileType = new Choice(); // add the items String types[] = FeatureList.getImportMgr().getNames(); for(int i=0; i < types.length; i++) fileType.add(types[i]); fileType.select(FeatureList.getImportMgr().getDefaultName()); add(fileType); fileType.setBounds(x + TYPE_LABEL_WIDTH, y, FILE_TYPE_WIDTH, FILE_TYPE_HEIGHT); x += DIRECTORY_WIDTH + COMPONENT_HSPACE; y = y - LIST_HEIGHT - COMPONENT_VSPACE; addButton = new Button("Add"); add(addButton); addButton.addActionListener(this); addButton.setBounds(x, y, BUTTON_WIDTH, BUTTON_HEIGHT); y += COMPONENT_VSPACE + BUTTON_HEIGHT; removeButton = new Button("Remove"); add(removeButton); removeButton.addActionListener(this); removeButton.setBounds(x, y, BUTTON_WIDTH, BUTTON_HEIGHT); y += COMPONENT_VSPACE + BUTTON_HEIGHT; addAllButton = new Button("Add All"); add(addAllButton); addAllButton.addActionListener(this); addAllButton.setBounds(x, y, BUTTON_WIDTH, BUTTON_HEIGHT); y += COMPONENT_VSPACE + BUTTON_HEIGHT; clearButton = new Button("Clear"); add(clearButton); clearButton.addActionListener(this); clearButton.setBounds(x, y, BUTTON_WIDTH, BUTTON_HEIGHT); x += BUTTON_WIDTH + COMPONENT_HSPACE; y = V_INSET + PATH_HEIGHT + COMPONENT_VSPACE; Label importLabel = new Label("Import Files:"); add(importLabel); importLabel.setBounds(x, y, IMPORT_WIDTH, PATH_HEIGHT); y += PATH_HEIGHT; openList = new List(); add(openList); openList.setBounds(x, y, IMPORT_WIDTH, LIST_HEIGHT); x += IMPORT_WIDTH + COMPONENT_HSPACE; importButton = new Button("Import"); add(importButton); importButton.addActionListener(this); importButton.setBounds(x, y, BUTTON_WIDTH, BUTTON_HEIGHT); y += BUTTON_HEIGHT + COMPONENT_VSPACE; cancelButton = new Button("Cancel"); add(cancelButton); cancelButton.addActionListener(this); cancelButton.setBounds(x, y, BUTTON_WIDTH, BUTTON_HEIGHT); } // ==================Unused methods required by interfaces================= /**Unused*/ public void windowOpened(WindowEvent e) {} /**Unused*/ public void windowClosed(WindowEvent e) {} /**Unused*/ public void windowIconified(WindowEvent e) {} /**Unused*/ public void windowDeiconified(WindowEvent e) {} /**Unused*/ public void windowActivated(WindowEvent e) {} /**Unused*/ public void windowDeactivated(WindowEvent e) {} }