/* LTOOLgui.java

   Java based graphical user interface for the LTOOLS toolkit.

   Requires Java 1.1 or newer. Does run as a standalone application, not as an applet.

   Copyright (C) 1999-2000 Werner Zimmermann(Werner.Zimmermann@fht-esslingen.de)
 */

import java.awt. *;
import java.awt.event. *;
import java.awt.dnd. *;
import java.awt.datatransfer. *;
import java.io. *;
import java.util. *;

import javax.swing. *;
import javax.swing.tree. *;
import javax.swing.event. *;
import javax.swing.SwingUtilities.*;


public class DirFilePane extends JSplitPane
{   static boolean DEBUG = ltoolgui.DEBUG;

    String currentDrive;

    boolean avoidSelectionRetrigger = false;
    String fileSelectionRetrigger = "";
    boolean avoidExpansionRetrigger = false;
    String fileExpansionRetrigger = "";
    boolean avoidDragRetrigger = false;

    static final int BUFSIZE = 32768;
    static final int stLINfn = 56;					//start column of LINUX filename in dirListing's lines

    String cache;
    String cachedDirectory;
    boolean cacheValid = false;

    //{{DECLARE_CONTROLS
     javax.swing.JScrollPane lScrollPane = new javax.swing.JScrollPane();
     javax.swing.JScrollPane rScrollPane = new javax.swing.JScrollPane();
    //}}
    Tree lTree;
    Tree rTree;

    boolean isLinux;

    //##### Constructor #######################################################################
    public DirFilePane(boolean isLinux, String drive, String directory)
    {   super();
	debugOutput("Creating DirFilePane isLinux=" + isLinux + " drive=" + drive + " directory=" + directory);
	currentDrive = drive;
	this.isLinux = isLinux;

	//{{INIT_CONTROLS
	setFont(new Font("SansSerif", Font.BOLD, 12));
	setSize(0, 0);
	lScrollPane.setOpaque(true);
	lScrollPane.setBounds(0, 0, 0, 0);
	rScrollPane.setOpaque(true);
	rScrollPane.setBounds(0, 0, 0, 0);
	lScrollPane.setMinimumSize(new Dimension(0, 0));
	rScrollPane.setMinimumSize(new Dimension(0, 0));
	setLeftComponent(lScrollPane);
	setRightComponent(rScrollPane);
	//}}
	setDividerLocation(200);
	lTree = new Tree(true);					//Create the trees
	rTree = new Tree(false);
	lTree.setNewRoot(directory);
	rTree.setNewRoot(directory);
	lTree.installListeners();
	rTree.installListeners();
	lTree.setBounds(0, 0, 0, 0);
	rTree.setBounds(0, 0, 0, 0);
	lScrollPane.setViewportView(lTree);			//Add the trees to the scroll pane
	rScrollPane.setViewportView(rTree);
	lTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);	//only allow single file selection
	rTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);

	//{{REGISTER_LISTENERS
	//}}
    }

    //##### Change a drive ############################################################################################
    //      Called by the 'Set Drive'-buttons in the Dos- or LinuxPane
    public void driveChanged(String newDrive, String rootDirectory)	//Called by the Drive buttons, when a new drive is set
    {   currentDrive = newDrive;
	cacheValid = false;
	lTree.setNewRoot(rootDirectory);			//Update the tree views
	rTree.setNewRoot(rootDirectory);
    }


    //######Debugging#################################################################################
    static void debugOutput(String text)			//Debug output to console
    {   if (DEBUG == true)
	    System.out.println(text);
    }


    //##### Class to handle our file system tree views ################################################################
    class Tree extends JTree implements TreeWillExpandListener, TreeSelectionListener, MouseListener, DropTargetListener,
     					DragSourceListener, DragGestureListener, KeyListener, Runnable
    {   DefaultMutableTreeNode root, node;
	DefaultTreeModel treeModel;

	Enumeration e = null;
	DefaultMutableTreeNode curNode = null, oldRoot = null, oldCurNode = null;
	int oldIndex = 0;

	boolean isLeftTree;
	DragSource dragSource;
	DropTarget dropTarget;

	//##### Constructor #######################################################################
	public Tree(boolean isLeftTree)
        {   super();
	    this.isLeftTree = isLeftTree;
	}

	//##### Install Listners, called, when the left and the right tree are constructed ########
	public void installListeners()
        {   addTreeWillExpandListener(this);
	    addTreeSelectionListener(this);
	    addMouseListener(this);
	    if (!isLeftTree)
	    	addKeyListener(this);
	    dragSource = new DragSource();
	    dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this);
	    dropTarget = new DropTarget(this, this);
	}

	//##### Set a new root for a tree, called by driveChanged #################################
	public void setNewRoot(String rootDirectory)
        {   debugOutput("setNewRoot " + rootDirectory);
	    if (isLinux)
            {   root = new DefaultMutableTreeNode(rootDirectory);
		if (buildNewLinuxTree(rootDirectory) == false)
                {   //JOptionPane.showMessageDialog(this, "Sorry, Linux drive  " + currentDrive + "  does not exist", "LTOOLS", JOptionPane.ERROR_MESSAGE);
		    root = new DefaultMutableTreeNode("DOES NOT EXIST - set another LINUX  drive");
		}
	    } else
            {   root = new DefaultMutableTreeNode(currentDrive + rootDirectory);
		if (buildNewDosTree(rootDirectory) == false)
                {   //JOptionPane.showMessageDialog(this, "Sorry, Dos drive  " + currentDrive + "  does not exist", "LTOOLS", JOptionPane.ERROR_MESSAGE);
		    root = new DefaultMutableTreeNode("DOES NOT EXIST - set another DOS drive");
		}
	    }
	    treeModel = new DefaultTreeModel(root);
	    setModel(treeModel);
    	    curNode = oldRoot = oldCurNode = null;
	}

	//##### Finds the index where to insert a new node 'node' into an tree with root node 'root' ##################
	int findIndexForAlphabeticInsert(DefaultMutableTreeNode root, DefaultMutableTreeNode node, boolean isDirectory)
	{   int curIndex=root.getChildCount();
	    boolean found=true;
	    boolean reDoIt=true;

/*	    if (curIndex > 100)					//No sorting for big directories to improve performance
	    	return curIndex;
*/
	    curIndex=0;

//	    debugOutput("\n##### Start search in root:"+root+"  for node:"+node);

	    if (root == oldRoot)
	    {
//	    	debugOutput("--- Search in oldRoot--->curNode:"+curNode+"   oldCurNode:"+oldCurNode+"   oldIndex:"+oldIndex);

/*		e = root.children();
	    	while(e.hasMoreElements())			//Search in the roots children enumeration
		{
	            debugOutput("----------"+(DefaultMutableTreeNode) e.nextElement());
		}
*/
            	if (oldCurNode==null)				//Previous item was added at the end of the list
            	{   if (curNode.toString().toUpperCase().compareTo(node.toString().toUpperCase()) < 0)	//New item has to added at the end of the list too
		    {	reDoIt=false;
//			debugOutput("+++++++++++++++++++ shortcut attach "+node+" at end of list at index "+oldIndex);
			curNode=oldCurNode;
			curIndex=oldIndex;
		    } else
		    {
//		    	debugOutput("+++++++++++++++++++ sorry, must do full search");
		    }
		} else
		{   if (   (curNode.toString().toUpperCase().compareTo(node.toString().toUpperCase()) < 0)
			&& (oldCurNode.toString().toUpperCase().compareTo(node.toString().toUpperCase()) > 0))
		    {	reDoIt=false;
//		    	debugOutput("+++++++++++++++++++ shortcut insert "+node+" between "+curNode+"  and  "+oldCurNode+" at index "+oldIndex);
		    	curNode=oldCurNode;
		    	curIndex=oldIndex;
		    }
		}
	    }


	    if (reDoIt)
	    {	curNode = null;
		curIndex= 0;

		e = root.children();
	    	while(e.hasMoreElements())			//Search in the roots children enumeration
	    	{   found = false;
	            curNode = (DefaultMutableTreeNode) e.nextElement();

//	            debugOutput("----- node:"+curNode);

	            if (curNode.toString().toUpperCase().compareTo(node.toString().toUpperCase()) > 0)
		    {   found = true;
		    	break;					//Break, when alphabetic position found
		    }
	    	}
	    	if (curNode!=null)
	    	    curIndex = root.getIndex(curNode);
	    	if (found==false)
	     	    curIndex++;
//	   	debugOutput("+++++ found at i="+curIndex+"   curNode:"+curNode+"   status:"+found);
	    }
	    oldRoot = root;
	    oldCurNode = curNode;
	    curNode = node;
	    oldIndex= curIndex+1;

	    return curIndex;
//	    return root.getChildCount();			//Use this if you want ldir's original order instead of sorting
	}

	//##### Builds up a new Dos tree, called by setNewRoot ####################################
	boolean buildNewDosTree(String rootDirectory)
        {   debugOutput("buildNewDosTree " + rootDirectory);
	    File DOSdir = new File(currentDrive + rootDirectory);
	    if (DOSdir.exists() == false)			//Check if drive and directory exist
		 return false;
	    if (DOSdir.isDirectory() == false)
		 return false;

	     String[] files = DOSdir.list();			//Get a list of the directory contents

	    for (int i = 0; i < files.length; i++)		//Convert the list into nodes
            {   File file = new File(currentDrive + rootDirectory + File.separator + files[i]);

		if (file.isDirectory())				//for directories we add a dummy child node
                {   node = new DefaultMutableTreeNode(files[i]);
//		    root.add(node);
		    root.insert(node,findIndexForAlphabeticInsert(root,node,true));
		    node.add(new DefaultMutableTreeNode(".."));
		} else if (isLeftTree == false)			//normal files only show up in right trees
                {
//                  root.add(new DefaultMutableTreeNode(files[i]));
		    node = new DefaultMutableTreeNode(files[i]);
		    root.insert(node,findIndexForAlphabeticInsert(root,node,false));
		}
	    }
	    return true;
	}

	//##### Builds up a new Linux tree, called by setNewRoot ####################################
	boolean buildNewLinuxTree(String rootDirectory)
        {   debugOutput("buildNewLinuxTree --- " + rootDirectory);
	    String command, response, line;
	    int i = 0;

	    if (cacheValid && rootDirectory.equals(cachedDirectory))
            {   response = cache;
	    } else
            {   command = "ldir -x -s" + currentDrive + " " + rootDirectory;
		response = execOperatingSystemCommand(command, false);
		cacheValid = true;
		cachedDirectory = rootDirectory;
		cache = response;
	    }

	    StringTokenizer token = new StringTokenizer(response, "\n\r");

	    command = token.nextToken();
	    if ((command.length() < stLINfn) || (getLdirField(command,9).equals(".") == false))
            {   return false;
	    }
	    command = token.nextToken();
	    if ((command.length() < stLINfn) || (getLdirField(command,9).equals("..") == false))
            {   return false;
	    }
	    while (token.hasMoreTokens())
            {   line = token.nextToken();
		if (line.substring(0, 1).equals("d"))
                {   node = new DefaultMutableTreeNode(getLdirField(line,9));
//		    root.add(node);
		    root.insert(node,findIndexForAlphabeticInsert(root,node,true));
		    node.add(new DefaultMutableTreeNode(".."));
		} else if (isLeftTree == false)			//normal files only show up in right trees
                {   node = new DefaultMutableTreeNode(getLdirField(line,9));
//		    root.add(node);
		    root.insert(node,findIndexForAlphabeticInsert(root,node,false));
		}
	    }
	    return true;
	}

	//Extract a field from ldir's output
	//0=mode 1=uid 2=gid 3=length ... 9=file/directory name
	String getLdirField(String command, int fieldNr)
	{   StringTokenizer token = new StringTokenizer(command, " ");
	    int i = 0;
	    while (token.hasMoreTokens())
            {   command = token.nextToken();
		if (i==fieldNr)
                {   return command;
		}
		i++;
	    }
	    return "";
	}

	//##### Starts a file or its application when double clicked ##############################
	void startDosApplication(TreePath path)
        {   debugOutput("startDosApplication");
	    if (File.separator.equals("\\") == false)
            {   JOptionPane.showMessageDialog(this, "Sorry, starting applications only supported under Dos", "LTOOLS", JOptionPane.ERROR_MESSAGE);
		return;
	    }
	    String currentFileFullPath = getName(path, false);
	    String command = "runit.bat " + currentFileFullPath;
	    command = execOperatingSystemCommand(command, true);
	}

	//##### Starts a file or its application when double clicked ##############################
	void startLinuxApplication(TreePath path)
        {   debugOutput("startLinuxApplication");
	    int i;
	    if (File.separator.equals("\\") == false)
            {   JOptionPane.showMessageDialog(this, "Sorry, starting applications only supported under Dos", "LTOOLS", JOptionPane.ERROR_MESSAGE);
		return;
	    }
	    String currentFileFullPath = getName(path, true);
	    String fileName = path.getLastPathComponent().toString();

	    if ((i = fileName.indexOf("->")) != -1)		//Handle symbolic links
		fileName = fileName.substring(i + 3);

	    String command;
	    if (ltoolgui.Connected)
		command = "ldir -READ -x -s" + currentDrive + " " + currentFileFullPath + " " + "zzz.xxx";
	    else
		command = "ldir -READ -x -s" + currentDrive + " " + currentFileFullPath + " " + fileName;
	    command = execOperatingSystemCommand(command, false);	//Copy the application from Linux to Dos

	    if (command.length() > 0)
	    {							//if LTOOLS's command line tool issues an error message, show it
		JOptionPane.showMessageDialog(this, command.trim(), "LTOOLS", JOptionPane.ERROR_MESSAGE);
		return;
	    }
	    if (ltoolgui.Connected)				//Remote files must be copied to the local machine, before we can start them
		getFileFromHost(fileName);

	    command = "runit.bat /wait " + fileName;		//Start it in Dos (wait, until application is closed again)
	    execOperatingSystemCommand(command, true);

	    File file = new File(fileName);			//Delete the Dos file again
	    if (file != null)
		file.delete();
	}


	//##### Make a new Dos directory ##########################################################
	void newDosDirectory(TreePath path)
        {   debugOutput("newDosDirectory");
	    String directoryFullPathName = getName(path, false);

	    ModifyDosDialog dialog = new ModifyDosDialog(ltoolgui.myProg, true, "", "", "", true, false);
	     dialog.setVisible(true);

	    if (dialog.getStatus() != ModifyDosDialog.OK)
		 return;

	    if (!dialog.getFileName().equals(""))
            {   File directory = new File(directoryFullPathName + File.separator + dialog.getFileName());
		if (!directory.mkdir())
		     JOptionPane.showMessageDialog(this, "Could not create directory " + directoryFullPathName + File.separator + dialog.getFileName(), "LTOOLS", JOptionPane.ERROR_MESSAGE);

		 rTree.expandDosTree(path);			//Update the tree
		 updateOtherTree(path);				//Update the other tree
	    }
	}

	//##### Make a new Linux directory ########################################################
	void newLinuxDirectory(TreePath path)
        {   debugOutput("newLinuxDirectory");
	    String directoryFullPathName = getName(path, true);

	    if (ltoolgui.isLinuxReadOnly==true)
	    { 	JOptionPane.showMessageDialog(this, "Your Linux filesystem is set to read only - change in menu 'File - Linux read only' first", "LTOOLS", JOptionPane.ERROR_MESSAGE);
	    	return;
	    }

	    ModifyLinuxDialog dialog = new ModifyLinuxDialog(ltoolgui.myProg, true, "", "", "", "          0", "          0", "-rwxr-xr-x");
	     dialog.setVisible(true);

	    if (dialog.getStatus() != ModifyLinuxDialog.OK)
		 return;

	    if (ltoolgui.Connected)
	    	putFileToHost("ldirtmpl");

	    String command = "ldir -WRITE ";
	    if (dialog.isLink())
		 command = command + "-link ";
	    else
		 command = command + "-mkdir ";
	     command = command + "-x -s" + currentDrive + " -f" + dialog.getAccess() + " -g" + dialog.getGid().trim() + " -u" + dialog.getUid().trim() + " ";
	    if (dialog.isLink())
	    {	 command = command + dialog.getTargetName() + " ";
	    } else
	    {   if (ltoolgui.Connected)
		    command = command + "zzz.xxx  ";
	    	else
	    	    command = command + "ldirtmpl ";
	    }

	    command = command + directoryFullPathName + "/" + dialog.getFileName();
	    command = execOperatingSystemCommand(command, false);
	    if (command.length() > 0)
	    {							//if LTOOLS's command line tool issues an error message, show first line of response in message box
		JOptionPane.showMessageDialog(this, command.trim(), "LTOOLS", JOptionPane.ERROR_MESSAGE);
		return;
	    }
	    cacheValid = false;
	    rTree.expandLinuxTree(path);			//Update the tree
	    updateOtherTree(path);				//Update the other tree
	}


	//##### Delete a Dos file or empty directory###############################################
	//      Called by the Del button or menu item
	public void deleteDosFile(TreePath path)
        {   String currentFileFullPath = getName(path, false);

	     debugOutput("deleteDosFile " + currentFileFullPath);

	    if (ltoolgui.myProg.ConfirmDelCheckBox.isSelected())
	    	if (JOptionPane.showConfirmDialog(this, "Do you really want to delete " + currentFileFullPath, "LTOOLS",
				    JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.NO_OPTION)
		    return;

	    File file = new File(currentFileFullPath);
	    if (file.delete() == false)
            {   JOptionPane.showMessageDialog(this, "Could not delete file or directory " + currentFileFullPath, "LTOOLS", JOptionPane.ERROR_MESSAGE);
		return;
	    }
	    TreePath parentPath = getParentPath(path);
	    rTree.expandDosTree(parentPath);			//Update the tree
	    updateOtherTree(parentPath);			//Update the other tree
	}

	//##### Delete a Linux file or empty directory#############################################
	//      Called by the Del button or menu item
	public void deleteLinuxFile(TreePath path)
        {   String currentFileFullPath = getName(path, true);

	     debugOutput("deleteLinuxFile " + currentFileFullPath);

	    if (ltoolgui.isLinuxReadOnly==true)
	    { 	JOptionPane.showMessageDialog(this, "Your Linux filesystem is set to read only - change in menu 'File - Linux read only' first", "LTOOLS", JOptionPane.ERROR_MESSAGE);
	    	return;
	    }

	    if (ltoolgui.myProg.ConfirmDelCheckBox.isSelected())
	    	if (JOptionPane.showConfirmDialog(this, "Do you really want to delete file " + currentFileFullPath, "LTOOLS",
				    JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.NO_OPTION)
		    return;

	    String command = "ldir -WRITE -x -del -s" + currentDrive + " " + currentFileFullPath;
	     command = execOperatingSystemCommand(command, false);
	    if (command.length() > 0)
	    {							//if LTOOLS's command line tool issues an error message, show first line of response in message box
		JOptionPane.showMessageDialog(this, command.trim(), "LTOOLS", JOptionPane.ERROR_MESSAGE);
		return;
	    }
	    cacheValid = false;
	    TreePath parentPath = getParentPath(path);
	    rTree.expandLinuxTree(parentPath);			//Update the tree
	    updateOtherTree(parentPath);			//Update the other tree
	}


	//##### Paste a Dos or Linux file into a DOS directory#####################################
	public boolean pasteToDos(TreePath path, boolean issueMessages)
        {   String targetPath = getName(path, false);

for (int j=0; j < ltoolgui.copyFileCount; j++)
{ 	    String targetFileFullPathname;
	    String sourcePath = getName(ltoolgui.copyFilePath[j], ltoolgui.copyFileIsLinux);
	    String targetFile = ltoolgui.copyFilePath[j].getLastPathComponent().toString();

	     debugOutput("pasteToDos " + sourcePath + " --> " + targetPath + " as " + targetFile);

  if (ltoolgui.copyFileCount==1)
  {
	    FileDialog saveDialog = new FileDialog(ltoolgui.myProg, "LTOOLS Copy file " + sourcePath, FileDialog.SAVE);
	     saveDialog.setDirectory(targetPath);		//Save file dialog
	     saveDialog.setFile(targetFile);
	     saveDialog.setVisible(true);
	    if ((targetFile = saveDialog.getFile()) == null)
            {   debugOutput("Empty file name, file not copied");
		return true;
	    }
	    targetFileFullPathname = saveDialog.getDirectory() + saveDialog.getFile();
  } else
  {	    targetFileFullPathname = targetPath + "\\" + targetFile;
  }
	    if (ltoolgui.copyFileIsLinux)			//Linux to Dos copy----------------
            {   String command;
		if (ltoolgui.Connected == false)
                {   command = "ldir -READ -x -s" + ltoolgui.copyFileDrive + " " + sourcePath + " " + targetFileFullPathname;
		} else
                {   command = "ldir -READ -x -s" + ltoolgui.copyFileDrive + " " + sourcePath + " " + "zzz.xxx";
		}
		command = execOperatingSystemCommand(command, false);
		if (command.length() > 0)
		{						//if LTOOLS's command line tool issues an error message, show first line of response in message box
		    if (issueMessages)
			JOptionPane.showMessageDialog(this, command.trim(), "LTOOLS", JOptionPane.ERROR_MESSAGE);
		    return false;
		}
		if (ltoolgui.Connected)
		    getFileFromHost(targetFileFullPathname);
	    } else
		//Dos to Dos copy------------------
            {   try
                {   FileInputStream fdIn = new FileInputStream(sourcePath);	//Open the files
		    FileOutputStream fdOut = new FileOutputStream(targetFileFullPathname);
		    int i;
		    byte[]buffer = new byte[BUFSIZE];
		    do						//Read the source and write to target
                    {   i = fdIn.read(buffer, 0, BUFSIZE);
			if (i < 0)
			    break;
			fdOut.write(buffer, 0, i);
		    }
		    while (i > 0);
		    fdIn.close();
		    fdOut.close();
		}
		catch(java.io.FileNotFoundException e)
                {   if (issueMessages)
			JOptionPane.showMessageDialog(this, "Sorry, could not open file " + sourcePath, "LTOOLS", JOptionPane.ERROR_MESSAGE);
		    return false;
		}
		catch(java.io.IOException e)
                {   if (issueMessages)
			JOptionPane.showMessageDialog(this, "Sorry, could not write to " + targetFileFullPathname, "LTOOLS", JOptionPane.ERROR_MESSAGE);
		    return false;
		}
	    }
}
	    ltoolgui.copyFileIsValid = false;
	    ltoolgui.copyFileCount=0;
	    rTree.expandDosTree(path);				//Update the tree
	    updateOtherTree(path);				//Update the other tree
	    return true;
	}

	//##### Paste a Dos or Linux file into a Linux directory###################################
	public boolean pasteToLinux(TreePath path, boolean issueMessages)
        {   String targetPath = getName(path, true);

for (int i=0; i < ltoolgui.copyFileCount; i++)
{
	    String sourcePath = getName(ltoolgui.copyFilePath[i], ltoolgui.copyFileIsLinux);
	    String targetFile = ltoolgui.copyFilePath[i].getLastPathComponent().toString();

	     debugOutput("pasteToLinux " + sourcePath + " --> " + targetPath + " as " + targetFile);

	    if (ltoolgui.isLinuxReadOnly==true)
	    { 	JOptionPane.showMessageDialog(this, "Your Linux filesystem is set to read only - change in menu 'File - Linux read only' first", "LTOOLS", JOptionPane.ERROR_MESSAGE);
	    	return false;
	    }

	    if (ltoolgui.copyFileIsLinux)			//Linux to Linux copy----------------
            {   String command;

		//Copy the file from Linux to Dos
		command = "ldir -READ -x -s" + ltoolgui.copyFileDrive + " " + sourcePath + " " + "zzz.tmp";
		command = execOperatingSystemCommand(command, false);
		if (command.length() > 0)
		{						//if LTOOLS's command line tool issues an error message, show first line of response in message box
		    if (issueMessages)
			JOptionPane.showMessageDialog(this, command.trim(), "LTOOLS", JOptionPane.ERROR_MESSAGE);
		    return false;
		}
		command = "ldir -WRITE -x -s" + currentDrive + " -copy " + "zzz.tmp" + " " + targetPath + "/" + targetFile;
		command = execOperatingSystemCommand(command, false);
		if (command.length() > 0)
		{						//if LTOOLS's command line tool issues an error message, show first line of response in message box
		    if (issueMessages)
			JOptionPane.showMessageDialog(this, command.trim(), "LTOOLS", JOptionPane.ERROR_MESSAGE);
		    return false;
		}
	    } else
		//Dos to Linux copy
            {   String command;
		if (ltoolgui.Connected == false)
                {   command = "ldir -WRITE -x -s" + currentDrive + " -copy " + sourcePath + " " + targetPath + "/" + targetFile;
		} else
                {   putFileToHost(sourcePath);
                    command = "ldir -WRITE -x -s" + currentDrive + " -copy " + "zzz.xxx" + " " + targetPath + "/" + targetFile;
		}
		command = execOperatingSystemCommand(command, false);
		if (command.length() > 0)
		{						//if LTOOLS's command line tool issues an error message, show first line of response in message box
		    if (issueMessages)
			JOptionPane.showMessageDialog(this, command.trim(), "LTOOLS", JOptionPane.ERROR_MESSAGE);
		    return false;
		}
	    }
}
	    ltoolgui.copyFileIsValid = false;
	    ltoolgui.copyFileCount=0;
	    cacheValid = false;
	    rTree.expandLinuxTree(path);			//Update the tree
	    updateOtherTree(path);				//Update the other tree
	    return true;
	}

	//##### Modify the name or attributes of a Dos file or directory ##########################
	public void modifyDosFile(TreePath path)
        {   String fileFullPathName = getName(path, false);
	    String fileName = path.getLastPathComponent().toString();
	     debugOutput("modifyDosFile " + fileFullPathName);
	    File file = new File(fileFullPathName);

	    String fileDate = (new Date(file.lastModified())).toString();
	    String fileLength = new Long(file.length()).toString();

	    ModifyDosDialog dialog = new ModifyDosDialog(ltoolgui.myProg, false, fileName, fileDate, fileLength, file.isDirectory(), !file.canWrite());
	     dialog.setVisible(true);

	    if (dialog.getStatus() != ModifyDosDialog.OK)
		 return;

	    if (!dialog.getFileName().equals(fileName))
            {   file.renameTo(new File(file.getParent() + file.separator + dialog.getFileName()));

		TreePath parentPath = getParentPath(path);
		 rTree.expandDosTree(parentPath);		//Update the tree
		 updateOtherTree(parentPath);			//Update the other tree
	    }
	}

	//##### Modify the name or attributes of a Linux file or directory ########################
	public void modifyLinuxFile(TreePath path)
        {   String fileFullPathName = getName(path, true);
	    String fileName = path.getLastPathComponent().toString();

	     debugOutput("modifyLinuxfile " + fileFullPathName);

	    String command, access = "", uid = "      ", gid = "      ", length = "", date = "", time = "";

	     command = "ldir -x -s" + currentDrive + " " + fileFullPathName;
	     command = execOperatingSystemCommand(command, false);

	    StringTokenizer token = new StringTokenizer(command, " ");
	    int i = 0;
	    while (token.hasMoreTokens())
            {   command = token.nextToken();
		switch (i)
                {   case 0:
			access = command;
			break;
		    case 1:uid = uid + command;
			break;
		    case 2:gid = gid + command;
			break;
		    case 3:length = command;
			break;
		    case 5:date = command;
			break;
		    case 6:date = date + " " + command;
			break;
		    case 7:time = command;
			break;
		    case 8:date = date + " " + command;
			break;
		}
		i++;
	    }

	    ModifyLinuxDialog dialog = new ModifyLinuxDialog(ltoolgui.myProg, false, fileName, date + " " + time, length,
							     uid, gid, access);
	    dialog.setVisible(true);

	    if (dialog.getStatus() != ModifyLinuxDialog.OK)
		return;

	    if (ltoolgui.isLinuxReadOnly==true)
	    { 	JOptionPane.showMessageDialog(this, "Your Linux filesystem is set to read only - change in menu 'File - Linux read only' first", "LTOOLS", JOptionPane.ERROR_MESSAGE);
	    	return;
	    }

	    command = "ldir -WRITE ";
	    if (!dialog.getFileName().equals(fileName))
		command = command + "-ren ";
	    command = command + "-x -s" + currentDrive + " -f" + dialog.getAccess() + " ";
	    if (!dialog.getGid().equals(gid))
		command = command + "-g" + dialog.getGid() + " ";
	    if (!dialog.getUid().equals(uid))
		command = command + "-u" + dialog.getUid() + " ";
	    command = command + fileFullPathName + " ";
	    if (!dialog.getFileName().equals(fileName))
		command = command + dialog.getFileName();
	    command = execOperatingSystemCommand(command, false);
	    if (command.length() > 0)
	    {							//if LTOOLS's command line tool issues an error message, show first line of response in message box
		JOptionPane.showMessageDialog(this, command.trim(), "LTOOLS", JOptionPane.ERROR_MESSAGE);
		return;
	    }
	    cacheValid = false;
	    TreePath parentPath = getParentPath(path);
	    rTree.expandLinuxTree(parentPath);			//Update the tree
	    updateOtherTree(parentPath);			//Update the other tree
	}

	//##### Show all Linux partitions on all disks ############################################
	public String showPartitions()
	{   String rawInfo = execOperatingSystemCommand("ldir -x -part",true);
	    String partInfo="Partition table info:\n";
	    String temp,temp1;
	    String disk="";

	    int i=0,j=0, k=0;
	    while (true)
	    {  	i = rawInfo.indexOf("#",i);			//Find line starting with '#'
	    	if (i<0)					//Bail out, if all lines have been scanned
	    	    break;
    	    	j = rawInfo.indexOf("\n",i);			//Find end of this line

		if ((k = rawInfo.substring(i,j).indexOf("Disk ")) > 0)		//Line containing disk info
		{   temp = rawInfo.substring(i,j).substring(k+5,k+8);
	    	    if (temp.equals("128"))
	    	    	disk="/dev/hda";
	    	    else if (temp.equals("129"))
	    	    	disk="/dev/hdb";
	    	    else if (temp.equals("130"))
	    	    	disk="/dev/hdc";
	    	    else if (temp.equals("131"))
	    	    	disk="/dev/hdd";
	    	    else if (temp.equals("132"))
	    	    	disk="/dev/hde";
	    	    else if (temp.equals("133"))
	    	    	disk="/dev/hdf";
	    	    else if (temp.equals("134"))
	    	    	disk="/dev/hdg";
	    	    else if (temp.equals("135"))
	    	    	disk="/dev/hdh";
	    	    else
	    	    	disk=temp;
		    partInfo=partInfo+"------------------------------------------------\n";

		} else if ((k = rawInfo.substring(i,j).indexOf("Type:")) > 0)	//Line containing partition info
		{   temp1= rawInfo.substring(i,j).substring(1,3);		//Partition number
		    temp = rawInfo.substring(i,j).substring(k+5,k+25);		//Partition type
	    	    partInfo=partInfo+" " + disk + temp1 +" :   "+ temp +"\n";
		}

	    	i=j;						//goto next line
	    }

	    return partInfo;
	}

	//##### Execute an operating system command and return commands response ##################
	String execOperatingSystemCommand(String command, boolean runLocal)
        {   String response = "";
	    Runtime r = Runtime.getRuntime();
	    Process myProcess;

	    int i = 0;
	     byte[] buffer = new byte[BUFSIZE];

	    if (ltoolgui.Connected && !runLocal)
            {   return remoteOperatingSystemCommand(command);
	    }
	    debugOutput("Executing operating system command: \n   " + command);

	    try
            {   myProcess = r.exec(ltoolgui.lreadguiDirectory+command);		//Execute operating system command as external process
		//Output goes to file 'zzz.zwz'
		try
                {   myProcess.waitFor();			//Wait until process has finished
		}
		catch(InterruptedException excpt)
                {   System.err.println("Failed Process waitFor " + excpt);
		}

		FileInputStream fd = new FileInputStream("zzz.zwz");	//Read in command's response

		do
                {   i = fd.read(buffer, 0, BUFSIZE);
		    if (i < 0)
			break;
		    response = response + new String(buffer, 0, i);
		}
		while (i > 0);
	    }
	    catch(IOException excpt)
            {   System.err.println("Failed I/O " + excpt);
		return "Executing operating system command failed:\n   " + command;
	    }
	    return response;
	}

	//##### Execute an operating system command on a remote computer and return commands response
	String remoteOperatingSystemCommand(String command)
        {   debugOutput("Executing command on remote server: \n   " + command);

	    String response = "";
	    String temp;
	    int bytesRead, i, j;
	    char[] buffer = new char[BUFSIZE];

	     try
            {   temp = "EXECUTE\n" + command + "\n";
		ltoolgui.qSend.write(temp, 0, temp.length());	//Send command to remote server
		ltoolgui.qSend.flush();

		temp = ltoolgui.qReceive.readLine();
		i = Integer.parseInt(temp);

		bytesRead = 0;
		while (bytesRead < i)
                {   j = ltoolgui.qReceive.read(buffer, 0, i);
		    temp = new String(buffer, 0, j);
		    bytesRead = bytesRead + j;
		    response = response + temp;
		}
	    }
	    catch(IOException excpt)
            {   System.err.println("Failed I/O to " + excpt);
	    }

	    return response;
	}

	//##### Put file "fileName" to remote host ############################
	//      file will be stored as "zzz.xxx"
	void putFileToHost(String fileName)
        {   //send command RECEIVEFILE\n filelength\n  file
	    String temp="";
	    int     bytesRead=0, bytesToRead,i,j;
	    byte[]  buffer= new byte[BUFSIZE];

	    try
            {  File DOSfile = new File(fileName);

               FileInputStream fd = new FileInputStream(fileName);

               temp="RECEIVEFILE\n"+DOSfile.length()+"\n";
               ltoolgui.qSend.write(temp,0,temp.length());			//Send command to remote server
               ltoolgui.qSend.flush();

	       do
	       {   i = fd.read(buffer,0, BUFSIZE);
		   if (i<0) break;
	           ltoolgui.bSend.write(buffer,0,i);
	           bytesRead=bytesRead+i;
	       } while (i>0);
               ltoolgui.bSend.flush();
	       fd.close();

	    } catch(IOException excpt)
            {   System.err.println("Failed I/O " + excpt);
	        return;
	    }
	}

	//##### Get a file "zzz.xxx" from a remote host #######################
	//      file will be stored as 'fileName'
	void getFileFromHost(String fileName)
        {   String temp = "";
	    int bytesRead = 0, bytesToRead, i, j;
	     byte[] buffer = new byte[BUFSIZE];

	     try
            {   temp = "SENDFILE" + "\n";
		ltoolgui.qSend.write(temp, 0, temp.length());	//Send command to server
		ltoolgui.qSend.flush();

		i = ltoolgui.bReceive.read(buffer, 0, BUFSIZE);	//Receive response
		temp = new String(buffer, 0, 32);		//Find out file length
		bytesToRead = Integer.parseInt(temp.substring(0, temp.indexOf('\n')));

		for (j = 0; j < i; j++)				//Find the start of data in buffer
                {   if (buffer[j] == 0x0D)			//DOS and UNIX files
			if (buffer[j] == 0x0A)
                        {   j = j + 2;
			    break;
			} else
                        {   j = j + 1;
			    break;
			}
		    if (buffer[j] == 0x0A)
			if (buffer[j] == 0x0D)
                        {   j = j + 2;
			    break;
			} else
                        {   j = j + 1;
			    break;
			}
		}

		FileOutputStream fd = new FileOutputStream(fileName);
		bytesRead = i - j;
		fd.write(buffer, j, bytesRead);

		while (bytesRead < bytesToRead)
                {   i = ltoolgui.bReceive.read(buffer, 0, BUFSIZE);
		    bytesRead = bytesRead + i;
		    fd.write(buffer, 0, i);
		}
		fd.close();

	    }
	    catch(IOException excpt)
            {   System.err.println("Failed I/O " + excpt);
		return;
	    }
	}

	//##### Update the tree in the other window, needed, when a path is expanded or deleted
	void updateOtherTree(TreePath path)
        {   debugOutput("updateOtherTree " + (isLeftTree ? "lTree" : "rTree") + (isLinux ? " Linux " : " Dos   ") + path);
	    TreePath otherPath;					//Path to the same node in the partner tree
	    Tree otherTree;					//The partner tree (rTree if lTree is clicked and vice versa);

	    if (isLeftTree)
            {   otherTree = rTree;
	    } else
            {   otherTree = lTree;
	    }
	    otherPath = getTreePathFromTree(path, otherTree);	//Get the path to the same node in the other tree
	    if (otherPath == null)				//If not found, return
		return;
	    if (isLinux)
		otherTree.expandLinuxTree(otherPath);
	    else
		otherTree.expandDosTree(otherPath);
	    otherTree.expandPath(otherPath);			//Expand this node in the other tree too
	    otherTree.scrollPath(otherPath);			//Make it visible in the other tree
	}


	public void scrollPath(TreePath path)
        {   debugOutput("scrollPath      " + (isLeftTree ? "lTree" : "rTree") + (isLinux ? " Linux " : " Dos   ") + path);
	    TreePath localPath = path;

	    int i = getRowForPath(localPath);			//Make a path visible
	    int rowIncrement = getVisibleRowCount() / 2;
	    int maxRows = getRowCount() - 1;
	    int j = i + rowIncrement - 1;
	    if (j > maxRows)
		 j = maxRows;

	     scrollRowToVisible(0);
	     scrollRowToVisible(j);
	}

	//##### Select a node in another tree##################################
	void selectOtherTree(TreePath path)
        {   debugOutput("selectOtherTree " + (isLeftTree ? "lTree" : "rTree") + (isLinux ? " Linux " : " Dos   ") + path);
	    TreePath otherPath;					//Path to the same node in the partner tree
	    Tree otherTree;					//The partner tree (rTree if lTree is clicked and vice versa);

	    if (isLeftTree)
            {   otherTree = rTree;
	    } else
            {   otherTree = lTree;
		if (((DefaultMutableTreeNode) path.getLastPathComponent()).isLeaf())	//Left tree does only contain directories, can't select files
                {   otherTree.clearSelection();
		    return;
		}
	    }
	    otherPath = getTreePathFromTree(path, otherTree);	//Get the path to the same node in the other tree
	    if (otherPath == null)
            {   otherTree.clearSelection();			//If not found in the other tree, at least clear the selection in the other tree
		return;
	    }
	    otherTree.scrollPath(otherPath);			//Make it visible in the other tree
	    otherTree.setSelectionPath(otherPath);		//Select the node in both trees
	}

	//##### Converts a Dos or Linux path to a string representing the full pathname############
	String getName(TreePath path, boolean isLinux)
        {   String currentFileFullPath, delimiter;
	    if (isLinux)
		 delimiter = "/";
	    else
		 delimiter = File.separator;
	    if (path.getPathCount() >= 2)
		 currentFileFullPath = path.getPathComponent(0).toString() + path.getPathComponent(1).toString();	//Build the full pathname
	    else
		 currentFileFullPath = path.getPathComponent(0).toString();
	    for (int i = 2; i < path.getPathCount(); i++)
		 currentFileFullPath = currentFileFullPath + delimiter + path.getPathComponent(i);
	     return currentFileFullPath;
	}

	//##### Gets a the path to a node in a tree for a given path ##########
	//      Used to find the path to a node in the left tree, when a node in the right tree was selected and vice versa
	TreePath getTreePathFromTree(TreePath path, Tree tree)
	{							//debugOutput("getTreePathFromTree path="+path+ (isLeftTree?"   lTree":"    rTree") + (isLinux?"    Linux":"    Dos"));
	    int i, j;
	    boolean found = false;

	    DefaultMutableTreeNode target;
	    DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getModel().getRoot();
	     Object[] myTreePath = new Object[path.getPathCount()];	//Object to hold the nodes for the new path

	     myTreePath[0] = node;				//Start at the root node in the tree

	    for (i = 1; i < path.getPathCount(); i++)		//Scan the path element per element
            {   target = (DefaultMutableTreeNode) path.getPathComponent(i);	//next path element
		if (target == null)
		    return null;
		for (j = 0; j < node.getChildCount(); j++)	//Scan the tree children, to find the one, which is equal to the path element
                {   if ((node.getChildAt(j).toString()).equals(target.toString()))
                    {   found = true;
			break;
		    }
		}
		if (found == false)				//no tree child found
		{						//JOptionPane.showMessageDialog(this,"Sorry, did not find node "+target,"LTOOLS",JOptionPane.ERROR_MESSAGE);
		    return null;
		}
		node = (DefaultMutableTreeNode) node.getChildAt(j);	//Tree child which is equal to the path element

		myTreePath[i] = node;				//Add it to the path
	    }

	    return new TreePath(myTreePath);			//Build the new path and return it
	}

	//##### Get the path to the parent for a child given by its path #################################################
	TreePath getParentPath(TreePath childPath)
        {   Object[] pathNodes = new Object[childPath.getPathCount() - 1];
	    for (int i = 0; i < childPath.getPathCount() - 1; i++)
		pathNodes[i] = childPath.getPathComponent(i);
	    return new TreePath(pathNodes);
	}

	//##### Support functions for TreeExpansionListener####################
	//~~~~~ Called when a DOS tree is expanded, updates expanded subdirectories
	boolean expandDosTree(TreePath path)
        {   debugOutput("expandDosTree   " + (isLeftTree ? "lTree" : "rTree") + (isLinux ? " Linux " : " Dos   ") + path);

	    String currentDirectory = File.separator;		//Build the DOS file path as a string (without drive, ends with '\')
	    for (int i = 1; i < path.getPathCount(); i++)
		 currentDirectory = currentDirectory + path.getPathComponent(i) + File.separator;

	    DefaultMutableTreeNode parent = (DefaultMutableTreeNode) path.getLastPathComponent();	//Directory node to expanded

	    while (parent.getChildCount() > 0)			//Remove this node's children before it is updated
            {   treeModel.removeNodeFromParent((DefaultMutableTreeNode) parent.getChildAt(0));
	    }

	    File DOSdir = new File(currentDrive + currentDirectory);	//Get a list of the directory's contents

	    String[]files = DOSdir.list();

	    for (int i = 0; i < files.length; i++)		//Convert the list into nodes and add them as children to the directory node
            {   File file = new File(currentDrive + currentDirectory + files[i]);

		if (file.isDirectory())				//for directories we add a dummy child node
                {   node = new DefaultMutableTreeNode(files[i]);
//		    treeModel.insertNodeInto(node, parent, parent.getChildCount());
		    treeModel.insertNodeInto(node, parent, findIndexForAlphabeticInsert(parent,node,true));
		    node.add(new DefaultMutableTreeNode(".."));
		} else if (isLeftTree == false)			//normal files only show up in right trees
                {   node = new DefaultMutableTreeNode(files[i]);
//		    treeModel.insertNodeInto(node, parent, parent.getChildCount());
		    treeModel.insertNodeInto(node, parent, findIndexForAlphabeticInsert(parent,node,false));
		}
	    }
	    if (parent.getChildCount() == 0)			//if the directory is empty, insert a dummy child node again
            {   treeModel.insertNodeInto(new DefaultMutableTreeNode(".."), parent, parent.getChildCount());
		return true;
	    }
	    return false;
	}

	//~~~~~ Called when a Linux tree is expanded, updates expanded subdirectories
	boolean expandLinuxTree(TreePath path)
        {   debugOutput("expandLinuxTree   " + (isLeftTree ? "lTree" : "rTree") + (isLinux ? " Linux " : " Dos   ") + path);
	    String command, response, line;

	    String currentDirectory = "/";			//Build the Linux file path as a string (without drive, ends with '/')
	    for (int i = 1; i < path.getPathCount(); i++)
		 currentDirectory = currentDirectory + path.getPathComponent(i) + "/";

	    if (cacheValid && currentDirectory.equals(cachedDirectory))
            {   response = cache;
	    } else
            {   command = "ldir -x -s" + currentDrive + " " + currentDirectory;
		response = execOperatingSystemCommand(command, false);
		cache = response;
		cachedDirectory = currentDirectory;
		cacheValid = true;
	    }

	    StringTokenizer token = new StringTokenizer(response, "\n\r");

	    command = token.nextToken();
	    if ((command.length() < stLINfn) || (getLdirField(command,9).equals(".") == false))
            {   return true;
	    }
	    command = token.nextToken();
	    if ((command.length() < stLINfn) || (getLdirField(command,9).equals("..") == false))
            {   return true;
	    }
	    DefaultMutableTreeNode parent = (DefaultMutableTreeNode) path.getLastPathComponent();	//Directory node to expanded

	    while (parent.getChildCount() > 0)			//Remove this node's children before it is updated
            {   treeModel.removeNodeFromParent((DefaultMutableTreeNode) parent.getChildAt(0));
	    }

	    while (token.hasMoreTokens())
            {   line = token.nextToken();
		if (line.substring(0, 1).equals("d"))
                {   node = new DefaultMutableTreeNode(getLdirField(line,9));
//		    treeModel.insertNodeInto(node, parent, parent.getChildCount());
		    treeModel.insertNodeInto(node, parent, findIndexForAlphabeticInsert(parent,node,true));
		    node.add(new DefaultMutableTreeNode(".."));
		} else if (isLeftTree == false)			//normal files only show up in right trees
                {   node = new DefaultMutableTreeNode(getLdirField(line,9));
//		    treeModel.insertNodeInto(node, parent, parent.getChildCount());
		    treeModel.insertNodeInto(node, parent, findIndexForAlphabeticInsert(parent,node,false));
		}
	    }

	    if (parent.getChildCount() == 0)			//if the directory is empty, insert a dummy child node again
            {   treeModel.insertNodeInto(new DefaultMutableTreeNode(".."), parent, parent.getChildCount());
		return true;
	    }
	    return false;
	}


	//##### Tree Expansion Listener #######################################
	//~~~~~ Called, when a tree is collapsed
	public void treeWillCollapse(TreeExpansionEvent event)
        {   debugOutput("Tree collapse   " + (isLeftTree ? "lTree" : "rTree") + (isLinux ? " Linux " : " Dos   ") + event.getPath());
	}

	//~~~~~ Called, when a tree is expanded
	public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException
        {   TreePath selectedPath = (TreePath) event.getPath();	//Path to the node, which shall be expanded, in the tree, which the user clicked
	    boolean isEmptyTree;
	     debugOutput("Tree expanded   " + (isLeftTree ? "lTree" : "rTree") + (isLinux ? " Linux " : " Dos   ") + selectedPath + " " + avoidExpansionRetrigger);

	    if (avoidExpansionRetrigger && getName(selectedPath, isLinux).equals(fileExpansionRetrigger))
            {   avoidExpansionRetrigger = false;		//Avoids retriggering the event
		if (isLeftTree)					//Do not expand empty directories in left trees
                {   if (((DefaultMutableTreeNode) selectedPath.getLastPathComponent()).getChildCount() == 1)
                    {   DefaultMutableTreeNode node = (DefaultMutableTreeNode) selectedPath.getLastPathComponent();
			if (node.getChildAt(0).toString().equals(".."))
                        {   throw new ExpandVetoException(event);
			}
		    }
		}
		return;
	    } else
            {   avoidExpansionRetrigger = true;
		fileExpansionRetrigger = getName(selectedPath, isLinux);
	    }

	    if (isLinux)
            {   isEmptyTree = expandLinuxTree(selectedPath);	//Expand a Linux tree
	    } else
            {   isEmptyTree = expandDosTree(selectedPath);	//Expand a Dos tree
	    }
	    if (isEmptyTree)
            {   if (isLeftTree)
                {   updateOtherTree(selectedPath);		//Update the other tree (rTree if lTree is clicked and vice versa)
		    throw new ExpandVetoException(event);
		}
	    } else
            {   updateOtherTree(selectedPath);			//Update the other tree (rTree if lTree is clicked and vice versa)
	    }
	}

	//~~~~~ Called, when a node in a tree is selected
	public void valueChanged(TreeSelectionEvent event)
        {   debugOutput("Tree selection  " + (isLeftTree ? "lTree" : "rTree") + (isLinux ? " Linux " : " Dos   ") + event.getPath() + " " + avoidSelectionRetrigger);

	    if (getSelectionPath() == null)			//No action, when nodes are deselected
		return;
	    if (avoidSelectionRetrigger && getName(getSelectionPath(), isLinux).equals(fileSelectionRetrigger))
            {   avoidSelectionRetrigger = false;		//Avoids retriggering the event when selectOtherTree is called
		return;
	    } else
            {   avoidSelectionRetrigger = true;
		fileSelectionRetrigger = getName(getSelectionPath(), isLinux);
	    }

	    if (isLinux)					//If a file in the Linux Pane is selected
            {   ltoolgui.myProg.DosPane.lTree.clearSelection();	//~~~ clear selections in the Dos Pane
		ltoolgui.myProg.DosPane.rTree.clearSelection();
	    } else
            {   ltoolgui.myProg.LinuxPane.lTree.clearSelection();	//~~~ clear selections in the Linux Pane

		ltoolgui.myProg.LinuxPane.rTree.clearSelection();
	    }
	    selectOtherTree(event.getPath());			//~~~select the same file in the other tree
	}

	//##### Mouse Listener ################################################
	//      Only used in the right tree
	public void mouseClicked(MouseEvent e)
        {   TreePath treePath = getPathForLocation(e.getX(), e.getY());
	    if (treePath == null)
            {   return;						//strange, but seems to happen
	    }
	    if (e.getClickCount() >= 2)				//identifies a double left or right click
            {   if (isLeftTree)
                {   return;
		}
		if (((DefaultMutableTreeNode) treePath.getLastPathComponent()).isLeaf())	//leafs are to be started as applications

                {   if (isLinux)
			startLinuxApplication(treePath);
		    else
			startDosApplication(treePath);
		} else
		    //directories are to be expanded or collapsed
                {   if (isCollapsed(treePath))
                    {   expandPath(treePath);
		    } else
                    {   collapsePath(treePath);
		    }
		}
	    } else if (javax.swing.SwingUtilities.isRightMouseButton(e))	///*e.paramString().toLowerCase().indexOf("mods=4") > 0*/)	//identifies a single right click
            {   if (isLinux)
		    modifyLinuxFile(treePath);
		else
		    modifyDosFile(treePath);
	    }
	}

	public void mouseEntered(MouseEvent e)
        {
        }

	public void mouseExited(MouseEvent e)
        {
        }

	public void mousePressed(MouseEvent e)
        {
        }

	public void mouseReleased(MouseEvent e)
        {
        }

	//##### KeyListener ###################################################
	public void keyPressed(KeyEvent e)
	{    TreePath[] delPaths = getSelectionPaths();
    	     int nSel = getSelectionCount();

	     if (e.getKeyCode()==KeyEvent.VK_DELETE)
	     {	if (isLinux)
		{   for (int i=0; i < nSel; i++)
		    {	deleteLinuxFile(delPaths[i]);
		    }
		} else
		{   for (int i=0; i < nSel; i++)
		    {	deleteDosFile(delPaths[i]);
		    }
		}
		ltoolgui.myProg.ConfirmDelCheckBox.setSelected(true);

	     } else if (e.getKeyCode()==KeyEvent.VK_INSERT)
	     {  if (nSel>1)
	        {   JOptionPane.showMessageDialog(this, "Sorry, only one item may be selected here", "LTOOLS", JOptionPane.ERROR_MESSAGE);
	            return;
	        }
	     	if (getModel().isLeaf(getSelectionPath().getLastPathComponent())==false)
	     	    if (isLinux)
	     	    	newLinuxDirectory(getSelectionPath());
	     	    else
	     	    	newDosDirectory(getSelectionPath());
	     } else if (e.getKeyCode()==KeyEvent.VK_ENTER)
	     {	if (nSel>1)
	        {   JOptionPane.showMessageDialog(this, "Sorry, only one item may be selected here", "LTOOLS", JOptionPane.ERROR_MESSAGE);
	            return;
	        }
	     	if (isLinux)
	     	    startLinuxApplication(getSelectionPath());
	    	else
	     	    startDosApplication(getSelectionPath());
	     }
	}
	public void keyReleased(KeyEvent e)
	{
	}
	public void keyTyped(KeyEvent e)
	{
	}

	//##### DragSourceListener ############################################
	public void dragDropEnd(DragSourceDropEvent e)
        {   debugOutput("dragDropEnd---DragSource  " + e.getDropSuccess());
	    if (!e.getDropSuccess())
            {   (new Thread(this)).start();			//Start an error messaging thread
	    }
	}
	public void dragEnter(DragSourceDragEvent e)
	{							//debugOutput("dragEnter---DragSource");
	}
	public void dragExit(DragSourceEvent e)
	{							//debugOutput("dragExit---DragSource");
	}
	public void dragOver(DragSourceDragEvent e)
	{							//debugOutput("dragOver---DragSource");
	}
	public void dropActionChanged(DragSourceDragEvent e)
	{							//debugOutput("dropActionChanged---DragSource");
	}

	//##### DragGestureListener ###########################################
	public void dragGestureRecognized(DragGestureEvent e)	//This initiates a drag and drop action
        {   if (getSelectionCount()>1)
	    {   JOptionPane.showMessageDialog(this, "Sorry, only one item may be selected here", "LTOOLS", JOptionPane.ERROR_MESSAGE);
	        return;
	    }
            ltoolgui.copyFilePath = getSelectionPaths();
            ltoolgui.copyFileCount= getSelectionCount();
	    if (ltoolgui.copyFilePath[0] == null)
		return;
	    if (avoidDragRetrigger)
		return;
	    if (((DefaultMutableTreeNode) getSelectionPath().getLastPathComponent()).isLeaf() == false)
            {   debugOutput("Drag failed - cannot drag directories");
		avoidDragRetrigger = true;
		JOptionPane.showMessageDialog(this, "Sorry, cannot drag directories!", "LTOOLS", JOptionPane.ERROR_MESSAGE);
		avoidDragRetrigger = false;
		return;
	    }
	    ltoolgui.copyFileDrive = currentDrive;
	    ltoolgui.copyFileIsLinux = isLinux;
	    ltoolgui.copyFileIsValid = true;
	    debugOutput("dragGestureRecognized " + ltoolgui.copyFilePath[0] + "   isLinux=" + isLinux + "  isLeftTree=" + isLeftTree);
	    try
            {   dragSource.startDrag(e, DragSource.DefaultMoveDrop, new StringSelection("dummy"), this);
	    }
	    catch(InvalidDnDOperationException evt)
            {   debugOutput("dragGestureRecognized.startDrag failed");
	    }
	}

	//##### DropTargetListener ############################################
	public void dragEnter(DropTargetDragEvent e)
        {   debugOutput("dragEnter---DropTarget");
	    if (isLeftTree)
		e.acceptDrag(e.getDropAction());
	    else
		e.rejectDrag();
	}
	public void dragExit(DropTargetEvent e)
	{							//debugOutput("dragExit---DropTarget");
	}
	public void dragOver(DropTargetDragEvent e)
	{							//debugOutput("dragOver---DropTarget");
	}
	public void drop(DropTargetDropEvent e) throws InvalidDnDOperationException
        {   debugOutput("drop---DropTarget");
	    boolean success;

	    if (isLeftTree == false)
            {   debugOutput("Drag and drop failed");
		e.rejectDrop();
		return;
	    }
	    TreePath targetTreePath = getPathForLocation(e.getLocation().x, e.getLocation().y);
	    if (targetTreePath == null)
            {   debugOutput("Drag and drop failed");
		e.rejectDrop();
		return;
	    }
	    e.acceptDrop(e.getDropAction());
	    debugOutput("drop target=" + targetTreePath + "   isLinux=" + isLinux + "  isLeftTree=" + isLeftTree);
	    if (isLinux)
		success = rTree.pasteToLinux(getTreePathFromTree(targetTreePath, lTree), false);
	    else
		success = rTree.pasteToDos(getTreePathFromTree(targetTreePath, lTree), false);
	    debugOutput("Copy did " + (success ? "" : "NOT") + " succeed");
	    e.dropComplete(success);
	}
	public void dropActionChanged(DropTargetDragEvent e)
	{							//debugOutput("dropActionChanged---DropTarget");
	}

	//This thread is necessary, because during a drag and drop sequence JOptionPane messages would freeze the system, so we
	//delay the message till the end of the drag and drop action
	public void run()
        {   JOptionPane.showMessageDialog(this, "Sorry, copying did not succeed due to one of the following reasons:\n"
					  + "* You must drop your file in a left tree, not in a right one.\n"
			     + "* When dropping on a LINUX directory, it must not contain a file with the same name.",
					  "LTOOLS", JOptionPane.ERROR_MESSAGE);
	}


    }

}

