diff --git a/uk/ac/sanger/artemis/components/filetree/FileList.java b/uk/ac/sanger/artemis/components/filetree/FileList.java new file mode 100644 index 0000000000000000000000000000000000000000..e32322eadf835e5a280f60a7b19fa8e1bfa23497 --- /dev/null +++ b/uk/ac/sanger/artemis/components/filetree/FileList.java @@ -0,0 +1,144 @@ +/******************************************************************** +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library 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 +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* @author: Copyright (C) Tim Carver +* +********************************************************************/ + +package uk.ac.sanger.artemis.components.filetree; + +import uk.ac.sanger.artemis.j2ssh.SshFileManager; +import java.util.Vector; +import java.util.Collections; +import java.io.IOException; +import java.io.File; + +public class FileList +{ + /** vector containing directories */ + private Vector vdir; + private Vector vfile; + private static SshFileManager ssh_client = new SshFileManager(); + + public FileList() + { + } + + protected void getDirList(String dir) + { + try + { + ssh_client.remoteList(dir); + } + catch(IOException ioe) + { + ioe.printStackTrace(); + } + vdir = ssh_client.getDirList(); + vfile = ssh_client.getFileList(); + Collections.sort(vfile); + } + + /** + * + * Delete the given file / directory + * + */ + protected boolean delete(String file) + { + return ssh_client.delete(file); + } + + /** + * + * Make a directory + * + */ + protected boolean mkdir(String dir) + { + return ssh_client.mkdir(dir); + } + + + /** + * + * Print working directory + * + */ + protected String pwd() + { + return ssh_client.pwd(); + } + + /** + * + * Rename a file + * + */ + protected boolean rename(String old_file, String new_file) + { + return ssh_client.rename(old_file, new_file); + } + + + /** + * + * Put a file + * + */ + protected boolean put(String dir, File local_file) + { + return ssh_client.put(dir, local_file); + } + + + /** + * + * Get the file contents + * + */ + protected byte[] getFileContents(String file) + { + return ssh_client.getFileContents(file); + } + + + /** + * + * Gets the list of files as a Vector + * @return list of files as a Vector + * + */ + public Vector fileVector() + { + return vfile; + } + + + /** + * + * Gets whether this name is a directory + * @return true if it is a directory + * + */ + public boolean isDirectory(String d) + { + return vdir.contains(d); + } + + +} diff --git a/uk/ac/sanger/artemis/components/filetree/FileManager.java b/uk/ac/sanger/artemis/components/filetree/FileManager.java new file mode 100644 index 0000000000000000000000000000000000000000..85aca7e09dbf8bf8fb5289c05775f71b01f22f7b --- /dev/null +++ b/uk/ac/sanger/artemis/components/filetree/FileManager.java @@ -0,0 +1,435 @@ +/******************************************************************** +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library 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 +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* Copyright (C) Genome Research Limited +* +********************************************************************/ + +package uk.ac.sanger.artemis.components.filetree; + +import uk.ac.sanger.artemis.util.StringVector; +import uk.ac.sanger.artemis.Options; + +import javax.swing.*; +import java.io.File; +import java.io.FileFilter; +import java.awt.event.*; +import java.awt.geom.*; +import java.awt.*; + +public class FileManager extends JFrame +{ + + /** busy cursor */ + private Cursor cbusy = new Cursor(Cursor.WAIT_CURSOR); + /** done cursor */ + private Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR); + + public FileManager(JFrame frame) + { + this(frame,getArtemisFilter()); + } + + /** + * + * File Manager Frame + * @param frame parent frame + * + */ + public FileManager(JFrame frame, FileFilter filter) + { + super("File Manager"); + + FileTree ftree = new FileTree(new File(System.getProperty("user.dir")), + frame, filter); + JScrollPane jsp = new JScrollPane(ftree); + JPanel pane = (JPanel)getContentPane(); + pane.setLayout(new BorderLayout()); + pane.add(jsp, BorderLayout.CENTER); + setJMenuBar(makeMenuBar(pane,ftree)); + pane.add(getFileFileterComboBox(ftree), BorderLayout.SOUTH); + + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + jsp.setPreferredSize(new Dimension(210, + (int)(screen.getHeight()/2))); + pack(); + + int yloc = (int)((screen.getHeight()-getHeight())/2); + setLocation(0,yloc); + setVisible(true); + } + + protected JComboBox getFileFileterComboBox(final FileTree ftree) + { + String[] filters = { "Artemis Files", "Sequence Files", + "Feature Files", "All Files" }; + final JComboBox comboFilter = new JComboBox(filters); + comboFilter.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + String select = (String)comboFilter.getSelectedItem(); + if(select.equals("Artemis Files")) + ftree.setFilter(getArtemisFilter()); + else if(select.equals("Sequence Files")) + ftree.setFilter(getSequenceFilter()); + else if(select.equals("Feature Files")) + ftree.setFilter(getFeatureFilter()); + else if(select.equals("All Files")) + { + ftree.setFilter(new FileFilter() + { + public boolean accept(File pathname) + { + if(pathname.getName().startsWith(".")) + return false; + return true; + } + }); + } + } + }); + return comboFilter; + } + + /** + * + * Get a file filter for sequence and feature suffixes. + * @return file filter + */ + protected static FileFilter getArtemisFilter() + { + final StringVector sequence_suffixes = + Options.getOptions().getOptionValues("sequence_file_suffixes"); + + final StringVector feature_suffixes = + Options.getOptions().getOptionValues("feature_file_suffixes"); + + final FileFilter artemis_filter = new FileFilter() + { + public boolean accept(File pathname) + { + if(pathname.isDirectory() && + !pathname.getName().startsWith(".")) + return true; + + for(int i = 0; i<sequence_suffixes.size(); ++i) + { + final String suffix = sequence_suffixes.elementAt(i); + + if(pathname.getName().endsWith("." + suffix) || + pathname.getName().endsWith("." + suffix + ".gz")) + return true; + } + + for(int i = 0; i<feature_suffixes.size(); ++i) + { + final String suffix = feature_suffixes.elementAt(i); + + if(pathname.getName().endsWith("." + suffix) || + pathname.getName().endsWith("." + suffix + ".gz")) + return true; + } + return false; + } + }; + return artemis_filter; + } + + + /** + * + * Get a file filter for feature suffixes. + * @return file filter + */ + protected static FileFilter getFeatureFilter() + { + final StringVector feature_suffixes = + Options.getOptions().getOptionValues("feature_file_suffixes"); + + final FileFilter feature_filter = new FileFilter() + { + public boolean accept(File pathname) + { + if(pathname.isDirectory() && + !pathname.getName().startsWith(".")) + return true; + + for(int i = 0; i<feature_suffixes.size(); ++i) + { + final String suffix = feature_suffixes.elementAt(i); + + if(pathname.getName().endsWith("." + suffix) || + pathname.getName().endsWith("." + suffix + ".gz")) + return true; + } + return false; + } + }; + return feature_filter; + } + + /** + * + * Get a file filter for sequence suffixes. + * @return file filter + */ + protected static FileFilter getSequenceFilter() + { + final StringVector sequence_suffixes = + Options.getOptions().getOptionValues("sequence_file_suffixes"); + + final FileFilter seq_filter = new FileFilter() + { + public boolean accept(File pathname) + { + if(pathname.isDirectory() && + !pathname.getName().startsWith(".")) + return true; + + for(int i = 0; i<sequence_suffixes.size(); ++i) + { + final String suffix = sequence_suffixes.elementAt(i); + + if(pathname.getName().endsWith("." + suffix) || + pathname.getName().endsWith("." + suffix + ".gz")) + return true; + } + + return false; + } + }; + return seq_filter; + } + + /** + * + * Set up a menu and tool bar + * @param pane panel to add toolbar to + * @param ftree file tree display + * + */ + private JMenuBar makeMenuBar(JPanel pane, final FileTree ftree) + { + JMenuBar mBar = new JMenuBar(); + JMenu fileMenu = new JMenu("File"); + mBar.add(fileMenu); + + JMenuItem fileMenuGoto = new JMenuItem("Go to Directory ..."); + fileMenuGoto.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + String dir = ftree.getRoot().getAbsolutePath(); + String newDir = JOptionPane.showInputDialog(FileManager.this, + "Go to Directory:",dir); + + if(newDir == null) + return; + + newDir = newDir.trim(); + File newDirFile = new File(newDir); + + if(newDirFile.exists() && + newDirFile.canRead() && + !newDir.equals(dir)) + ftree.newRoot(newDir); + else + { + String error = null; + if(!newDirFile.exists()) + error = new String(newDir+" doesn't exist!"); + else if(!newDirFile.canRead()) + error = new String(newDir+" cannot be read!"); + else if(newDir.equals(dir)) + error = new String("Same directory!"); + + if(error != null) + JOptionPane.showMessageDialog(FileManager.this, + error, "Warning", + JOptionPane.WARNING_MESSAGE); + } + } + }); + fileMenu.add(fileMenuGoto); + fileMenu.add(new JSeparator()); + + JMenuItem fileMenuClose = new JMenuItem("Close"); + fileMenuClose.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + setVisible(false); + } + }); + fileMenu.add(fileMenuClose); + + // tool bar set up + JToolBar toolBar = new JToolBar(); + Dimension buttonSize = new Dimension(22,24); + + JButton upBt = new JButton() + { + public void paintComponent(Graphics g) + { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D)g; + + g2.setColor(new Color(0,128,0)); + float loc1[][] = { {11,18}, {7,18}, {7,14}, + {3,14}, {11,4} }; + + g2.fill(makeShape(loc1)); + g2.setColor(Color.green); + + float loc2[][] = { {11,18}, {15,18}, {15,14}, + {19,14}, {11,4} }; + g2.fill(makeShape(loc2)); + + setSize(22,24); + } + }; + upBt.setPreferredSize(buttonSize); + upBt.setMinimumSize(buttonSize); + + upBt.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + FileManager.this.setCursor(cbusy); + File root = ftree.getRoot(); + String parent = root.getParent(); + if(parent != null) + ftree.newRoot(parent); + FileManager.this.setCursor(cdone); + } + }); + toolBar.add(upBt); + +// yeastpub + JButton shortCut1 = new JButton() + { + public void paintComponent(Graphics g) + { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D)g; + Font font = new Font("Monospaced", Font.BOLD, 14); + g2.setFont(font); + + g2.setColor(Color.black); + g2.drawString("Y",4,18); + g2.setColor(Color.red); + g2.drawString("P",10,15); + setSize(22,24); + } + }; + shortCut1.setPreferredSize(buttonSize); + shortCut1.setMinimumSize(buttonSize); + shortCut1.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ftree.newRoot("/nfs/disk222/yeastpub"); + } + }); + + if((new File("/nfs/disk222/yeastpub")).exists()) + toolBar.add(shortCut1); + +// pathdata + JButton shortCut2 = new JButton() + { + public void paintComponent(Graphics g) + { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D)g; + Font font = new Font("Monospaced", Font.BOLD, 14); + g2.setFont(font); + + g2.setColor(Color.black); + g2.drawString("P",4,18); + g2.setColor(Color.red); + g2.drawString("D",10,15); + setSize(22,24); + } + }; + shortCut2.setPreferredSize(buttonSize); + shortCut2.setMinimumSize(buttonSize); + shortCut2.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ftree.newRoot("/nfs/pathdata/"); + } + }); + + if((new File("/nfs/pathdata/")).exists()) + toolBar.add(shortCut2); + +// home button + JButton homeBt = new JButton() + { + public void paintComponent(Graphics g) + { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D)g; + + g2.setColor(Color.blue); + float loc1[][] = { {3,14}, {11,3}, {19,14}, + {17,14}, {17,18}, {5,18}, {5,14} }; + g2.fill(makeShape(loc1)); + + setSize(22,24); + } + }; + homeBt.setPreferredSize(buttonSize); + homeBt.setMinimumSize(buttonSize); + homeBt.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ftree.newRoot(System.getProperty("user.home")); + } + }); + toolBar.add(homeBt); + + toolBar.add(Box.createVerticalStrut(35)); + pane.add(toolBar, BorderLayout.NORTH); + + return mBar; + } + + /** + * + * Used to draw a Shape. + * + */ + public static GeneralPath makeShape(float loc[][]) + { + GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO); + + path.moveTo(loc[0][0],loc[0][1]); + + for(int i=1; i<loc.length; i++) + path.lineTo(loc[i][0],loc[i][1]); + + return path; + } + + +} + diff --git a/uk/ac/sanger/artemis/components/filetree/FileNode.java b/uk/ac/sanger/artemis/components/filetree/FileNode.java new file mode 100644 index 0000000000000000000000000000000000000000..6542722e7c292e2c4d383102599d9c0a697e1529 --- /dev/null +++ b/uk/ac/sanger/artemis/components/filetree/FileNode.java @@ -0,0 +1,159 @@ +/******************************************************************** +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library 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 +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* Copyright (C) Genome Research Limited +* +********************************************************************/ + +package uk.ac.sanger.artemis.components.filetree; + +import java.awt.datatransfer.*; +import javax.swing.tree.*; +import java.io.*; +import java.util.*; + +/** +* +* File node for local file tree manager +* +*/ +public class FileNode extends DefaultMutableTreeNode + implements Transferable, Serializable +{ + /** true if explored */ + private boolean explored = false; + /** data flavour of a file node */ + public static DataFlavor FILENODE = + new DataFlavor(FileNode.class, "Local file"); + /** flavours file node and string */ + static DataFlavor flavors[] = { FILENODE, DataFlavor.stringFlavor }; + + /** + * + * @param file file node file + * + */ + public FileNode(File file) + { + setUserObject(file); + } + + /** Determine if this is a directory */ + public boolean getAllowsChildren() { return isDirectory(); } + /** Determine if this is a file */ + public boolean isLeaf() { return !isDirectory(); } + /** Get the File this node represents */ + public File getFile() { return (File)getUserObject(); } + /** Determine if this node has been explored */ + public boolean isExplored() { return explored; } + /** Determine if this is a directory */ + public boolean isDirectory() + { + File file = getFile(); + return file.isDirectory(); + } + + /** + * + * Returns the name of the file + * + */ + public String toString() + { + File file = (File)getUserObject(); + String filename = file.toString(); + int index = filename.lastIndexOf(File.separator); + + return (index != -1 && index != filename.length()-1) ? + filename.substring(index+1) : + filename; + } + + /** + * + * Explores a directory adding a FileNode for each + * child + * + */ + public void explore(FileFilter filter) + { + if(!isDirectory()) + return; + + if(!isExplored()) + { + File file = getFile(); + explored = true; + File[] children; +// filter files + children = file.listFiles(filter); + +// sort into alphabetic order + java.util.Arrays.sort(children); + for(int i=0; i < children.length; ++i) + add(new FileNode(children[i])); + } + } + + /** + * + * Forces the directory to be re-explored + * + */ + public void reExplore(FileFilter filter) + { + explored = false; + removeAllChildren(); + explore(filter); + } + +// Transferable + public DataFlavor[] getTransferDataFlavors() + { + return flavors; + } + + public boolean isDataFlavorSupported(DataFlavor f) + { + if(f.equals(FILENODE) || f.equals(DataFlavor.stringFlavor)) + return true; + return false; + } + + public Object getTransferData(DataFlavor d) + throws UnsupportedFlavorException, IOException + { + if(d.equals(FILENODE)) + return this; + else if(d.equals(DataFlavor.stringFlavor)) + return getFile().getAbsolutePath(); + else throw new UnsupportedFlavorException(d); + } + +//Serializable + private void writeObject(java.io.ObjectOutputStream out) throws IOException + { + out.defaultWriteObject(); + } + + private void readObject(java.io.ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + } + +} diff --git a/uk/ac/sanger/artemis/components/filetree/FileTree.java b/uk/ac/sanger/artemis/components/filetree/FileTree.java new file mode 100644 index 0000000000000000000000000000000000000000..cd86e466b3b628700df6f045f9f24d55b1bc347f --- /dev/null +++ b/uk/ac/sanger/artemis/components/filetree/FileTree.java @@ -0,0 +1,947 @@ +/******************************************************************** +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library 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 +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* Copyright (C) Genome Research Limited +* +********************************************************************/ + +package uk.ac.sanger.artemis.components.filetree; + +import uk.ac.sanger.artemis.components.SwingWorker; +import uk.ac.sanger.artemis.components.EntryEdit; +import uk.ac.sanger.artemis.components.EntryFileDialog; + +import uk.ac.sanger.artemis.io.EntryInformation; +import uk.ac.sanger.artemis.io.SimpleEntryInformation; +import uk.ac.sanger.artemis.*; +import uk.ac.sanger.artemis.util.InputStreamProgressListener; +import uk.ac.sanger.artemis.util.InputStreamProgressEvent; +import uk.ac.sanger.artemis.util.FileDocument; +import uk.ac.sanger.artemis.util.OutOfRangeException; +import uk.ac.sanger.artemis.sequence.NoSequenceException; +import uk.ac.sanger.artemis.components.MessageDialog; + +import java.awt.*; +import java.awt.event.*; +import java.awt.datatransfer.*; +import java.awt.dnd.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.tree.*; +import java.io.*; +import java.util.Vector; +import java.util.Enumeration; +import java.util.Hashtable; + + +/** +* +* Creates a local file tree manager. This acts as a drag +* source and sink for files. +* +*/ +public class FileTree extends JTree implements DragGestureListener, + DragSourceListener, DropTargetListener, ActionListener, + Autoscroll +{ + + /** root directory */ + private File root; + /** store of directories that are opened */ + private Vector openNode; + /** file separator */ + private String fs = new String(System.getProperty("file.separator")); + /** popup menu */ + private JPopupMenu popup; + /** busy cursor */ + private Cursor cbusy = new Cursor(Cursor.WAIT_CURSOR); + /** done cursor */ + private Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR); + /** AutoScroll margin */ + private static final int AUTOSCROLL_MARGIN = 45; + /** used by AutoScroll method */ + private Insets autoscrollInsets = new Insets( 0, 0, 0, 0 ); + /** file filter */ + private FileFilter filter = null; + + /** + * + * @param rt root directory + * @param f frame + * + */ + public FileTree(File rt, final JFrame f, + FileFilter filter) + { + this.root = rt; + this.filter = filter; + + DragSource dragSource = DragSource.getDefaultDragSource(); + + dragSource.createDefaultDragGestureRecognizer( + this, // component where drag originates + DnDConstants.ACTION_COPY_OR_MOVE, // actions + this); // drag gesture recognizer + + setDropTarget(new DropTarget(this,this)); + DefaultTreeModel model = createTreeModel(root); + setModel(model); + createTreeModelListener(); + + this.getSelectionModel().setSelectionMode + (TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); + + // Popup menu + addMouseListener(new PopupListener()); + popup = new JPopupMenu(); + + JMenuItem menuItem = new JMenuItem("Refresh"); + menuItem.addActionListener(this); + popup.add(menuItem); + popup.add(new JSeparator()); + //open menu + JMenu openMenu = new JMenu("Open With"); + popup.add(openMenu); + menuItem = new JMenuItem("Jemboss Alignment Editor"); + menuItem.addActionListener(this); + openMenu.add(menuItem); + menuItem = new JMenuItem("Artemis"); + menuItem.addActionListener(this); + openMenu.add(menuItem); + + menuItem = new JMenuItem("Rename..."); + menuItem.addActionListener(this); + popup.add(menuItem); + menuItem = new JMenuItem("New Folder..."); + menuItem.addActionListener(this); + popup.add(menuItem); + menuItem = new JMenuItem("Delete..."); + menuItem.addActionListener(this); + popup.add(menuItem); + popup.add(new JSeparator()); + menuItem = new JMenuItem("De-select All"); + menuItem.addActionListener(this); + popup.add(menuItem); + + //Listen for when a file is selected + MouseListener mouseListener = new MouseAdapter() + { + public void mouseClicked(MouseEvent me) + { + if(me.getClickCount() == 2 && isFileSelection() && + !me.isPopupTrigger()) + { + f.setCursor(cbusy); + FileNode node = getSelectedNode(); + String selected = node.getFile().getAbsolutePath(); + showFilePane(selected); + f.setCursor(cdone); + } + } + }; + this.addMouseListener(mouseListener); + + addTreeExpansionListener(new TreeExpansionListener() + { + public void treeCollapsed(TreeExpansionEvent e){} + public void treeExpanded(TreeExpansionEvent e) + { + TreePath path = e.getPath(); + if(path != null) + { + FileNode node = (FileNode)path.getLastPathComponent(); + + if(!node.isExplored()) + { + f.setCursor(cbusy); + exploreNode(node); + f.setCursor(cdone); + } + } + } + }); + + } + + + /** + * + * Popup menu actions + * @param e action event + * + */ + public void actionPerformed(ActionEvent e) + { + + JMenuItem source = (JMenuItem)(e.getSource()); + final FileNode node = getSelectedNode(); + + if(source.getText().equals("Refresh")) + { + if(node == null) + newRoot(root.getAbsolutePath()); + else if(node.isLeaf()) + refresh((FileNode)node.getParent()); + else + refresh(node); + return; + } + + if(node == null) + { + JOptionPane.showMessageDialog(null,"No file selected.", + "Warning", + JOptionPane.WARNING_MESSAGE); + return; + } + + final File f = node.getFile(); + + if(source.getText().equals("Jemboss Alignment Editor")) + { + org.emboss.jemboss.editor.AlignJFrame ajFrame = + new org.emboss.jemboss.editor.AlignJFrame(f); + ajFrame.setVisible(true); + } + else if(source.getText().equals("Artemis")) + { + setCursor(cbusy); + String selected = node.getFile().getAbsolutePath(); + showFilePane(selected); + setCursor(cdone); + } + else if(source.getText().equals("Text Editor")) + showFilePane(f.getAbsolutePath()); + else if(source.getText().equals("New Folder...")) + { + String path = null; + if(node.isLeaf()) + path = f.getParent(); + else + path = f.getAbsolutePath(); + + String inputValue = JOptionPane.showInputDialog(null, + "Folder Name","Create New Folder in "+path, + JOptionPane.QUESTION_MESSAGE); + + if(inputValue != null && !inputValue.equals("") ) + { + String fullname = path+fs+inputValue; + File dir = new File(fullname); + + if(dir.exists()) + JOptionPane.showMessageDialog(null, fullname+" alread exists!", + "Error", JOptionPane.ERROR_MESSAGE); + else + { + if(dir.mkdir()) + addObject(inputValue,path,node); + else + JOptionPane.showMessageDialog(null, + "Cannot make the folder\n"+fullname, + "Error", JOptionPane.ERROR_MESSAGE); + } + } + } + else if(source.getText().equals("Delete...")) + { + File fn[] = getSelectedFiles(); + String names = ""; + for(int i=0; i<fn.length;i++) + names = names.concat(fn[i].getAbsolutePath()+"\n"); + int n = JOptionPane.showConfirmDialog(null, + "Delete \n"+names+"?", + "Delete Files", + JOptionPane.YES_NO_OPTION); + + FileNode nodes[] = getSelectedNodes(); + if(n == JOptionPane.YES_OPTION) + for(int i=0; i<nodes.length;i++) + deleteFile(nodes[i]); + } + else if(source.getText().equals("De-select All")) + clearSelection(); + else if(isFileSelection() && source.getText().equals("Rename...")) + { + String inputValue = (String)JOptionPane.showInputDialog(null, + "New File Name","Rename "+f.getName(), + JOptionPane.QUESTION_MESSAGE,null,null,f.getName()); + + if(inputValue != null && !inputValue.equals("") ) + { + String path = f.getParent(); + String fullname = path+fs+inputValue; + File newFile = new File(fullname); + + try + { + renameFile(f,node,newFile.getCanonicalPath()); + } + catch(IOException ioe){} + } + } + } + + + /** + * + * Delete a file from the tree + * @param node node to delete + * + */ + public void deleteFile(final FileNode node) + { + File f = node.getFile(); + if(f.delete()) + { + Runnable deleteFileFromTree = new Runnable() + { + public void run () { deleteObject(node); }; + }; + SwingUtilities.invokeLater(deleteFileFromTree); + } + else + JOptionPane.showMessageDialog(null,"Cannot delete\n"+ + f.getAbsolutePath(),"Warning", + JOptionPane.ERROR_MESSAGE); + } + + + /** + * + * Method to rename a file and update the filenode's. + * @param oldFile file to rename + * @param oldNode filenode to be removed + * @param newFullName name of the new file + * + */ + private void renameFile(final File oldFile, final FileNode oldNode, + String newFullName) + { + final File fnew = new File(newFullName); + if(fnew.exists()) + JOptionPane.showMessageDialog(null, newFullName+" alread exists!", + "Warning", JOptionPane.ERROR_MESSAGE); + else + { + if(oldFile.renameTo(fnew)) + { + Runnable renameFileInTree = new Runnable() + { + public void run () + { + addObject(fnew.getName(),fnew.getParent(),oldNode); + deleteObject(oldNode); + }; + }; + SwingUtilities.invokeLater(renameFileInTree); + } + else + JOptionPane.showMessageDialog(null, + "Cannot rename \n"+oldFile.getAbsolutePath()+ + "\nto\n"+fnew.getAbsolutePath(), "Rename Error", + JOptionPane.ERROR_MESSAGE); + } + return; + } + + + /** + * + * Define a directory root for the file tree + * @param newRoot directory to use as the root for + * the tree. + * + */ + public void newRoot(String newRoot) + { + root = new File(newRoot); + DefaultTreeModel model = (DefaultTreeModel)getModel(); + model = createTreeModel(root); + setModel(model); + } + + /** + * + * Get the current root node. + * @return directory root. + * + */ + public File getRoot() + { + return root; + } + + /** + * + * Refresh + * @param FileNode node to refresh + * + */ + public void refresh(FileNode node) + { + node.reExplore(filter); + DefaultTreeModel model = (DefaultTreeModel)getModel(); + model.nodeStructureChanged(node); + } + + /** + * + * Set the current file filter + * + */ + public void setFilter(FileFilter filter) + { + this.filter = filter; + Enumeration en = openNode.elements(); + + while(en.hasMoreElements()) + refresh((FileNode)en.nextElement()); + } + + /** + * + * Get FileNode of selected node + * @return node that is currently selected + * + */ + public FileNode getSelectedNode() + { + TreePath path = getLeadSelectionPath(); + if(path == null) + return null; + FileNode node = (FileNode)path.getLastPathComponent(); + return node; + } + + + /** + * + * Get FileNodes of selected nodes + * @return node that is currently selected + * + */ + public FileNode[] getSelectedNodes() + { + TreePath path[] = getSelectionPaths(); + if(path == null) + return null; + + int numberSelected = path.length; + FileNode nodes[] = new FileNode[numberSelected]; + for(int i=0;i<numberSelected;i++) + nodes[i] = (FileNode)path[i].getLastPathComponent(); + + return nodes; + } + + + /** + * + * Get selected files + * @return node that is currently selected + * + */ + public File[] getSelectedFiles() + { + FileNode[] fn = getSelectedNodes(); + int numberSelected = fn.length; + File files[] = new File[numberSelected]; + for(int i=0;i<numberSelected;i++) + files[i] = fn[i].getFile(); + + return files; + } + + + /** + * + * Return true if selected node is a file + * @return true is a file is selected, false if + * a directory is selected + * + */ + public boolean isFileSelection() + { + TreePath path = getLeadSelectionPath(); + if(path == null) + return false; + + FileNode node = (FileNode)path.getLastPathComponent(); + return node.isLeaf(); + } + + + /** + * + * Make the given directory the root and create a new + * DefaultTreeModel. + * @param root root directory + * @param tree model with the root node set + * to the given directory + * + */ + private DefaultTreeModel createTreeModel(File root) + { + FileNode rootNode = new FileNode(root); + rootNode.explore(filter); + openNode = new Vector(); + openNode.add(rootNode); + return new DefaultTreeModel(rootNode); + } + + + /** + * + * Adding a file (or directory) to the file tree manager. + * This looks to see if the directory has already been opened + * and updates the filetree if it has. + * @param child new child to add in + * @param path path to where child is to be added + * @param node node to add child to + * + */ + public DefaultMutableTreeNode addObject(String child, + String path, FileNode node) + { + + DefaultTreeModel model = (DefaultTreeModel)getModel(); + if(node == null) + { + node = getNode(path); + if(node==null) + return null; + } + + FileNode parentNode = getNode(path); + File newleaf = new File(path + fs + child); + FileNode childNode = null; + + if(parentNode.isExplored()) + { + childNode = new FileNode(newleaf); + int index = getAnIndex(parentNode,child); + if(index > -1) + model.insertNodeInto(childNode, parentNode, index); + } + else if(parentNode.isDirectory()) + { + exploreNode(parentNode); + childNode = getNode(path + fs + child); + } + + // Make sure the user can see the new node. + this.scrollPathToVisible(new TreePath(childNode.getPath())); + return childNode; + } + + + /** + * + * Delete a node from the JTree + * @param node node for deletion + * + */ + public void deleteObject(FileNode node) + { + DefaultTreeModel model =(DefaultTreeModel)getModel(); + FileNode parentNode = getNode(node.getFile().getParent()); + model.removeNodeFromParent(node); + } + + + /** + * + * Explore a directory node + * @param dirNode direcory node to display + * + */ + public void exploreNode(FileNode dirNode) + { + DefaultTreeModel model = (DefaultTreeModel)getModel(); + dirNode.explore(filter); + openNode.add(dirNode); + model.nodeStructureChanged(dirNode); + } + + /** + * + * Gets the node from the existing explored nodes and their + * children. + * @param path path to a file or directory + * @return corresponding node if the directory or + * file is visible otherwise returns null. + * + */ + private FileNode getNode(String path) + { + Enumeration en = openNode.elements(); + + while(en.hasMoreElements()) + { + FileNode node = (FileNode)en.nextElement(); + String nodeName = node.getFile().getAbsolutePath(); + if(nodeName.equals(path)) + return node; + } + +// check children of explored nodes + en = openNode.elements(); + while(en.hasMoreElements()) + { + FileNode child = getChildNode((FileNode)en.nextElement(),path); + if(child != null) + return child; + } + + return null; + } + + + /** + * + * Gets the child node of a parent node + * @param parent parent node + * @param childName name of child + * @return the child node + * + */ + private FileNode getChildNode(FileNode parent, String childName) + { + for(Enumeration children = parent.children(); children.hasMoreElements() ;) + { + FileNode childNode = (FileNode)children.nextElement(); + String nodeName = childNode.getFile().getAbsolutePath(); + if(childName.equals(nodeName)) + return childNode; + } + + return null; + } + + /** + * + * Finds a new index for adding a new file to the file manager. + * @param parentNode parent directory node + * @param child new child node + * @return index of the child in the directory + * + */ + private int getAnIndex(FileNode parentNode, String child) + { + //find the index for the child + int num = parentNode.getChildCount(); + int childIndex = num; + for(int i=0;i<num;i++) + { + String nodeName = + ((FileNode)parentNode.getChildAt(i)).getFile().getName(); + if(nodeName.compareTo(child) > 0) + { + childIndex = i; + break; + } + else if (nodeName.compareTo(child) == 0) //file already exists + { + childIndex = -1; + break; + } + } + return childIndex; + } + + + /** + * + * Read a file into a byte array. + * @param filename file name + * @return byte[] contents of file + * + */ + protected static byte[] readByteFile(String filename) + { + + File fn = new File(filename); + byte[] b = null; + try + { + long s = fn.length(); + if(s == 0) + return b; + b = new byte[(int)s]; + FileInputStream fi = new FileInputStream(fn); + fi.read(b); + fi.close(); + } + catch (IOException ioe) + { + System.out.println("Cannot read file: " + filename); + } + return b; + } + + /** + * + * Opens a JFrame with the file contents displayed. + * @param filename file name to display + * + */ + private void showFilePane(final String filename) + { + SwingWorker entryWorker = new SwingWorker() + { + EntryEdit entry_edit; + public Object construct() + { + try + { + EntryInformation new_entry_information = + new SimpleEntryInformation(Options.getArtemisEntryInformation()); + + final Entry entry = new Entry(EntryFileDialog.getEntryFromFile( + null, new FileDocument(new File(filename)), + new_entry_information, true)); + if(entry == null) + return null; + + final EntryGroup entry_group = + new SimpleEntryGroup(entry.getBases()); + + entry_group.add(entry); + entry_edit = new EntryEdit(entry_group); + return null; + } + catch(NoSequenceException e) + { + new MessageDialog(null, "read failed: entry contains no sequence"); + } + catch(OutOfRangeException e) + { + new MessageDialog(null, "read failed: one of the features in " + + " the entry has an out of range " + + "location: " + e.getMessage()); + + } + catch(NullPointerException npe){} + + return null; + } + + public void finished() + { + if(entry_edit != null) + entry_edit.setVisible(true); + } + }; + entryWorker.start(); + + } + + +//////////////////// +// DRAG AND DROP +//////////////////// +// drag source + public void dragGestureRecognized(DragGestureEvent e) + { + // ignore if mouse popup trigger + InputEvent ie = e.getTriggerEvent(); + if(ie instanceof MouseEvent) + if(((MouseEvent)ie).isPopupTrigger()) + return; + + // drag only files + if(isFileSelection()) + e.startDrag(DragSource.DefaultCopyDrop, // cursor + (Transferable)getSelectedNode(), // transferable data + this); // drag source listener + } + public void dragDropEnd(DragSourceDropEvent e) {} + public void dragEnter(DragSourceDragEvent e) {} + public void dragExit(DragSourceEvent e) {} + public void dragOver(DragSourceDragEvent e) {} + public void dropActionChanged(DragSourceDragEvent e) {} + +// drop sink + public void dragEnter(DropTargetDragEvent e) + { + if(e.isDataFlavorSupported(FileNode.FILENODE)) + e.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); + } + + public void drop(DropTargetDropEvent e) + { + + Transferable t = e.getTransferable(); + final FileNode dropNode = getSelectedNode(); + if(dropNode == null) + { + e.rejectDrop(); + return; + } + + //local drop + if(t.isDataFlavorSupported(FileNode.FILENODE)) + { + try + { + FileNode fn = (FileNode)t.getTransferData(FileNode.FILENODE); + fn = getNode(fn.getFile().getAbsolutePath()); + + if (dropNode.isLeaf()) + { + e.rejectDrop(); + return; + } + + String dropDir = dropNode.getFile().getAbsolutePath(); + String newFullName = dropDir+fs+fn.toString(); + renameFile(fn.getFile(),fn,newFullName); + } + catch(Exception ufe){} + } + else + { + e.rejectDrop(); + return; + } + + } + + + /** + * + * When a suitable DataFlavor is offered over a remote file + * node the node is highlighted/selected and the drag + * accepted. Otherwise the drag is rejected. + * + */ + public void dragOver(DropTargetDragEvent e) + { + if(e.isDataFlavorSupported(FileNode.FILENODE)) + { + Point ploc = e.getLocation(); + TreePath ePath = getPathForLocation(ploc.x,ploc.y); + if (ePath == null) + { + e.rejectDrag(); + return; + } + FileNode node = (FileNode)ePath.getLastPathComponent(); + if(!node.isDirectory()) + e.rejectDrag(); + else + { + setSelectionPath(ePath); + e.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); + } + } + else + e.rejectDrag(); + + return; + } + + public void dropActionChanged(DropTargetDragEvent e) {} + public void dragExit(DropTargetEvent e){} + + +//////////////////// +// AUTO SCROLLING // +//////////////////// + /** + * + * Handles the auto scrolling of the JTree. + * @param location The location of the mouse. + * + */ + public void autoscroll( Point location ) + { + int top = 0, left = 0, bottom = 0, right = 0; + Dimension size = getSize(); + Rectangle rect = getVisibleRect(); + int bottomEdge = rect.y + rect.height; + int rightEdge = rect.x + rect.width; + if( location.y - rect.y < AUTOSCROLL_MARGIN && rect.y > 0 ) + top = AUTOSCROLL_MARGIN; + if( location.x - rect.x < AUTOSCROLL_MARGIN && rect.x > 0 ) + left = AUTOSCROLL_MARGIN; + if( bottomEdge - location.y < AUTOSCROLL_MARGIN && bottomEdge < size.height ) + bottom = AUTOSCROLL_MARGIN; + if( rightEdge - location.x < AUTOSCROLL_MARGIN && rightEdge < size.width ) + right = AUTOSCROLL_MARGIN; + rect.x += right - left; + rect.y += bottom - top; + scrollRectToVisible( rect ); + } + + + /** + * + * Gets the insets used for the autoscroll. + * @return The insets. + * + */ + public Insets getAutoscrollInsets() + { + Dimension size = getSize(); + Rectangle rect = getVisibleRect(); + autoscrollInsets.top = rect.y + AUTOSCROLL_MARGIN; + autoscrollInsets.left = rect.x + AUTOSCROLL_MARGIN; + autoscrollInsets.bottom = size.height - (rect.y+rect.height) + AUTOSCROLL_MARGIN; + autoscrollInsets.right = size.width - (rect.x+rect.width) + AUTOSCROLL_MARGIN; + return autoscrollInsets; + } + + /** + * + * Popup menu listener + * + */ + class PopupListener extends MouseAdapter + { + public void mousePressed(MouseEvent e) + { + maybeShowPopup(e); + } + + public void mouseReleased(MouseEvent e) + { + maybeShowPopup(e); + } + + private void maybeShowPopup(MouseEvent e) + { + if(e.isPopupTrigger()) + popup.show(e.getComponent(), + e.getX(), e.getY()); + } + } + + public static void main(String[] args) + { + JFrame tree_frame = new JFrame("File Manager"); + FileTree ftree = new FileTree(new File(System.getProperty("user.home")), + tree_frame,null); + JScrollPane jsp = new JScrollPane(ftree); + tree_frame.getContentPane().add(jsp); + tree_frame.pack(); + tree_frame.setVisible(true); + } + +} + diff --git a/uk/ac/sanger/artemis/components/filetree/LocalAndRemoteFileManager.java b/uk/ac/sanger/artemis/components/filetree/LocalAndRemoteFileManager.java new file mode 100644 index 0000000000000000000000000000000000000000..b105a2e9c78404e48805148f64a1ad96c3bac0be --- /dev/null +++ b/uk/ac/sanger/artemis/components/filetree/LocalAndRemoteFileManager.java @@ -0,0 +1,541 @@ +/******************************************************************** +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library 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 +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* Copyright (C) Genome Research Limited +* +********************************************************************/ + +package uk.ac.sanger.artemis.components.filetree; + +import uk.ac.sanger.artemis.util.StringVector; +import uk.ac.sanger.artemis.Options; + +import javax.swing.*; +import java.io.File; +import java.io.FileFilter; +import java.awt.event.*; +import java.awt.geom.*; +import java.awt.*; + +public class LocalAndRemoteFileManager extends JFrame +{ + + /** busy cursor */ + private Cursor cbusy = new Cursor(Cursor.WAIT_CURSOR); + /** done cursor */ + private Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR); + + public LocalAndRemoteFileManager(JFrame frame) + { + this(frame,getArtemisFilter()); + } + + /** + * + * File Manager Frame + * @param frame parent frame + * + */ + public LocalAndRemoteFileManager(JFrame frame, FileFilter filter) + { + super("File Manager"); + + final JPanel localPanel = new JPanel(new BorderLayout()); + FileTree ftree = new FileTree(new File(System.getProperty("user.dir")), + frame, filter); + JScrollPane localTree = new JScrollPane(ftree); + localPanel.add(localTree,BorderLayout.CENTER); + + final JPanel remotePanel = new JPanel(new BorderLayout()); + + FileList flist = new FileList(); + String pwd = flist.pwd(); + SshFileTree sshtree = new SshFileTree(pwd); + JScrollPane remoteTree = new JScrollPane(sshtree); + remotePanel.add(remoteTree,BorderLayout.CENTER); + + final JSplitPane treePane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + localPanel,remotePanel); + + JPanel pane = (JPanel)getContentPane(); + pane.setLayout(new BorderLayout()); + pane.add(treePane, BorderLayout.CENTER); + + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + Dimension panelSize = new Dimension(210, (int)(screen.getHeight()/3)); + setJMenuBar(makeMenuBar(pane,ftree,sshtree,localPanel,remotePanel,treePane,panelSize)); + localPanel.add(getFileFileterComboBox(ftree), BorderLayout.SOUTH); + + remoteTree.setPreferredSize(panelSize); + localTree.setPreferredSize(panelSize); + + pack(); + + int yloc = (int)((screen.getHeight()-getHeight())/2); + setLocation(0,yloc); + setVisible(true); + } + + protected JComboBox getFileFileterComboBox(final FileTree ftree) + { + String[] filters = { "Artemis Files", "Sequence Files", + "Feature Files", "All Files" }; + final JComboBox comboFilter = new JComboBox(filters); + comboFilter.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + String select = (String)comboFilter.getSelectedItem(); + if(select.equals("Artemis Files")) + ftree.setFilter(getArtemisFilter()); + else if(select.equals("Sequence Files")) + ftree.setFilter(getSequenceFilter()); + else if(select.equals("Feature Files")) + ftree.setFilter(getFeatureFilter()); + else if(select.equals("All Files")) + { + ftree.setFilter(new FileFilter() + { + public boolean accept(File pathname) + { + if(pathname.getName().startsWith(".")) + return false; + return true; + } + }); + } + } + }); + return comboFilter; + } + + /** + * + * Get a file filter for sequence and feature suffixes. + * @return file filter + */ + protected static FileFilter getArtemisFilter() + { + final StringVector sequence_suffixes = + Options.getOptions().getOptionValues("sequence_file_suffixes"); + + final StringVector feature_suffixes = + Options.getOptions().getOptionValues("feature_file_suffixes"); + + final FileFilter artemis_filter = new FileFilter() + { + public boolean accept(File pathname) + { + if(pathname.isDirectory() && + !pathname.getName().startsWith(".")) + return true; + + for(int i = 0; i<sequence_suffixes.size(); ++i) + { + final String suffix = sequence_suffixes.elementAt(i); + + if(pathname.getName().endsWith("." + suffix) || + pathname.getName().endsWith("." + suffix + ".gz")) + return true; + } + + for(int i = 0; i<feature_suffixes.size(); ++i) + { + final String suffix = feature_suffixes.elementAt(i); + + if(pathname.getName().endsWith("." + suffix) || + pathname.getName().endsWith("." + suffix + ".gz")) + return true; + } + return false; + } + }; + return artemis_filter; + } + + + /** + * + * Get a file filter for feature suffixes. + * @return file filter + */ + protected static FileFilter getFeatureFilter() + { + final StringVector feature_suffixes = + Options.getOptions().getOptionValues("feature_file_suffixes"); + + final FileFilter feature_filter = new FileFilter() + { + public boolean accept(File pathname) + { + if(pathname.isDirectory() && + !pathname.getName().startsWith(".")) + return true; + + for(int i = 0; i<feature_suffixes.size(); ++i) + { + final String suffix = feature_suffixes.elementAt(i); + + if(pathname.getName().endsWith("." + suffix) || + pathname.getName().endsWith("." + suffix + ".gz")) + return true; + } + return false; + } + }; + return feature_filter; + } + + /** + * + * Get a file filter for sequence suffixes. + * @return file filter + */ + protected static FileFilter getSequenceFilter() + { + final StringVector sequence_suffixes = + Options.getOptions().getOptionValues("sequence_file_suffixes"); + + final FileFilter seq_filter = new FileFilter() + { + public boolean accept(File pathname) + { + if(pathname.isDirectory() && + !pathname.getName().startsWith(".")) + return true; + + for(int i = 0; i<sequence_suffixes.size(); ++i) + { + final String suffix = sequence_suffixes.elementAt(i); + + if(pathname.getName().endsWith("." + suffix) || + pathname.getName().endsWith("." + suffix + ".gz")) + return true; + } + + return false; + } + }; + return seq_filter; + } + + /** + * + * Set up a menu and tool bar + * @param pane panel to add toolbar to + * @param ftree file tree display + * + */ + private JMenuBar makeMenuBar(JPanel pane, final FileTree ftree, final SshFileTree sshtree, + final JPanel localPanel, final JPanel remotePanel, + final JSplitPane treePane, final Dimension panelSize) + { + JMenuBar mBar = new JMenuBar(); + JMenu fileMenu = new JMenu("File"); + mBar.add(fileMenu); + + JMenuItem fileMenuGoto = new JMenuItem("Go to Directory ..."); + fileMenuGoto.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + String dir = ftree.getRoot().getAbsolutePath(); + String newDir = JOptionPane.showInputDialog(LocalAndRemoteFileManager.this, + "Go to Directory:",dir); + + if(newDir == null) + return; + + newDir = newDir.trim(); + File newDirFile = new File(newDir); + + if(newDirFile.exists() && + newDirFile.canRead() && + !newDir.equals(dir)) + ftree.newRoot(newDir); + else + { + String error = null; + if(!newDirFile.exists()) + error = new String(newDir+" doesn't exist!"); + else if(!newDirFile.canRead()) + error = new String(newDir+" cannot be read!"); + else if(newDir.equals(dir)) + error = new String("Same directory!"); + + if(error != null) + JOptionPane.showMessageDialog(LocalAndRemoteFileManager.this, + error, "Warning", + JOptionPane.WARNING_MESSAGE); + } + } + }); + fileMenu.add(fileMenuGoto); + fileMenu.add(new JSeparator()); + + JRadioButtonMenuItem prefV = new JRadioButtonMenuItem("Vertical Split"); + fileMenu.add(prefV); + prefV.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + treePane.remove(remotePanel); + treePane.remove(localPanel); + treePane.setOrientation(JSplitPane.VERTICAL_SPLIT); + treePane.setTopComponent(localPanel); + treePane.setBottomComponent(remotePanel); + remotePanel.setPreferredSize(panelSize); + localPanel.setPreferredSize(panelSize); + + pack(); + treePane.setDividerLocation(0.5); + } + }); +// prefV.setSelected(true); + ButtonGroup group = new ButtonGroup(); + group.add(prefV); + + JRadioButtonMenuItem prefH = new JRadioButtonMenuItem("Horizontal Split"); + fileMenu.add(prefH); + prefH.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + treePane.remove(remotePanel); + treePane.remove(localPanel); + treePane.setOrientation(JSplitPane.HORIZONTAL_SPLIT); + treePane.setLeftComponent(localPanel); + treePane.setRightComponent(remotePanel); + + remotePanel.setPreferredSize(panelSize); + localPanel.setPreferredSize(panelSize); + + pack(); + treePane.setDividerLocation(0.5); + } + }); + group.add(prefH); + prefH.setSelected(true); + + JMenuItem fileMenuClose = new JMenuItem("Close"); + fileMenuClose.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + setVisible(false); + } + }); + fileMenu.add(fileMenuClose); + + // remote tool bar set up + JToolBar remoteToolBar = new JToolBar(); + Dimension buttonSize = new Dimension(22,24); + + JButton remoteUpBt = new UpButton(); + remoteUpBt.setPreferredSize(buttonSize); + remoteUpBt.setMinimumSize(buttonSize); + + remoteUpBt.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + LocalAndRemoteFileManager.this.setCursor(cbusy); + sshtree.up(); + LocalAndRemoteFileManager.this.setCursor(cdone); + } + }); + remoteToolBar.add(remoteUpBt); + + remoteToolBar.add(Box.createVerticalStrut(35)); + remotePanel.add(remoteToolBar, BorderLayout.NORTH); + + // local tool bar set up + JToolBar toolBar = new JToolBar(); + + JButton upBt = new UpButton(); + upBt.setPreferredSize(buttonSize); + upBt.setMinimumSize(buttonSize); + + upBt.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + LocalAndRemoteFileManager.this.setCursor(cbusy); + File root = ftree.getRoot(); + String parent = root.getParent(); + if(parent != null) + ftree.newRoot(parent); + LocalAndRemoteFileManager.this.setCursor(cdone); + } + }); + toolBar.add(upBt); + +// yeastpub + JButton shortCut1 = new JButton() + { + public void paintComponent(Graphics g) + { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D)g; + Font font = new Font("Monospaced", Font.BOLD, 14); + g2.setFont(font); + + g2.setColor(Color.black); + g2.drawString("Y",4,18); + g2.setColor(Color.red); + g2.drawString("P",10,15); + setSize(22,24); + } + }; + shortCut1.setPreferredSize(buttonSize); + shortCut1.setMinimumSize(buttonSize); + shortCut1.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ftree.newRoot("/nfs/disk222/yeastpub"); + } + }); + + if((new File("/nfs/disk222/yeastpub")).exists()) + toolBar.add(shortCut1); + +// pathdata + JButton shortCut2 = new JButton() + { + public void paintComponent(Graphics g) + { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D)g; + Font font = new Font("Monospaced", Font.BOLD, 14); + g2.setFont(font); + + g2.setColor(Color.black); + g2.drawString("P",4,18); + g2.setColor(Color.red); + g2.drawString("D",10,15); + setSize(22,24); + } + }; + shortCut2.setPreferredSize(buttonSize); + shortCut2.setMinimumSize(buttonSize); + shortCut2.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ftree.newRoot("/nfs/pathdata/"); + } + }); + + if((new File("/nfs/pathdata/")).exists()) + toolBar.add(shortCut2); + +// home button + JButton homeBt = new JButton() + { + public void paintComponent(Graphics g) + { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D)g; + + g2.setColor(Color.blue); + float loc1[][] = { {3,14}, {11,3}, {19,14}, + {17,14}, {17,18}, {5,18}, {5,14} }; + g2.fill(makeShape(loc1)); + + setSize(22,24); + } + }; + homeBt.setPreferredSize(buttonSize); + homeBt.setMinimumSize(buttonSize); + homeBt.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + ftree.newRoot(System.getProperty("user.home")); + } + }); + toolBar.add(homeBt); + + toolBar.add(Box.createVerticalStrut(35)); + localPanel.add(toolBar, BorderLayout.NORTH); + + return mBar; + } + + /** + * + * Used to draw a Shape. + * + */ + public static GeneralPath makeShape(float loc[][]) + { + GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO); + + path.moveTo(loc[0][0],loc[0][1]); + + for(int i=1; i<loc.length; i++) + path.lineTo(loc[i][0],loc[i][1]); + + return path; + } + + class UpButton extends JButton + { + public void paintComponent(Graphics g) + { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D)g; + + g2.setColor(new Color(0,128,0)); + float loc1[][] = { {11,18}, {7,18}, {7,14}, + {3,14}, {11,4} }; + + g2.fill(makeShape(loc1)); + g2.setColor(Color.green); + + float loc2[][] = { {11,18}, {15,18}, {15,14}, + {19,14}, {11,4} }; + g2.fill(makeShape(loc2)); + + setSize(22,24); + } + } + + public static void main(String args[]) + { + final javax.swing.LookAndFeel look_and_feel = + javax.swing.UIManager.getLookAndFeel(); + + final javax.swing.plaf.FontUIResource font_ui_resource = + Options.getOptions().getFontUIResource(); + + java.util.Enumeration keys = UIManager.getDefaults().keys(); + while(keys.hasMoreElements()) + { + Object key = keys.nextElement(); + Object value = UIManager.get(key); + if(value instanceof javax.swing.plaf.FontUIResource) + UIManager.put(key, font_ui_resource); + } + + JFrame frame = new LocalAndRemoteFileManager(null); + frame.pack(); + frame.setVisible(true); + } +} + diff --git a/uk/ac/sanger/artemis/components/filetree/RemoteFileNode.java b/uk/ac/sanger/artemis/components/filetree/RemoteFileNode.java new file mode 100644 index 0000000000000000000000000000000000000000..4e7a678a716f57b9e8dbee715831beaddb72b90f --- /dev/null +++ b/uk/ac/sanger/artemis/components/filetree/RemoteFileNode.java @@ -0,0 +1,266 @@ +/******************************************************************** +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library 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 +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* @author: Copyright (C) Tim Carver +* +********************************************************************/ + +package uk.ac.sanger.artemis.components.filetree; + +import java.awt.datatransfer.*; +import javax.swing.tree.*; +import java.io.*; +import java.util.*; + +/** +* +* File node for remote file tree manager +* +*/ +public class RemoteFileNode extends DefaultMutableTreeNode + implements Transferable, Serializable +{ + /** true if node is explored */ + private boolean explored = false; + /** true if node is a directory */ + private boolean isDir = false; + /** full name of node */ + private String fullname; + /** path to the file on the server */ + private String serverPathToFile; + /** root directory */ + private String rootdir; + + /** parent directory listing */ + private transient FileList parentList; // make transient for + /** jemboss properties */ + private transient Properties mysettings; // Transferable to work + /** remote server file roots */ + private transient String froots; + /** file separator for server files */ + private String fs = "/"; + + final public static DataFlavor REMOTEFILENODE = + new DataFlavor(RemoteFileNode.class, "Remote file"); + static DataFlavor remoteFlavors[] = { REMOTEFILENODE, + DataFlavor.stringFlavor }; + + + /** + * + * @param mysettings jemboss properties + * @param froots remote server file roots + * @param file file for this node + * @param parentList parent directory listing + * @param parent parent to this node + * + */ + public RemoteFileNode(Properties mysettings, String froots, + String file, FileList parentList, String parent) + { + this(mysettings, froots, file, parentList, parent, false); + } + + /** + * + * @param mysettings jemboss properties + * @param froots remote server file roots + * @param file file for this node + * @param parentList parent directory listing + * @param parent parent to this node + * @param ldir true if the node is a directory + * + */ + public RemoteFileNode(Properties mysettings, String froots, + String file, FileList parentList, String parent, + boolean ldir) + { + this.mysettings = mysettings; + this.froots = froots; + this.parentList = parentList; + isDir = ldir; + rootdir = froots; + serverPathToFile = froots; + + if(parent != null) + { + if(parent.endsWith("/.")) + parent = parent.substring(0,parent.length()-1); + else if(parent.endsWith(fs)) + parent = parent.substring(0,parent.length()); + + if(parent.equals(".")) + fullname = file; + else + { + fullname = parent + fs + file; + if(serverPathToFile.endsWith(fs)) + serverPathToFile = serverPathToFile.concat(parent); + else + serverPathToFile = serverPathToFile.concat(fs+parent); + } + } + + if(parentList != null) + { + if(parentList.isDirectory(file)) + isDir = true; + } + else if(parent == null) + fullname = "."; + + setUserObject(file); + } + + /** @return true if node is a directory */ + public boolean getAllowsChildren() { return isDir; } + /** @return true if node is a file */ + public boolean isLeaf() { return !isDir; } + /** @return true if node is a directory */ + public boolean isDirectory() { return isDir; } + /** @return the node name */ + public String getFile() { return (String)getUserObject(); } + /** @return root directory */ + public String getRootDir() { return rootdir; } + /** @return full name of the node */ + public String getFullName() { return fullname; } + /** @return path on server */ + public String getPathName() { return serverPathToFile; } + /** @return true if explored */ + public boolean isExplored() { return explored; } + + protected void setDir(boolean isDir) + { + this.isDir = isDir; + } + + /** + * + * Get the server name + * @return server name + * + */ + public String getServerName() + { + String prefix = serverPathToFile; + if(!prefix.endsWith(fs)) + prefix = prefix.concat(fs); + + if(fullname.equals(".")) + return prefix; + + return prefix + (String)getUserObject(); + } + + /** + * + * Explore the node and add new child nodes + * + */ + public void explore() + { + if(!isDir) + return; + + if(!explored) + { + FileList flist = new FileList(); + + String dir; + if(getRootDir().equals("")) + dir = new String("~/"+getFullName()); + else + dir = new String(getRootDir()+"/"+getFullName()); + dir = dir.trim(); + + flist.getDirList(dir); + Vector children = flist.fileVector(); + for(int i=0;i<children.size();i++) + add(new RemoteFileNode(mysettings,froots,(String)children.get(i), + flist,fullname)); + } + explored = true; + } + + protected boolean delete() + { + FileList flist = new FileList(); + return flist.delete(getRootDir()+"/"+getFullName()); + } + + protected boolean mkdir(String dir) + { + FileList flist = new FileList(); + return flist.mkdir(dir); + } + + protected boolean rename(String new_file) + { + FileList flist = new FileList(); + return flist.rename(getRootDir()+"/"+getFullName(), new_file); + } + + protected boolean put(File local_file) + { + FileList flist = new FileList(); + return flist.put(getRootDir()+"/"+getFullName(), local_file); + } + + + public byte[] getFileContents() + { + FileList flist = new FileList(); + return flist.getFileContents(getRootDir()+"/"+getFullName()); + } + +// Transferable + public DataFlavor[] getTransferDataFlavors() + { + return remoteFlavors; + } + + public boolean isDataFlavorSupported(DataFlavor f) + { + if(f.equals(REMOTEFILENODE) || f.equals(DataFlavor.stringFlavor)) + return true; + return false; + } + + public Object getTransferData(DataFlavor d) + throws UnsupportedFlavorException, IOException + { + if(d.equals(REMOTEFILENODE)) + return this; + else if(d.equals(DataFlavor.stringFlavor)) + return getServerName(); + else throw new UnsupportedFlavorException(d); + } + +// Serializable + private void writeObject(java.io.ObjectOutputStream out) throws IOException + { + out.defaultWriteObject(); + } + + private void readObject(java.io.ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + } + +} + diff --git a/uk/ac/sanger/artemis/components/filetree/SshFileTree.java b/uk/ac/sanger/artemis/components/filetree/SshFileTree.java new file mode 100644 index 0000000000000000000000000000000000000000..19be4f2a0d514dccb5912bb23ac831eef8762594 --- /dev/null +++ b/uk/ac/sanger/artemis/components/filetree/SshFileTree.java @@ -0,0 +1,1000 @@ +/******************************************************************** +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library 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 +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* @author: Copyright (C) Tim Carver +* +********************************************************************/ + +package uk.ac.sanger.artemis.components.filetree; + +import uk.ac.sanger.artemis.components.SwingWorker; +import uk.ac.sanger.artemis.components.EntryEdit; +import uk.ac.sanger.artemis.components.EntryFileDialog; +import uk.ac.sanger.artemis.io.EntryInformation; +import uk.ac.sanger.artemis.io.SimpleEntryInformation; +import uk.ac.sanger.artemis.*; +import uk.ac.sanger.artemis.util.RemoteFileDocument; +import uk.ac.sanger.artemis.util.OutOfRangeException; +import uk.ac.sanger.artemis.sequence.NoSequenceException; +import uk.ac.sanger.artemis.components.MessageDialog; + +import java.awt.*; +import java.awt.event.*; +import java.awt.datatransfer.*; +import java.awt.dnd.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.tree.*; +import java.io.*; +import java.util.*; + + +/** +* +* Creates a remote file tree which is a drag source & sink +* +*/ +public class SshFileTree extends JTree implements DragGestureListener, + DragSourceListener, DropTargetListener, ActionListener, + Autoscroll +{ + + /** properties */ + private Properties mysettings; + /** remote directory roots */ + private static String froots; + /** popup menu */ + private JPopupMenu popup; + /** file separator */ + private String fs = new String(System.getProperty("file.separator")); + /** line separator */ + private String ls = new String(System.getProperty("line.separator")); + /** store of directories that are opened */ + private Vector openNode; + /** busy cursor */ + private Cursor cbusy = new Cursor(Cursor.WAIT_CURSOR); + /** done cursor */ + private Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR); + /** AutoScroll margin */ + private static final int AUTOSCROLL_MARGIN = 45; + /** used by AutoScroll method */ + private Insets autoscrollInsets = new Insets( 0, 0, 0, 0 ); + + + /** + * + * @param froots remote directory roots + * + */ + public SshFileTree(String froots) + { + this.froots = froots; + + DragSource dragSource = DragSource.getDefaultDragSource(); + dragSource.createDefaultDragGestureRecognizer( + this, // component where drag originates + DnDConstants.ACTION_COPY_OR_MOVE, // actions + this); // drag gesture recognizer + + setDropTarget(new DropTarget(this,this)); + DefaultTreeModel model = createTreeModel(froots); + setModel(model); + createTreeModelListener(); + + this.getSelectionModel().setSelectionMode + (TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); + +// popup menu + addMouseListener(new PopupListener()); + popup = new JPopupMenu(); + JMenuItem menuItem = new JMenuItem("Refresh"); + menuItem.addActionListener(this); + popup.add(menuItem); + popup.add(new JSeparator()); +//open menu + JMenu openMenu = new JMenu("Open With"); + popup.add(openMenu); + menuItem = new JMenuItem("Jemboss Aligmnment Editor"); + menuItem.addActionListener(this); + openMenu.add(menuItem); + menuItem = new JMenuItem("Artemis"); + menuItem.addActionListener(this); + openMenu.add(menuItem); + + menuItem = new JMenuItem("Rename..."); + menuItem.addActionListener(this); + popup.add(menuItem); + menuItem = new JMenuItem("New Folder..."); + menuItem.addActionListener(this); + popup.add(menuItem); + menuItem = new JMenuItem("Delete..."); + menuItem.addActionListener(this); + popup.add(menuItem); + popup.add(new JSeparator()); + menuItem = new JMenuItem("De-select All"); + menuItem.addActionListener(this); + popup.add(menuItem); + + + //Listen for when a file is selected + addMouseListener(new MouseListener() + { + public void mouseClicked(MouseEvent me) + { + if(me.getClickCount() == 2 && isFileSelection()) + { + RemoteFileNode node = (RemoteFileNode)getLastSelectedPathComponent(); + if(node==null) + return; + setCursor(cbusy); + if(node.isLeaf()) + showFilePane(node); + setCursor(cdone); + } + } + public void mousePressed(MouseEvent me){} + public void mouseEntered(MouseEvent me){} + public void mouseExited(MouseEvent me){} + public void mouseReleased(MouseEvent me){} + }); + + addTreeExpansionListener(new TreeExpansionListener() + { + public void treeExpanded(TreeExpansionEvent e) + { + TreePath path = e.getPath(); + if(path != null) + { + setCursor(cbusy); + RemoteFileNode node = (RemoteFileNode)path.getLastPathComponent(); + + if(!node.isExplored()) + exploreNode(node); + setCursor(cdone); + } + } + public void treeCollapsed(TreeExpansionEvent e){} + }); + + } + + + /** + * + * This is used to refresh the file manager + * + */ + public void refreshRoot() + { + DefaultTreeModel model = (DefaultTreeModel)getModel(); + model = createTreeModel(froots); + setModel(model); + } + + + /** + * + * Define a directory root for the file tree + * @param newRoot directory to use as the root for + * the tree. + * + */ + public void up() + { + File current = new File(froots); + froots = current.getParentFile().getAbsolutePath(); + DefaultTreeModel model = (DefaultTreeModel)getModel(); + model = createTreeModel(froots); + setModel(model); + } + + /** + * + * Popup menu actions + * @param e action event + * + */ + public void actionPerformed(ActionEvent e) + { + + JMenuItem source = (JMenuItem)(e.getSource()); + final RemoteFileNode node = getSelectedNode(); + if(node == null) + { + JOptionPane.showMessageDialog(null,"No file selected.", + "Warning", + JOptionPane.WARNING_MESSAGE); + return; + } + + final String fn = node.getFullName(); + final String parent = node.getPathName(); + String rootPath = node.getRootDir(); + RemoteFileNode pn = node; + + if(source.getText().equals("Refresh")) + { + refreshRoot(); + } + else if(source.getText().equals("Jemboss Aligmnment Editor")) + { + final byte[] contents = node.getFileContents(); + org.emboss.jemboss.editor.AlignJFrame ajFrame = + new org.emboss.jemboss.editor.AlignJFrame(new String(contents), fn); + ajFrame.setVisible(true); + } + else if(source.getText().equals("Artemis")) + showFilePane(node); + else if(source.getText().equals("New Folder...")) + { + final String inputValue = JOptionPane.showInputDialog(null, + "Folder Name","Create New Folder in", + JOptionPane.QUESTION_MESSAGE); + + String dropDest = null; + + if(node.isLeaf()) + { + pn = (RemoteFileNode)node.getParent(); + dropDest = pn.getFullName() + "/" + inputValue; //assumes unix file sep.! + } + else + dropDest = node.getFullName() + "/" + inputValue; + + String newNode = pn.getServerName(); + if(!newNode.endsWith("/")) + newNode = newNode.concat("/"); + newNode = newNode.concat(inputValue); + + if(nodeExists(pn,newNode)) + return; + + if(inputValue != null && !inputValue.equals("") ) + { + final RemoteFileNode pnn = pn; + node.mkdir(newNode); + + Runnable addDirToTree = new Runnable() + { + public void run () { addObject(pnn,inputValue,true); }; + }; + SwingUtilities.invokeLater(addDirToTree); + } + } + else if(source.getText().equals("Delete...")) + { + RemoteFileNode nodes[] = getSelectedNodes(); + String sname = ""; + for(int i=0;i<nodes.length;i++) + sname = sname.concat(nodes[i].getServerName()+ls); + + int n = JOptionPane.showConfirmDialog(null, + "Delete"+ls+sname+"?", "Delete "+sname, + JOptionPane.YES_NO_OPTION); + if(n == JOptionPane.YES_OPTION) + for(int i=0;i<nodes.length;i++) + deleteNode(nodes[i]); + } + else if(source.getText().equals("De-select All")) + clearSelection(); + else if(source.getText().equals("Rename...")) + { + if(node.isLeaf()) + { + String inputValue = (String)JOptionPane.showInputDialog(null, + "New File Name","Rename "+fn, + JOptionPane.QUESTION_MESSAGE,null,null,fn); + + pn = (RemoteFileNode)node.getParent(); + + if(inputValue != null && !inputValue.equals("") ) + { + String newfile = null; + if(parent.endsWith("/")) + newfile = parent+inputValue; + else + newfile = parent+"/"+inputValue; + String dir = ((RemoteFileNode)node.getParent()).getFullName(); + if(inputValue.indexOf("/") > 0) + { + int index = inputValue.lastIndexOf("/"); + dir = inputValue.substring(0,index); + } + + RemoteFileNode parentNode = getNode(dir); + if(!nodeExists(parentNode,newfile)) + rename(rootPath,fn,parent,inputValue,node,parentNode); + } + } + } + } + + + /** + * + * Delete a node (file or directory) from the tree + * and from the server + * @param node node to remove + * + */ + private void deleteNode(final RemoteFileNode node) + { + setCursor(cbusy); + + boolean deleted = false; + deleted = node.delete(); + + if(!deleted && !node.isLeaf()) + JOptionPane.showMessageDialog(null,"Cannot delete"+ls+ + node.getServerName()+ + ls+"this directory is not empty","Warning", + JOptionPane.ERROR_MESSAGE); + else if(deleted) + { + Runnable deleteFileFromTree = new Runnable() + { + public void run () { deleteObject(node); }; + }; + SwingUtilities.invokeLater(deleteFileFromTree); + } + + setCursor(cdone); + } + + + /** + * + * Explore a directory node + * @param dirNode direcory node to display + * + */ + public void exploreNode(RemoteFileNode dirNode) + { + DefaultTreeModel model = (DefaultTreeModel)getModel(); + dirNode.explore(); + openNode.add(dirNode); + model.nodeStructureChanged(dirNode); + } + + + /** + * + * Test if a child node exists + * @param parentNode parent node + * @param child child to test for + * + */ + public boolean nodeExists(RemoteFileNode parentNode,String child) + { + RemoteFileNode childNode = getChildNode(parentNode,child); + if(childNode != null) + { + String ls = System.getProperty("line.separator"); + JOptionPane.showMessageDialog(null, child+ls+" already exists!", + "File Exists", + JOptionPane.ERROR_MESSAGE); + return true; + } + + return false; + } + + + /** + * + * Rename a node from the tree + * @param rootPath root path + * @param fullname full name of node to rename + * @param pathToNewFile path to new file + * @param newfile new file name + * @param node node to rename + * + */ + private void rename(String rootPath, final String fullname, + String pathToNewFile, final String newfile, + final RemoteFileNode node, final RemoteFileNode parentNode) + { + setCursor(cbusy); + node.rename(pathToNewFile+"/"+newfile); + setCursor(cdone); + Runnable deleteFileFromTree = new Runnable() + { + public void run () + { + addObject(parentNode,newfile,false); + deleteObject(node); + }; + }; + SwingUtilities.invokeLater(deleteFileFromTree); + } + + + /** + * + * Adding a file (or directory) to the file tree manager. + * This looks to see if the directory has already been opened + * and updates the filetree if it has. + * @param parentNode parent node + * @param child file to add to the tree + * @param ldir true if child is a directory + * + */ + public void addObject(RemoteFileNode parentNode,String child, + boolean ldir) + { + DefaultTreeModel model = (DefaultTreeModel)getModel(); + + if(parentNode == null) + return; + + String path = parentNode.getFullName(); + //create new file node + if(path.equals(" ")) + path = ""; + + if(child.indexOf("/") > -1) + child = child.substring(child.lastIndexOf("/")+1); + + RemoteFileNode childNode = null; + if(!parentNode.isExplored()) + { + exploreNode(parentNode); + childNode = getNode(parentNode.getServerName() + "/" + child); + } + else + { + childNode = new RemoteFileNode(mysettings,froots, + child,null,path,ldir); + + //find the index for the child + int num = parentNode.getChildCount(); + int childIndex = num; + for(int i=0;i<num;i++) + { + String nodeName = ((RemoteFileNode)parentNode.getChildAt(i)).getFile(); + if(nodeName.compareTo(child) > 0) + { + childIndex = i; + break; + } + else if(nodeName.compareTo(child) == 0) //file already exists + { + childIndex = -1; + break; + } + } + if(childIndex != -1) + model.insertNodeInto(childNode,parentNode,childIndex); + } + + // Make sure the user can see the new node. + this.scrollPathToVisible(new TreePath(childNode.getPath())); + + return; + } + + + /** + * + * Delete a node from the tree + * @param node node to remove + * + */ + public void deleteObject(RemoteFileNode node) + { + RemoteFileNode parentNode = (RemoteFileNode)node.getParent(); + DefaultTreeModel model = (DefaultTreeModel)getModel(); + model.removeNodeFromParent(node); +// model.nodeStructureChanged(parentNode); + return; + } + + + /** + * + * Get RemoteFileNode of selected node + * @return node that is currently selected + * + */ + public RemoteFileNode getSelectedNode() + { + TreePath path = getLeadSelectionPath(); + if(path == null) + return null; + RemoteFileNode node = (RemoteFileNode)path.getLastPathComponent(); + return node; + } + + + /** + * + * Get RemoteFileNodes of selected nodes + * @return node that is currently selected + * + */ + public RemoteFileNode[] getSelectedNodes() + { + TreePath path[] = getSelectionPaths(); + if(path == null) + return null; + + int numberSelected = path.length; + RemoteFileNode nodes[] = new RemoteFileNode[numberSelected]; + for(int i=0;i<numberSelected;i++) + nodes[i] = (RemoteFileNode)path[i].getLastPathComponent(); + + return nodes; + } + + + /** + * + * Determine if selected node is a file + * @return true if the selected node is a file + * + */ + public boolean isFileSelection() + { + TreePath path = getLeadSelectionPath(); + if(path == null) + return false; + RemoteFileNode node = (RemoteFileNode)path.getLastPathComponent(); + return !node.isDirectory(); + } + + + /** + * + * Get the selected node file name + * @return file name + * + */ + public String getFilename() + { + TreePath path = getLeadSelectionPath(); + RemoteFileNode node = (RemoteFileNode)path.getLastPathComponent(); + return node.getServerName(); + } + + + /** + * + * Creates the tree model for the given root + * @param root root to create model for + * @return tree model + * + */ + private DefaultTreeModel createTreeModel(String root) + { + setCursor(cbusy); + + File f = new File(root); + RemoteFileNode rootNode = new RemoteFileNode(mysettings,froots, + f.getName(),null,null); + + rootNode.setDir(true); + rootNode.explore(); + openNode = new Vector(); + openNode.add(rootNode); + setCursor(cdone); + return new DefaultTreeModel(rootNode); + } + + /** + * + * Gets the node from the existing explored nodes and their children. + * @param path path to a file or directory + * @return corresponding node if the directory or + * file is visible otherwise returns null. + * + */ + private RemoteFileNode getNode(String path) + { + Enumeration en = openNode.elements(); + + while(en.hasMoreElements()) + { + RemoteFileNode node = (RemoteFileNode)en.nextElement(); + String nodeName = node.getFullName(); + + if(nodeName.equals(path)) + return node; + } + +// check children of explored nodes + en = openNode.elements(); + while(en.hasMoreElements()) + { + RemoteFileNode child = getChildNode((RemoteFileNode)en.nextElement(),path); + if(child != null) + return child; + } + + return null; + } + + + /** + * + * Gets the child node of a parent node + * @param parent parent node + * @param childName name of child + * @return the child node + * + */ + private RemoteFileNode getChildNode(RemoteFileNode parent, String childName) + { + for(Enumeration children = parent.children(); + children.hasMoreElements() ;) + { + RemoteFileNode childNode = (RemoteFileNode)children.nextElement(); + String nodeName = childNode.getServerName(); + if(childName.equals(nodeName)) + return childNode; + } + + return null; + } + + + /** + * + * Opens a JFrame with the file contents displayed. + * @param filename file name + * @param mysettings jemboss properties + * + */ + public static void showFilePane(final RemoteFileNode node) + { + SwingWorker entryWorker = new SwingWorker() + { + EntryEdit entry_edit; + public Object construct() + { + try + { + EntryInformation new_entry_information = + new SimpleEntryInformation(Options.getArtemisEntryInformation()); + + final Entry entry = new Entry(EntryFileDialog.getEntryFromFile( + null, new RemoteFileDocument(node), + new_entry_information, true)); + if(entry == null) + return null; + + final EntryGroup entry_group = + new SimpleEntryGroup(entry.getBases()); + + entry_group.add(entry); + entry_edit = new EntryEdit(entry_group); + return null; + } + catch(NoSequenceException e) + { + new MessageDialog(null, "read failed: entry contains no sequence"); + } + catch(OutOfRangeException e) + { + new MessageDialog(null, "read failed: one of the features in " + + " the entry has an out of range " + + "location: " + e.getMessage()); + + } + catch(NullPointerException npe){} + + return null; + } + + public void finished() + { + if(entry_edit != null) + entry_edit.setVisible(true); + } + }; + entryWorker.start(); + } + + +//////////////////// +// DRAG AND DROP +//////////////////// + public void dragGestureRecognized(DragGestureEvent e) + { + // ignore if mouse popup trigger + InputEvent ie = e.getTriggerEvent(); + if(ie instanceof MouseEvent) + if(((MouseEvent)ie).isPopupTrigger()) + return; + + // drag only files + if(isFileSelection()) + e.startDrag(DragSource.DefaultCopyDrop, // cursor + (Transferable)getSelectedNode(), // transferable data + this); // drag source listener + } + +// Source + public void dragDropEnd(DragSourceDropEvent e) {} + public void dragEnter(DragSourceDragEvent e) {} + public void dragExit(DragSourceEvent e) {} + public void dragOver(DragSourceDragEvent e) {} + public void dropActionChanged(DragSourceDragEvent e) {} + +// Target + public void dragEnter(DropTargetDragEvent e) + { + if(e.isDataFlavorSupported(FileNode.FILENODE) || + e.isDataFlavorSupported(RemoteFileNode.REMOTEFILENODE)) + e.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); + } + + public void drop(DropTargetDropEvent e) + { + Transferable t = e.getTransferable(); + + if(t.isDataFlavorSupported(RemoteFileNode.REMOTEFILENODE)) + { + try + { + Point ploc = e.getLocation(); + TreePath dropPath = getPathForLocation(ploc.x,ploc.y); + if(dropPath != null) + { + RemoteFileNode fn = + (RemoteFileNode)t.getTransferData(RemoteFileNode.REMOTEFILENODE); + + fn = getNode(fn.getServerName()); // need to get the instance of + // this directly to manipulate tree + String dropDest = null; + RemoteFileNode fdropPath = (RemoteFileNode)dropPath.getLastPathComponent(); + String dropRoot = fdropPath.getRootDir(); + String dropFile = null; + if(fdropPath.getFile().equals(" ")) + dropFile = fn.getFile(); + else + dropFile = fdropPath.getFile()+"/"+fn.getFile(); + + if(!nodeExists(fdropPath,fdropPath.getServerName()+fn.getFile())) + rename(fn.getRootDir(),fn.getFullName(), + fdropPath.getPathName(), + dropFile, fn, fdropPath); + } + } + catch(Exception ex){} + } + else if(t.isDataFlavorSupported(FileNode.FILENODE)) + { + try + { + Point ploc = e.getLocation(); + TreePath dropPath = getPathForLocation(ploc.x,ploc.y); + if (dropPath != null) + { + FileNode fn = (FileNode)t.getTransferData(FileNode.FILENODE); + File lfn = fn.getFile(); + + String dropDest = null; + RemoteFileNode fdropPath = (RemoteFileNode)dropPath.getLastPathComponent(); + String dropRoot = fdropPath.getRootDir(); + + RemoteFileNode pn = fdropPath; + if(fdropPath.isLeaf()) + { + pn = (RemoteFileNode)fdropPath.getParent(); + dropDest = pn.getFullName() + "/" + lfn.getName(); //assumes unix file sep.! + } + else + dropDest = fdropPath.getFullName()+ "/" + lfn.getName(); + + if(!nodeExists(pn,pn.getServerName()+lfn.getName())) + { + pn.put(lfn); + try + { + +// Vector params = new Vector(); +// byte[] fileData = getLocalFile(lfn); +// params.addElement("fileroot=" + dropRoot); +// params.addElement(dropDest); +// params.addElement(fileData); + +// setCursor(cbusy); +// PrivateRequest gReq = new PrivateRequest(mysettings,"EmbreoFile", +// "put_file",params); +// setCursor(cdone); + //add file to remote file tree + RemoteFileNode parentNode = fdropPath; + if(parentNode.isLeaf()) + parentNode = (RemoteFileNode)fdropPath.getParent(); + else + parentNode = fdropPath; + + if(parentNode.isExplored()) + addObject(parentNode,lfn.getName(),false); + else + { + exploreNode(parentNode); + RemoteFileNode childNode = getNode(parentNode.getServerName() + + "/" + lfn.getName()); + scrollPathToVisible(new TreePath(childNode.getPath())); + } + } + catch (Exception exp) + { + setCursor(cdone); + System.out.println("SshFileTree: caught exception " + dropRoot + + " Destination: " + dropDest + " Local File " + lfn.toString()); + } + } + else + e.rejectDrop(); + } + } + catch (Exception ex) + { + } + } + else + e.rejectDrop(); + + } + + /** + * + * When a suitable DataFlavor is offered over a remote file + * node the node is highlighted/selected and the drag + * accepted. Otherwise the drag is rejected. + * + */ + public void dragOver(DropTargetDragEvent e) + { + if (e.isDataFlavorSupported(FileNode.FILENODE)) + { + Point ploc = e.getLocation(); + TreePath ePath = getPathForLocation(ploc.x,ploc.y); + if (ePath == null) + e.rejectDrag(); + else + { + setSelectionPath(ePath); + e.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); + } + } + else if(e.isDataFlavorSupported(RemoteFileNode.REMOTEFILENODE)) + { + Point ploc = e.getLocation(); + TreePath ePath = getPathForLocation(ploc.x,ploc.y); + if (ePath == null) + { + e.rejectDrag(); + return; + } + RemoteFileNode node = (RemoteFileNode)ePath.getLastPathComponent(); + if(!node.isDirectory()) + e.rejectDrag(); + else + { + setSelectionPath(ePath); + e.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE); + } + } + else + e.rejectDrag(); + + } + + public void dropActionChanged(DropTargetDragEvent e) {} + public void dragExit(DropTargetEvent e){} + + +//////////////////// +// AUTO SCROLLING // +//////////////////// + /** + * + * Handles the auto scrolling of the JTree. + * @param location The location of the mouse. + * + */ + public void autoscroll( Point location ) + { + int top = 0, left = 0, bottom = 0, right = 0; + Dimension size = getSize(); + Rectangle rect = getVisibleRect(); + int bottomEdge = rect.y + rect.height; + int rightEdge = rect.x + rect.width; + if( location.y - rect.y < AUTOSCROLL_MARGIN && rect.y > 0 ) + top = AUTOSCROLL_MARGIN; + if( location.x - rect.x < AUTOSCROLL_MARGIN && rect.x > 0 ) + left = AUTOSCROLL_MARGIN; + if( bottomEdge - location.y < AUTOSCROLL_MARGIN && bottomEdge < size.height ) + bottom = AUTOSCROLL_MARGIN; + if( rightEdge - location.x < AUTOSCROLL_MARGIN && rightEdge < size.width ) + right = AUTOSCROLL_MARGIN; + rect.x += right - left; + rect.y += bottom - top; + scrollRectToVisible( rect ); + } + + + /** + * + * Gets the insets used for the autoscroll. + * @return The insets. + * + */ + public Insets getAutoscrollInsets() + { + Dimension size = getSize(); + Rectangle rect = getVisibleRect(); + autoscrollInsets.top = rect.y + AUTOSCROLL_MARGIN; + autoscrollInsets.left = rect.x + AUTOSCROLL_MARGIN; + autoscrollInsets.bottom = size.height - (rect.y+rect.height) + AUTOSCROLL_MARGIN; + autoscrollInsets.right = size.width - (rect.x+rect.width) + AUTOSCROLL_MARGIN; + return autoscrollInsets; + } + + /** + * + * Popup menu listener + * + */ + class PopupListener extends MouseAdapter + { + public void mousePressed(MouseEvent e) + { + maybeShowPopup(e); + } + + public void mouseReleased(MouseEvent e) + { + maybeShowPopup(e); + } + + private void maybeShowPopup(MouseEvent e) + { + if(e.isPopupTrigger()) + popup.show(e.getComponent(), + e.getX(), e.getY()); + } + } + + public static void main(String args[]) + { + final javax.swing.LookAndFeel look_and_feel = + javax.swing.UIManager.getLookAndFeel(); + + final javax.swing.plaf.FontUIResource font_ui_resource = + Options.getOptions().getFontUIResource(); + + java.util.Enumeration keys = UIManager.getDefaults().keys(); + while(keys.hasMoreElements()) + { + Object key = keys.nextElement(); + Object value = UIManager.get(key); + if(value instanceof javax.swing.plaf.FontUIResource) + UIManager.put(key, font_ui_resource); + } + + JFrame frame = new JFrame("SSH :: File Manager"); + JScrollPane jsp = new JScrollPane(new SshFileTree("/nfs/team81/tjc")); + frame.getContentPane().add(jsp); + frame.pack(); + frame.setVisible(true); + } + +}