Package org.cfeclipse.cfml.views.explorer.vfs.view

Source Code of org.cfeclipse.cfml.views.explorer.vfs.view.VFSView$FileAndConnectionId

/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*******************************************************************************/
package org.cfeclipse.cfml.views.explorer.vfs.view;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.*;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.dnd.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.program.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.IShowInTarget;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.part.ViewPart;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.text.*;
import java.util.*;

import org.apache.commons.vfs.FileName;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileSystemManager;
import org.apache.commons.vfs.FileSystemOptions;
import org.apache.commons.vfs.VFS;
import org.apache.commons.vfs.auth.StaticUserAuthenticator;
import org.apache.commons.vfs.impl.DefaultFileSystemConfigBuilder;
import org.apache.commons.vfs.provider.ftp.FtpFileSystemConfigBuilder;
import org.apache.commons.vfs.provider.sftp.SftpFileSystemConfigBuilder;
import org.apache.commons.vfs.util.Os;

import org.cfeclipse.cfml.editors.CFJavaFileEditorInput;
import org.cfeclipse.cfml.editors.CFMLEditor;
import org.cfeclipse.cfml.images.ColumnViewerToolTipSupport;
import org.cfeclipse.cfml.images.ToolTip;
import org.cfeclipse.cfml.net.FTPConnectionProperties;
import org.cfeclipse.cfml.net.RemoteFile;
import org.cfeclipse.cfml.net.RemoteFileEditorInput;
import org.cfeclipse.cfml.preferences.FtpConnectionDialog;
import org.cfeclipse.cfml.util.CFPluginImages;
import org.cfeclipse.cfml.views.explorer.FileExplorerView;
import org.cfeclipse.cfml.views.explorer.vfs.FileOperation;
import org.cfeclipse.cfml.views.explorer.vfs.view.menus.EditMenu;
import org.cfeclipse.cfml.views.explorer.vfs.view.menus.FileMenu;
import org.cfeclipse.cfml.views.explorer.vfs.view.menus.EditMenu.CopyAction;
import org.cfeclipse.cfml.views.explorer.vfs.view.menus.EditMenu.PasteAction;
import org.cfeclipse.cfml.views.explorer.vfs.view.menus.FileMenu.ConnectionAction;
import org.cfeclipse.cfml.views.explorer.vfs.view.menus.FileMenu.DeleteAction;
import org.cfeclipse.cfml.views.explorer.vfs.view.menus.FileMenu.NewFileAction;
import org.cfeclipse.cfml.views.explorer.vfs.view.menus.FileMenu.NewFolderAction;


/**
* File Viewer example
*/
public class VFSView extends ViewPart implements IShowInTarget
{
  public static final String ID_FILEEXPLORER = "org.cfeclipse.cfml.views.explorer.vfs.view.VFSView";
  private static final String RESOURCE_BUNDLE= VFSView.class.getName();
  private static ResourceBundle resourceBundle= ResourceBundle.getBundle(RESOURCE_BUNDLE);

  private final static String DRIVE_A = "a:" + FileName.SEPARATOR;
//  private final static String DRIVE_B = "b:" + FileName.SEPARATOR;
  private static boolean initial = true;
  
  /* UI elements */  
  private Display display;
  private Shell shell;
//  private ToolBar toolBar;

  private Label numObjectsLabel;
  private Label diskSpaceLabel;
 
  /* currently selected directory */
  private FileObject currentDirectory = null;
 
  /* Drag and drop optimizations */
  private boolean isDragging = false; // if this app is dragging
  private boolean isDropping = false; // if this app is dropping

//  private FileObject[]  processedDropFiles = null; // so Drag only deletes what it needs to
  private FileObject[]  deferredRefreshFiles = null;      // to defer notifyRefreshFiles while we do DND

  private boolean deferredRefreshRequested = false; // to defer notifyRefreshFiles while we do DND

  /* Combo view */
  private static final String COMBODATA_ROOTS = "Combo.roots";
    // File[]: Array of files whose paths are currently displayed in the combo
  private static final String COMBODATA_LASTTEXT = "Combo.lastText";
    // String: Previous selection text string
  private static final String COMBODATA_LASTCONNECTIONID = "Combo.lastConnectionId";
  // String: Previous connectionid

  private Combo combo;

  /* Tree view */
  static public ImageRegistry iconCache = CFPluginImages.getImageRegistry();
 
  public static final String TREEITEMDATA_FILE = "TreeItem.file";
    // File: File associated with tree item
  public static final String TREEITEMDATA_IMAGEEXPANDED = "TreeItem.imageExpanded";
    // Image: shown when item is expanded
  public static final String TREEITEMDATA_IMAGECOLLAPSED = "TreeItem.imageCollapsed";
    // Image: shown when item is collapsed
  private static final String TREEITEMDATA_STUB = "TreeItem.stub";
    // Object: if not present or null then the item has not been populated

  public static final String TREEITEMDATA_URI = "TreeItem.uri";
    // VFS URI associated with table row
  public static final String TREEITEMDATA_CONNECTIONID = "TableItem.connectionId";
    // connection id

  private static Tree tree;
  private Label treeScopeLabel;

  /* Table view */
  protected static final DateFormat dateFormat = DateFormat.getDateTimeInstance(
    DateFormat.MEDIUM, DateFormat.MEDIUM);

  public static final String TABLEITEMDATA_FILE = "TableItem.file";
    // File: File associated with table row
  public static final String TABLEDATA_DIR = "Table.dir";
    // File: Currently visible directory

  private static final int[] tableWidths = new int[] {150, 60, 75, 150};
    public static final String TABLEITEMDATA_CONNECTIONID = "Table.connectionId";
    protected static final String TABLEDATA_CONNECTIONID = "TableItem.connectionId";
  private final String[] tableTitles = new String [] {
    VFSView.getResourceString("table.Name.title"),
    VFSView.getResourceString("table.Size.title"),
    VFSView.getResourceString("table.Type.title"),
    VFSView.getResourceString("table.Modified.title")
  };
  private Table table;
 
  /* File Items Tabs */
  private TabItem tabFileItems;
//  private TabItem tabTrasnferItems;
 
  /* Table update worker */
  // Control data
  private final Object workerLock = new Object();
    // Lock for all worker control data and state
  private volatile Thread  workerThread = null;
    // The worker's thread
  private volatile boolean workerStopped = false;
    // True if the worker must exit on completion of the current cycle
  private volatile boolean workerCancelled = false;
    // True if the worker must cancel its operations prematurely perhaps due to a state update

  // Worker state information -- this is what gets synchronized by an update
  private volatile FileObject workerStateDir = null;

  // State information to use for the next cycle
  private volatile FileObject workerNextDir = null;

  // Comnnons VFS Manager
  private FileSystemManager fsManager = null;
  public TreeMap<String, FTPConnectionProperties> fConnections;
  private String currentConnectionId;
  private String workerNextConnectionId;
  protected String workerStateConnectionId;
  public Object fSourceConnectionId;
  private ConnectionAction connectionAction;
  private DeleteAction deleteAction;
  private CopyAction copyAction;
  private PasteAction pasteAction;
  private NewFolderAction newFolderAction;
  private NewFileAction newFileAction;
  //private static boolean VFS_DEBUG = System.getProperty("org.cfeclipse.cfml.views.explorer.vfs.debug") != null;
  private static boolean VFS_DEBUG = true;
     
  public static void error (String s) {
    if ( VFS_DEBUG )
      System.err.println(s);
  }

  public static void debug (Exception e) {
    if ( VFS_DEBUG )
      e.printStackTrace();
  }
 
  public Tree getTree () {
    return tree;
  }
 
  public Table getTable () {
    return table;
  }
 
  /**
   * Returns the Commons VFS File System Manager
   */
  public FileSystemManager getFileSystemManager ()
  {
    return fsManager;
  }
 
  /**
   * Closes the main program.
   */
  public void dispose() {
    workerStop();
    //return super.close();
  }
 
  /**
   * Returns a string from the resource bundle.
   * We don't want to crash because of a missing String.
   * Returns the key if not found.
   */
  public static String getResourceString(String key) {
    try {
      return resourceBundle.getString(key);
    } catch (MissingResourceException e) {
      return key;
    } catch (NullPointerException e) {
      return "!" + key + "!";
    }     
  }

  /**
   * Returns a string from the resource bundle and binds it
   * with the given arguments. If the key is not found,
   * return the key.
   */
  public static String getResourceString(String key, Object[] args) {
    try {
      return MessageFormat.format(getResourceString(key), args);
    } catch (MissingResourceException e) {
      return key;
    } catch (NullPointerException e) {
      return "!" + key + "!";
    }
  }

  /**
   * Returns a string from the resource bundle and binds it
   * with the given arguments. If the key is not found,
   * return the key.
   */
  public static String getResourceString(String key, Object arg) {
    try {
      return MessageFormat.format(getResourceString(key), new Object []{arg});
    } catch (MissingResourceException e) {
      return key;
    } catch (NullPointerException e) {
      return "!" + key + "!";
    }
  }
   
  /**
   * Returns a new menu manager for the window
   */
  protected MenuManager createMenuManager()
  {
    MenuManager menuManager = (MenuManager) getViewSite().getActionBars().getMenuManager();
    // File
    MenuManager fileMenu =new MenuManager(getResourceString("menu.File.text"));
    connectionAction = new FileMenu.ConnectionAction(this
        , getResourceString("menu.File.New.text")
        , getResourceString("menu.File.New.text") );
    fileMenu.add(connectionAction);

    deleteAction = new FileMenu.DeleteAction(this
        , getResourceString("menu.Connection.Delete.text")
        , getResourceString("menu.Connection.Delete.text") );
    fileMenu.add(deleteAction);

    newFolderAction = new FileMenu.NewFolderAction(this
        , getResourceString("menu.File.NewFolder.text")
        , getResourceString("menu.File.NewFolder.text"));
    fileMenu.add(newFolderAction);
   
    newFileAction = new FileMenu.NewFileAction(this
        , getResourceString("menu.File.NewFile.text")
        , getResourceString("menu.File.NewFile.text"));
    fileMenu.add(newFileAction);

    // Edit
    MenuManager editMenu =new MenuManager(getResourceString("menu.Edit.text"));
    copyAction = new EditMenu.CopyAction(this
        , getResourceString("menu.Edit.copy.text")
        , getResourceString("menu.Edit.copy.text") );
    editMenu.add(copyAction);
   
    pasteAction = new EditMenu.PasteAction(this
        , getResourceString("menu.Edit.paste.text")
        , getResourceString("menu.Edit.paste.text"));
    editMenu.add(pasteAction);
   

   
    menuManager.add(fileMenu);
    menuManager.add(editMenu);

    // hook the tree context menu
    MenuManager menuMgr = new MenuManager("#PopupMenu");
    menuMgr.setRemoveAllWhenShown(true);
    menuMgr.addMenuListener(new IMenuListener() {
      public void menuAboutToShow(IMenuManager manager) {
        VFSView.this.fillContextMenu(manager,new Action[]{connectionAction,deleteAction,copyAction,pasteAction,newFolderAction,newFileAction});
      }
    });
    Menu menu = menuMgr.createContextMenu(tree);
    tree.setMenu(menu);

    // hook the table context menu
    MenuManager tabMenuMgr = new MenuManager("#PopupMenu");
    tabMenuMgr.setRemoveAllWhenShown(true);
    tabMenuMgr.addMenuListener(new IMenuListener() {
      public void menuAboutToShow(IMenuManager manager) {
        VFSView.this.fillContextMenu(manager,new Action[]{copyAction,pasteAction,newFolderAction,newFileAction});
      }
    });
    Menu tabMenu = tabMenuMgr.createContextMenu(table);
    table.setMenu(tabMenu);
   
    return menuManager;
  }

    private void fillContextMenu(IMenuManager manager,Action[] actions) {
    //Somehow we have to know what this item is
      for(Action action:actions) {       
        manager.add(action);
      }   
  }
 
   /**
     * Helper method to calculate the size of the cool item
     *
     * @param item the cool item
     */
  private void calcSize(CoolItem item) {
      Control control = item.getControl();
      Point pt = control.computeSize(SWT.DEFAULT, SWT.DEFAULT);
      pt = item.computeSize(pt.x, pt.y);
      item.setSize(pt);
  }
 
  /**
   * Construct the UI
   *
   * @param container the ShellContainer managing the Shell we are rendering inside
   */
    public void createPartControl(Composite parent)
  {
      display = parent.getDisplay();
    shell = getSite().getShell();
    fConnections = VFSUtil.loadFileSystems();
    try {
      fsManager = VFS.getManager();
    } catch (FileSystemException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    Composite container = new Composite(parent, SWT.NONE);
    container.setLayout(new GridLayout(1, true));
   
    GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
//    gridData.horizontalSpan = 3;
    gridData.grabExcessHorizontalSpace = true;
//    createComboView(parent, gridData); // shell, gridData);
   
    CoolBar coolbar = new CoolBar(container, SWT.NONE);
    coolbar.setLayoutData(gridData);

      // Create a toolbar coolitem
    final CoolItem item1 = new CoolItem(coolbar, SWT.NONE);
      item1.setControl(createToolBar(coolbar));
      calcSize(item1);
   
    // Create a combo coolitem
    gridData = new GridData(GridData.FILL_HORIZONTAL);
    gridData.widthHint = 185;
   
      final CoolItem item2 = new CoolItem(coolbar, SWT.NONE);
      item2.setControl(createComboView(coolbar, gridData));
      calcSize(item2);
     
    SashForm sashForm = new SashForm(container, SWT.NONE);
    sashForm.setOrientation(SWT.VERTICAL);
   
    gridData = new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL);
    sashForm.setLayoutData(gridData);
   
    createTreeView(sashForm);
    createTableView(sashForm);
   
    sashForm.setWeights(new int[] { 2, 5 });

    Composite container1 = new Composite(container, SWT.NONE);
    container1.setLayout(new GridLayout(2, true));
    container1.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));

    numObjectsLabel = new Label(container1, SWT.BORDER);
    numObjectsLabel.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true));// gridData);

    diskSpaceLabel = new Label(container1, SWT.BORDER);
    diskSpaceLabel.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true)); //gridData);
    createMenuManager();
    try {
      notifyRefreshFiles(null);
    } catch (Exception e) {
      debug (e);
    }
    //return container;
  }
 
 
  /**
   * Creates the toolbar
   *
   * @param shell the shell on which to attach the toolbar
   * @param layoutData the layout data
   */
  private Control createToolBar(final Composite shell) { //, Object layoutData) {
    final ToolBar toolBar = new ToolBar(shell, SWT.FLAT);
//    toolBar.setLayoutData(layoutData);
   
    // New VFS
    ToolItem item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.get(CFPluginImages.ICON_DRIVE_FTP));//.iconFile]);
    //item.setText(getResourceString("tool.New.vfs.text"));
    item.setToolTipText(getResourceString("tool.New.vfs.tip"));
   
    item.addSelectionListener(new SelectionAdapter () {
      public void widgetSelected(SelectionEvent e)
      {
        connectionAction.run();
      }
    });

    // Home
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.get(CFPluginImages.ICON_HOME));
    //item.setText(getResourceString("tool.home.text"));
    item.setToolTipText(getResourceString("tool.home.tip"));
   
    item.addSelectionListener(new SelectionAdapter () {
      public void widgetSelected(SelectionEvent e)
      {
        try {
          // Jump the file system home directory, some FS don't have homes
         
          String homeDir   = getCurrentDirectory().getName().getScheme().equals("file")
              ? VFSUtil.USER_HOME
              : (String)getCurrentDirectory().getFileSystem().getAttribute("HOME_DIRECTORY");
         
          // Build a URI from the home dir
          String uri   = Os.isFamily(Os.OS_FAMILY_UNIX)
                ? getCurrentDirectory().getName().getRootURI() + homeDir
                : getCurrentDirectory().getName().getScheme() + ":///" + homeDir.replace('\\','/');
               
          FileObject fo  = resolveURI(uri,getCurrentConnectionId());
         
          if ( fo.exists() )
            notifySelectedDirectory(fo, getCurrentConnectionId());
          else
            VFSUtil.MessageBoxInfo(getResourceString("error.jump.home", new Object[] {homeDir} ));
         
        } catch (FileSystemException ex) {
          FileObject fo;
          try {
            fo = resolveURI(fConnections.get(getCurrentConnectionId()).getURI().toString(),getCurrentConnectionId());
            if ( fo.exists() )
              notifySelectedDirectory(fo, getCurrentConnectionId());
            else
              VFSUtil.MessageBoxInfo(getResourceString("error.jump.home", new Object[] {fo.getURL().toString()} ));
          } catch (FileSystemException e1) {
            VFSUtil.MessageBoxInfo(ex.getMessage());
          }
        }
      }
    });
   
    // New folder
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.get(CFPluginImages.ICON_FOLDER_NEW));
    //item.setText(getResourceString("tool.New.folder.text"));
    item.setToolTipText(getResourceString("tool.New.folder.tip"));
   
    item.addSelectionListener(new SelectionAdapter () {
      public void widgetSelected(SelectionEvent e)
      {
        VFSUtil.newFolder(getCurrentDirectory());
      }
    });
   
    item = new ToolItem(toolBar, SWT.SEPARATOR);
   
    // Parent
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.get(CFPluginImages.ICON_FOLDER_PARENT));
    //item.setText(getResourceString("tool.Parent.tiptext"));
    item.setToolTipText(getResourceString("tool.Parent.tiptext"));
    item.addSelectionListener(new SelectionAdapter () {
      public void widgetSelected(SelectionEvent e) {
        doParent();
      }
    });
   
    // Refresh
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.get(CFPluginImages.ICON_REFRESH));
    //item.setText(getResourceString("tool.Refresh.tiptext"));
    item.setToolTipText(getResourceString("tool.Refresh.tiptext"));
    item.addSelectionListener(new SelectionAdapter () {
      public void widgetSelected(SelectionEvent e) {
        doRefresh();
      }
    });
 
    return toolBar;
  }

  /**
   * Creates the combo box view.
   *
   * @param parent the parent control
   */
  private Control createComboView(Composite parent, Object layoutData)
  {
    combo = new Combo(parent, SWT.NONE);
    combo.setLayoutData(layoutData);
    combo.addSelectionListener(new SelectionAdapter() {
     
      public void widgetSelected(SelectionEvent e)
      {
        final FileObject[] roots = (FileObject[]) combo.getData(COMBODATA_ROOTS);
        if (roots == null) return;
        int selection = combo.getSelectionIndex();
       
        if (selection >= 0 && selection < roots.length)
        {
          try {
            System.out.println("combo.widgetSelected " + roots[selection]);
            notifySelectedDirectory(roots[selection],roots[selection].getName().toString());
          } catch (Exception ex) {
            System.err.println("createComboView.widgetSelected: " + ex);
          }
        }
      }
     
      public void widgetDefaultSelected(SelectionEvent e) {
        final String lastText = (String) combo.getData(COMBODATA_LASTTEXT);
        final String lastConnectionId = (String) combo.getData(COMBODATA_LASTCONNECTIONID);
        String text = combo.getText();
        if (text == null) return;
        if (lastText != null && lastText.equals(text)) return;
        combo.setData(COMBODATA_LASTTEXT, text);
       
        try {
          FileObject file = resolveURI(text,lastConnectionId);
         
          if ( file.exists() )
            notifySelectedDirectory(file,lastConnectionId);
          else {
            VFSUtil.MessageBoxInfo(
                getResourceString("file.not.found", new Object[] {file}) );
            combo.setText(getCurrentDirectory().toString());
          }
         
        } catch (FileSystemException ex) {
          System.err.println("createComboView::widgetDefaultSelected Error: " + ex);
        }
      }
    });
    return combo;
  }

  /**
   * Creates the file tree view.
   *
   * @param parent the parent control
   */
  private void createTreeView(Composite parent) {
    Composite composite = new Composite(parent, SWT.NONE);
    GridLayout gridLayout = new GridLayout();
    gridLayout.numColumns = 1;
    gridLayout.marginHeight = gridLayout.marginWidth = 2;
    gridLayout.horizontalSpacing = gridLayout.verticalSpacing = 0;
    composite.setLayout(gridLayout);

    treeScopeLabel = new Label(composite, SWT.BORDER);
    treeScopeLabel.setText(VFSView.getResourceString("details.AllFolders.text"));
    treeScopeLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL));

    tree = new Tree(composite, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.SINGLE);
    tree.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL));

    tree.addSelectionListener(new SelectionListener() {
      public void widgetSelected(SelectionEvent event)
      {
       
        final TreeItem[] selection = tree.getSelection();
        if (selection != null && selection.length != 0) {
          TreeItem item = selection[0];
         
          try {
            //FileObject file = (FileObject) item.getData(TREEITEMDATA_FILE);
            String uri = (String) item.getData(TREEITEMDATA_URI);
            String connectionId = (String) item.getData(TREEITEMDATA_CONNECTIONID);
           
            shell.setText(getResourceString("Title.Resolving"
                , new Object[] { uri } ));

            notifySelectedDirectory(resolveURI(uri, connectionId),connectionId);
          }
          catch (FileSystemException e) {
            debug(e); // "createTreeView.widgetSelected " +
           
            VFSUtil.MessageBoxError( VFSUtil.getErrorMessageStack(e));
            VFSUtil.setUiStateIdle(shell, getCurrentDirectory().getName().toString());
          }
        }
      }
     
      public void widgetDefaultSelected(SelectionEvent event) {
        final TreeItem[] selection = tree.getSelection();
        if (selection != null && selection.length != 0) {
          TreeItem item = selection[0];
          String imageName;
          if(item.getExpanded()) {
            imageName = TREEITEMDATA_IMAGECOLLAPSED;           
          } else {
            imageName = TREEITEMDATA_IMAGEEXPANDED;
          }
          item.setExpanded(!item.getExpanded());
          final Image image = (Image) item.getData(imageName);
          if (image != null) item.setImage(image);
          treeExpandItem(item);
        }
      }
    });
    tree.addTreeListener(new TreeAdapter() {
      public void treeExpanded(TreeEvent event) {
        final TreeItem item = (TreeItem) event.item;
        final Image image = (Image) item.getData(TREEITEMDATA_IMAGEEXPANDED);
        if (image != null) item.setImage(image);
        treeExpandItem(item);
      }
      public void treeCollapsed(TreeEvent event) {
        final TreeItem item = (TreeItem) event.item;
        final Image image = (Image) item.getData(TREEITEMDATA_IMAGECOLLAPSED);
        if (image != null) item.setImage(image);
      }
    });
   
    // Listen for key presses
    tree.addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent event)
      {
        if ( event.keyCode == 127 )
        {
          TreeItem item = tree.getSelection()[0];
          String connectionId = (String)item.getData(TREEITEMDATA_CONNECTIONID);
         
          // delete at the root level only
          if ( ! (item.getParentItem() == null) ) return;
         
          if ( connectionId.startsWith("file://"))
            VFSUtil.MessageBoxInfo(getResourceString("remove.local.fs"));
          else {
              deleteAction.run(item);
          }
        }
      }
    });
   
    createTreeDragSource(tree);
    createTreeDropTarget(tree);
  }

  /**
   * Creates the Drag & Drop DragSource for items being dragged from the tree.
   *
   * @return the DragSource for the tree
   */
  private DragSource createTreeDragSource(final Tree tree){
    DragSource dragSource = new DragSource(tree, DND.DROP_COPY); // | DND.DROP_MOVE
   
    // Transfer data type is text (file URI)
    dragSource.setTransfer(new Transfer[] { FileTransfer.getInstance() });
    //dragSource.setTransfer(new Transfer[] { RTFTransfer.getInstance() });
   
    dragSource.addDragListener(new DragSourceListener() {
      TreeItem[] dndSelection = null;
      String[] sourceNames = null;
     
      public void dragStart(DragSourceEvent event){
        dndSelection = tree.getSelection();
        sourceNames = null;
        event.doit = dndSelection.length > 0;
        isDragging = true;
//        processedDropFiles = null;
      }
     
      public void dragFinished(DragSourceEvent event){
        dragSourceHandleDragFinished(event, sourceNames);
        dndSelection = null;
        sourceNames = null;
        isDragging = false;
//        processedDropFiles = null;
//TODO        try {
//          handleDeferredRefresh();
//        } catch (Exception e) {
//          debug ("createTreeDragSource.dragFinished " + e);
//        }
      }
     
      public void dragSetData(DragSourceEvent event){
        if (dndSelection == null || dndSelection.length == 0) return;
        //if (! FileTransfer.getInstance().isSupportedType(event.dataType)) return;
       
        if (! FileTransfer.getInstance().isSupportedType(event.dataType)) {
          //debug("createTreeDragSource.dragSetData File transfer data type is required");
          return;
        }
       
        sourceNames    = new String[dndSelection.length];
        FileObject file = null;
        String uri    = null;
        String connectionId = null;
        for (int i = 0; i < dndSelection.length; i++) {
          uri   = (String) dndSelection[i].getData(TREEITEMDATA_URI);
          connectionId = (String) dndSelection[i].getData(TREEITEMDATA_CONNECTIONID);
          try {
            file   = resolveURI(uri,connectionId);
          } catch (FileSystemException e) {
            debug(e); //"createTreeDragSource.dragSetData " +
            continue;
          }
         
          /*
           * In Order for FileTransfer to Work, the name must begin w/
           * / in UNIX systems
           */
          sourceNames[i] = Os.isFamily(Os.OS_FAMILY_UNIX)
            ? FileName.SEPARATOR + file.toString()
            : file.toString();
     
          System.out.println("createTreeDragSource.dragSetData file path=" + sourceNames[i]);         
        }
        //sourceNames[0] = connectionId;
        fSourceConnectionId = connectionId;
        event.data = sourceNames;
      }
    });
    return dragSource;
  }

  /**
   * Creates the Drag & Drop DropTarget for items being dropped onto the tree.
   *
   * @return the DropTarget for the tree
   */
  private DropTarget createTreeDropTarget(final Tree tree)
  {
    DropTarget dropTarget = new DropTarget(tree, DND.DROP_COPY); // | DND.DROP_MOVE
   
    dropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() });
   
    // Transfer data type is text
    //dropTarget.setTransfer(new Transfer[] { RTFTransfer.getInstance()} );
   
System.out.println("createTreeDropTarget");

    dropTarget.addDropListener(new DropTargetAdapter() {
      public void dragEnter(DropTargetEvent event) {
        isDropping = true;
      }
      public void dragLeave(DropTargetEvent event) {
        isDropping = false;
//TODO        try {
//          handleDeferredRefresh();
//        } catch (Exception e) {
//          debug("createTreeDropTarget.dragLeave " + e);
//        }
      }
      public void dragOver(DropTargetEvent event) {
        dropTargetValidate(event, getTargetFile(event));
        event.feedback |= DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL;
      }
     
      public void drop(DropTargetEvent event) {
        FileAndConnectionId targetFile = getTargetFile(event);
       
        try {
System.out.println("createTreeDropTarget.drop targetFile=" + targetFile.file);
          if (dropTargetValidate(event, targetFile))
            dropTargetHandleDrop(event, targetFile);
         
        }
        catch (FileSystemException e) {
          VFSUtil.MessageBoxError(e.getMessage());
        }
        catch (Exception e1) {
          debug(e1); // "createTreeDropTarget.drop " +
        }
      }
     
      private FileAndConnectionId getTargetFile(DropTargetEvent event) {
        // Determine the target FileObject for the drop
        TreeItem item = tree.getItem(tree.toControl(new Point(event.x, event.y)));
        FileObject targetFile = null;
        String connectionId = "";
       
        if (item != null) {
          // We are over a particular item in the tree, use the item's file
          //targetFile = (FileObject) item.getData(TREEITEMDATA_FILE);
          String uri = (String)item.getData(TREEITEMDATA_URI);
          connectionId = (String)item.getData(TREEITEMDATA_CONNECTIONID);
          try {
            targetFile   = resolveURI(uri,connectionId);
          } catch (Exception e) {
            debug(e); //"createTreeDropTarget.getTargetFile " +
          }
        }
        return new FileAndConnectionId(targetFile,connectionId);
      }
    });
    return dropTarget; 
  }

  /**
   * Handles expand events on a tree item.
   *
   * @param item the TreeItem to fill in
   */
  private void treeExpandItem(TreeItem item
  {
    shell.setCursor(CFPluginImages.cursors[CFPluginImages.cursorWait]);
    final Object stub = item.getData(TREEITEMDATA_STUB);
   
    if (stub == null) {
      try {
        treeRefreshItem(item, true);
      } catch (Exception e) {
        debug(e); // "treeExpandItem " +
      }
    }
    shell.setCursor(CFPluginImages.cursors[CFPluginImages.cursorDefault]);
  }
 
  /**
   * Add a file sytem node
   * @param filesystem
   */
  public void addFileSystem (String connectionId)
  {
    try {     
      TreeItem newItem = new TreeItem(tree, SWT.NULL);
      treeInitVolume(newItem, connectionId) ;// masterFile);
     
      //  placeholder child item to get "expand" button
      new TreeItem(newItem, SWT.NULL);
     
    } catch (Exception e) {
      VFSUtil.MessageBoxInfo(e.getMessage());
    }
  }
 
  /**
   * Traverse the entire tree and update only what has changed.
   *
   * @param roots the root directory listing
   */
  private void treeRefresh(FileObject[] masterFiles) {
    TreeItem[] items = tree.getItems();
    int masterIndex = 0;
    int itemIndex = 0;

    for (int i = 0; i < items.length; ++i) {
      final TreeItem item = items[i];
//      final String uri = (String)item.getData(TREEITEMDATA_URI);
      final FileObject itemFile = null;
     
     
      if ((itemFile == null) || (masterIndex == masterFiles.length)) {
        // remove bad item or placeholder
        item.dispose();
        continue;
      }
      final FileObject masterFile = masterFiles[masterIndex];
     
      int compare = VFSUtil.compareFiles(masterFile, itemFile);
     
      if (compare == 0) {
        // same file, update it
        try {
          treeRefreshItem(item, false);
        } catch (Exception e) {
          error("treeRefresh.treeRefreshItem " + e);
        }
       
        ++itemIndex;
        ++masterIndex;
      }
      else if (compare < 0) {
        // should appear before file, insert it
        TreeItem newItem = new TreeItem(tree, SWT.NULL, itemIndex);
        treeInitVolume(newItem, masterFile.getName().toString()) ;// masterFile);
        new TreeItem(newItem, SWT.NULL); // placeholder child item to get "expand" button
        ++itemIndex;
        ++masterIndex;
        --i;
      } else {
        // should appear after file, delete stale item
        item.dispose();
      }
    }

    for (;masterIndex < masterFiles.length; ++masterIndex) {
      final FileObject masterFile = masterFiles[masterIndex];
      TreeItem newItem = new TreeItem(tree, SWT.NULL);
     
//System.out.println("treeRefresh file=" + masterFile.getName().getURI());
      treeInitVolume(newItem, masterFile.getName().toString()) ;// masterFile);
      new TreeItem(newItem, SWT.NULL); // placeholder child item to get "expand" button
    }   
  }

  /**
   * Insert URIS from $HOME/.vfs into the Tree
   */
  private void treeInsertRemoteURIs ()
  {
    // Add remote URI nodes to the tree
   
    if ( fConnections != null ) {
        Iterator it = fConnections.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pairs = (Map.Entry)it.next();
            FTPConnectionProperties conProps = (FTPConnectionProperties) pairs.getValue();
            if(!conProps.getURI().equals(conProps.getConnectionid())) {             
              VFSUtil.treeAddFsNode(tree, conProps);
            }
        }
    }
  }

  public void treeRefreshRemoteURIs ()
  {
    fConnections = VFSUtil.loadFileSystems();   
    try {
      treeRefresh(getRoots(fsManager));
    } catch (FileSystemException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    if ( fConnections != null ) {
        Iterator it = fConnections.entrySet().iterator();
      TreeItem[] items = tree.getItems();
      int masterIndex = 0;
      int itemIndex = 0;

        while (it.hasNext()) {
          boolean foundConnection = false;
            Map.Entry pairs = (Map.Entry)it.next();
            FTPConnectionProperties conProps = (FTPConnectionProperties) pairs.getValue();
        for (int i = 0; i < items.length; ++i) {
          final TreeItem item = items[i];
          final String treeConnectionId = (String)item.getData(TREEITEMDATA_CONNECTIONID);
          final String filePath = (String)item.getData(TREEITEMDATA_FILE);
          if(treeConnectionId.equals(conProps.getConnectionid())){
            foundConnection = true;
            break;
          } else {
            if(fConnections.get(treeConnectionId) == null && filePath != treeConnectionId) {
              item.dispose();             
            }
          }
         
        }
        if(!foundConnection) {
          VFSUtil.treeAddFsNode(tree, conProps);         
        }
        }
    }
  }
 
 
  /**
   * Traverse an item in the tree and update only what has changed.
   *
   * @param dirItem the tree item of the directory
   * @param forcePopulate true iff we should populate non-expanded items as well
   */
  private void treeRefreshItem(TreeItem dirItem, boolean forcePopulate)
    throws FileSystemException
  {
    final String uri = (String) dirItem.getData(TREEITEMDATA_URI);

//System.out.println("treeRefreshItem uri=" + uri);

    if (! forcePopulate && ! dirItem.getExpanded()) {
      // Refresh non-expanded item
      if (dirItem.getData(TREEITEMDATA_STUB) != null) {
        treeItemRemoveAll(dirItem);
        new TreeItem(dirItem, SWT.NULL); // placeholder child item to get "expand" button
        dirItem.setData(TREEITEMDATA_STUB, null);
      }
      return;
    }
    // Refresh expanded item
    dirItem.setData(TREEITEMDATA_STUB, this); // clear stub flag

    /* Get directory listing */
    FileObject[] subFiles = (uri != null)
      ? getDirectoryList( resolveURI(uri,(String) dirItem.getData(TREEITEMDATA_CONNECTIONID)))
      : null;
     
    if (subFiles == null || subFiles.length == 0) {
      /* Error or no contents */
      treeItemRemoveAll(dirItem);
      dirItem.setExpanded(false);
      return;
    }

    /* Refresh sub-items */
    TreeItem[] items = dirItem.getItems();
    final FileObject[] masterFiles = subFiles;
   
    int masterIndex = 0;
    int itemIndex = 0;
    FileObject masterFile = null;
   
    String folderBaseName = null;
   
    for (int i = 0; i < items.length; ++i) {
      while ((masterFile == null) && (masterIndex < masterFiles.length)) {
        masterFile = masterFiles[masterIndex++];
       
        //if (! masterFile.isDirectory()) masterFile = null;
        if (! VFSUtil.isDirectory(masterFile)) masterFile = null;
      }

      final TreeItem item = items[i];
//      final String itemUri = (String) item.getData(TREEITEMDATA_URI);
//System.out.println("itemUri=" + itemUri);     
      final String itemFile = (String) item.getData(TREEITEMDATA_URI);
      //final FileObject itemFile = (FileObject) item.getData(TREEITEMDATA_FILE);
     
      if ((itemFile == null) || (masterFile == null)) {
        // remove bad item or placeholder
        item.dispose();
        continue;
      }
      int compare = VFSUtil.compareFiles(masterFile, resolveURI( itemFile, (String) item.getData(TREEITEMDATA_CONNECTIONID)) );
     
      if (compare == 0) {
        // same file, update it
        treeRefreshItem(item, false);
        masterFile = null;
        ++itemIndex;
      }
      else if (compare < 0) {
        // should appear before file, insert it
        TreeItem newItem = new TreeItem(dirItem, SWT.NULL, itemIndex);
       
        folderBaseName = masterFile.getName().getBaseName();
       
        //treeInitFolder(newItem, folderBaseName , uri + FileName.SEPARATOR + folderBaseName); //masterFile);
        treeInitFolder(newItem, folderBaseName , masterFile.toString());
       
        new TreeItem(newItem, SWT.NULL); // add a placeholder child item so we get the "expand" button
        masterFile = null;
        ++itemIndex;
        --i;
      } else {
        // should appear after file, delete stale item
        item.dispose();
      }
    }
    while ((masterFile != null) || (masterIndex < masterFiles.length)) {
      if (masterFile != null) {
        TreeItem newItem = new TreeItem(dirItem, SWT.NULL);
       
        folderBaseName = masterFile.getName().getBaseName();
       
        //treeInitFolder(newItem, folderBaseName , uri + FileName.SEPARATOR + folderBaseName );
        treeInitFolder(newItem, folderBaseName , masterFile.toString());
       
        new TreeItem(newItem, SWT.NULL); // add a placeholder child item so we get the "expand" button
        if (masterIndex == masterFiles.length) break;
      }
      masterFile = masterFiles[masterIndex++];
     
      if (!  VFSUtil.isDirectory(masterFile) ) {
        masterFile = null;
      }
    }
  }

  /**
   * Foreign method: removes all children of a TreeItem.
   * @param treeItem the TreeItem
   */
  private static void treeItemRemoveAll(TreeItem treeItem) {
    final TreeItem[] children = treeItem.getItems();
    for (int i = 0; i < children.length; ++i) {
      children[i].dispose();
    }
  }

  /**
   * Initializes a folder item.
   *
   * @param item the TreeItem to initialize
   * @param folder the FileObject associated with this TreeItem
   */
  private void treeInitFolder(TreeItem item, String folder, String uri) {
    item.setText(folder);
    item.setImage(iconCache.get(CFPluginImages.ICON_FOLDER));
    item.setData(TREEITEMDATA_FILE, folder);
    item.setData(TREEITEMDATA_URI, uri);
    item.setData(TREEITEMDATA_CONNECTIONID, item.getParentItem().getData(TREEITEMDATA_CONNECTIONID));   
    item.setData(TREEITEMDATA_IMAGEEXPANDED, iconCache.get(CFPluginImages.ICON_FOLDER_OPEN));
    item.setData(TREEITEMDATA_IMAGECOLLAPSED, iconCache.get(CFPluginImages.ICON_FOLDER));
  }

  /**
   * Initializes a volume item.
   *
   * @param item the TreeItem to initialize
   * @param volume the File associated with this TreeItem
   */
  private void treeInitVolume(TreeItem item, String connectionId ) {//FileObject volume) {
    FTPConnectionProperties connectionProperties = fConnections.get(connectionId);
    if(connectionProperties == null) {
      addLocalConnection(connectionId);
      connectionProperties = fConnections.get(connectionId);
    }
    item.setText(connectionId);
    item.setImage(iconCache.get(CFPluginImages.ICON_DRIVE));
    item.setData(TREEITEMDATA_FILE, connectionProperties.getConnectionid());
    item.setData(TREEITEMDATA_URI, connectionProperties.getURI());
    item.setData(TREEITEMDATA_CONNECTIONID, connectionProperties.getConnectionid());
    item.setData(TREEITEMDATA_IMAGEEXPANDED, iconCache.get(CFPluginImages.ICON_DRIVE_FTP));
    item.setData(TREEITEMDATA_IMAGECOLLAPSED, iconCache.get(CFPluginImages.ICON_DRIVE));
  }

  /**
   * Creates the file details table.
   *
   * @param parent the parent control
   */
  private void createTableView(Composite parent)
  {
    //Composite composite = new Composite(parent, SWT.NONE);
    TabFolder composite = new TabFolder(parent, SWT.NONE);
    GridLayout gridLayout = new GridLayout();
   
    gridLayout.numColumns = 1;
    gridLayout.marginHeight = gridLayout.marginWidth = 2;
    gridLayout.horizontalSpacing = gridLayout.verticalSpacing = 0;
    composite.setLayout(gridLayout);
   
    tabFileItems     = new TabItem(composite, SWT.CLOSE);
   
//    tableContentsOfLabel = new Label(composite, SWT.BORDER);
//    tableContentsOfLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL));

    table = new Table(composite, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.FULL_SELECTION);
    table.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL));

    //ColumnViewerToolTipSupport.enableFor(table);
    // Tooltips
//    final ToolTipHandler tooltip = new ToolTipHandler(parent.getParent().getShell());
//    tooltip.activateHoverHelp(table);
   
    tabFileItems.setControl(table);
   
    for (int i = 0; i < tableTitles.length; ++i) {
      TableColumn column = new TableColumn(table, SWT.NONE);
      column.setText(tableTitles[i]);
      column.setWidth(tableWidths[i]);
    }
    table.setHeaderVisible(true);
    // Listen for key presses
    table.addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent event) {
        // Del key pressed
        if ( event.keyCode == 127 )
        {
          FileObject[] files = getSelectedFiles();
          int count = files.length;
         
          // set a single delete or multiple delete text message
          String message = (count == 1)
            ? getResourceString("msg.box.delete.uri", new Object[]{files[0]})
            : getResourceString("msg.box.delete.resources", new Object[]{new Integer(count), getCurrentDirectory() }) ;
           
          if ( VFSUtil.MessageBoxYesNo( message) )
          {
            deleteObjects(files);
           
            // Refresh
            doRefresh();
          }
        }
      }

      private FileObject[] getSelectedFiles() {
        final TableItem[] items = table.getSelection();
        final FileObject[] files = new FileObject[items.length];
       
        for (int i = 0; i < items.length; ++i) {
          files[i] = (FileObject) items[i].getData(TABLEITEMDATA_FILE);
        }
        return files;
      }
    });
   
    // Listen for selections
    table.addSelectionListener(new SelectionAdapter() {
      // Fires when item is clicked
      public void widgetSelected(SelectionEvent event) {
        try {
          notifySelectedFiles(getSelectedFiles());
        } catch (Exception e) {
          //debug ("createTableView.widgetSelected " + e);
        }
      }
     
      // fires when list item is selected (double clicked)
      public void widgetDefaultSelected(SelectionEvent event) {
        doDefaultFileAction(getSelectedFiles());
      }
     
      private FileObject[] getSelectedFiles() {
        final TableItem[] items = table.getSelection();
        final FileObject[] files = new FileObject[items.length];
       
        for (int i = 0; i < items.length; ++i) {
          files[i] = (FileObject) items[i].getData(TABLEITEMDATA_FILE);
        }
        return files;
      }
    });

    createTableDragSource(table);
    createTableDropTarget(table);
  }

  /**
   * Creates the Drag & Drop DragSource for items being dragged from the table.
   *
   * @return the DragSource for the table
   */
  private DragSource createTableDragSource(final Table table) {
    DragSource dragSource = new DragSource(table, DND.DROP_COPY); // | DND.DROP_MOVE

    // transfer type is text data (file URI)
    dragSource.setTransfer(new Transfer[] { FileTransfer.getInstance() });
   
    dragSource.addDragListener(new DragSourceListener() {
      TableItem[] dndSelection = null;
      String[] sourceNames = null;
     
      public void dragStart(DragSourceEvent event){
        dndSelection = table.getSelection();
        sourceNames = null;
        event.doit = dndSelection.length > 0;
        isDragging = true;
      }
     
      public void dragFinished(DragSourceEvent event){
        dragSourceHandleDragFinished(event, sourceNames);
        dndSelection = null;
        sourceNames = null;
        isDragging = false;
       
//TODO        try {
//          handleDeferredRefresh();
//        } catch (Exception e) {
//          debug("createTableDragSource.dragFinished " + e);
//        }
       
      }
     
      public void dragSetData(DragSourceEvent event) {
        if (dndSelection == null || dndSelection.length == 0) return;
       
        if (! FileTransfer.getInstance().isSupportedType(event.dataType)) {
          error("createTableDragSource.dragSetData File Transfer data type text is required.");
          return;
        }
       
        // Cannot put Objects in the transfer event data field, they must be path
        // and begin with / in UNIX
        sourceNames  = new String[dndSelection.length+1];
        String connectionId = "";
        for (int i = 1; i < dndSelection.length; i++) {
          FileObject file = (FileObject) dndSelection[i].getData(TABLEITEMDATA_FILE);
          connectionId = (String) dndSelection[i].getData(TABLEITEMDATA_CONNECTIONID);
          /*
           * In Order for FileTransfer to Work, the name must begin w/
           * / in UNIX systems
           */
          sourceNames[i] = Os.isFamily(Os.OS_FAMILY_UNIX)
            ? FileName.SEPARATOR + file.toString()
            : file.toString();
         
System.out.println("createTableDragSource::dragSetData: sourceNames ["
    + i + "] path= " + sourceNames[i] );

        }
        sourceNames[0] = connectionId;
        event.data = sourceNames;
      }
    });
    return dragSource;
  }

  /**
   * Creates the Drag & Drop DropTarget for items being dropped onto the table.
   *
   * @return the DropTarget for the table
   */
  private DropTarget createTableDropTarget(final Table table){
    DropTarget dropTarget = new DropTarget(table, DND.DROP_COPY); // | DND.DROP_MOVE
   
    dropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() });
    //dropTarget.setTransfer(new Transfer[] { RTFTransfer.getInstance() });
   
    dropTarget.addDropListener(new DropTargetAdapter() {
      public void dragEnter(DropTargetEvent event) {
        isDropping = true;
      }
     
      public void dragLeave(DropTargetEvent event) {
        isDropping = false;
       
// TODO        try {
//          handleDeferredRefresh();
//        } catch (Exception e) {
//          debug("createTableDropTarget.dragLeave " + e);
//        }
       
      }
     
      public void dragOver(DropTargetEvent event) {
        dropTargetValidate(event, getTargetFile(event));
        event.feedback |= DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL;
      }
      public void drop(DropTargetEvent event) {
        FileAndConnectionId targetFile = getTargetFile(event);
        try {
          if (dropTargetValidate(event, targetFile))
            dropTargetHandleDrop(event, targetFile);
         
        } catch (Exception e) {
          debug(e); // "createTableDropTarget.drop " +
        }
      }
     
      private FileAndConnectionId getTargetFile(DropTargetEvent event) {
        // Determine the target FileObject for the drop
        TableItem item = table.getItem(table.toControl(new Point(event.x, event.y)));
        FileObject targetFile = null;
        String connectionId = "";
        if (item == null) {
          // We are over an unoccupied area of the table.
          // If it is a COPY, we can use the table's root file.
          if (event.detail == DND.DROP_COPY) {
            targetFile = (FileObject) table.getData(TABLEDATA_DIR);
            connectionId = (String) table.getData(TABLEDATA_CONNECTIONID);
          }
        } else {
          // We are over a particular item in the table, use the item's file
          targetFile = (FileObject) item.getData(TABLEITEMDATA_FILE);
          connectionId = (String) table.getData(TABLEITEMDATA_CONNECTIONID);
        }
        return new FileAndConnectionId(targetFile,connectionId);
      }
    });
    return dropTarget;
  }

 
  /**
   * Notifies the application components that a new current directory has been selected
   *
   * @param dir the directory that was selected, null is ignored
   */
  public void notifySelectedDirectory(FileObject dir, String connectionId)
    throws FileSystemException
  {
    if (dir == null || connectionId == null) return;
    if (getCurrentDirectory() != null && dir.equals(getCurrentDirectory())) return;
   
    setCurrentDirectory(dir);
    setCurrentConnectionId(connectionId);

//System.err.println("notifySelectedDirectory currentDirectory:"
//    + currentDirectory + " notify dir:" + dir );

    // Processing...
    VFSUtil.setUiStateListContents(shell, getCurrentDirectory().getName().toString());
   
    notifySelectedFiles(null);

    /* Table view:
     * Displays the contents of the selected directory.
     */
    workerUpdate(dir, connectionId, false);

    /* Combo view:
     * Sets the combo box to point to the selected directory.
     */
    final FileObject[] comboRoots = (FileObject[]) combo.getData(COMBODATA_ROOTS);
    int comboEntry = -1;
    if (comboRoots != null) {   
      for (int i = 0; i < comboRoots.length; ++i) {
        if (dir.equals(comboRoots[i])) {
          comboEntry = i;
          break;
        }
      }
    }
   
    if (comboEntry == -1) combo.setText(dir.getName().toString());
    else combo.select(comboEntry);

    /* Tree view:
     * If not already expanded, recursively expands the parents of the specified
     * directory until it is visible.
     */
    Vector /* of FileObject */ path = new Vector();
   
    // Build a stack of paths from the root of the tree
    while (dir != null) {
      path.add(dir);
      //dir = dir.getParentFile();
     
      try {
        dir = dir.getParent();
      } catch (Exception e) {
        System.err.println("notifySelectedDirectory:" + e);
      }
    }

//System.out.println("notifySelectedDirectory currentDirectory:"
//    + currentDirectory + " dir:" + dir + " tree path:" + path);

    // Recursively expand the tree to get to the specified directory
    TreeItem[] items = tree.getItems();
    TreeItem lastItem = null;
    String destConnectionId = null;
    TreeItem item = null;
    // filter by connection first
    for (int k = 0; k < items.length; ++k) {     
      item = items[k];
      destConnectionId     = (String)item.getData(TREEITEMDATA_CONNECTIONID);
      if(connectionId.equals(connectionId)){
        items = items[k].getItems();
      }
    }
   
    for (int i = path.size() - 1; i >= 0; --i)
    {
      final FileObject pathElement = (FileObject) path.elementAt(i);

      // Search for a particular FileObject in the array of tree items
      // No guarantee that the items are sorted in any recognizable fashion, so we'll
      // just sequential scan.  There shouldn't be more than a few thousand entries.
      item = null;
      for (int k = 0; k < items.length; ++k) {
        item = items[k];
        if (item.isDisposed()) continue;

        final String fileURI     = ((String)item.getData(TREEITEMDATA_URI));
        destConnectionId     = (String)item.getData(TREEITEMDATA_CONNECTIONID);
       
        // Resolving the children will attempt a server connection. This will hang the UI if the server is down
        // Thus perform a comparison by Host/Scheme/Path only
        // Cannot just compare the URI strings cuz FileObject hides the port number
       
        if (connectionId.equals(destConnectionId) && VFSUtil.compareURIs(fileURI, pathElement.toString()) )
          break;
      }
       
      if (item == null) break;
      lastItem = item;
     
      if (i != 0 && !item.getExpanded()) {
        treeExpandItem(item);
        item.setExpanded(true);
      }
      items = item.getItems();
    }
    if(connectionId.equals(destConnectionId)) {
      tree.setSelection((lastItem != null) ? new TreeItem[] { lastItem } : new TreeItem[0]);
    }

    /* Shell:
     * Sets the title to indicate the selected directory
     */
    VFSUtil.setUiStateIdle(shell, VFSUtil.stripUserTokens(getCurrentDirectory().toString()));
  }
 
  /**
   * Notifies the application components that files have been selected
   *
   * @param files the files that were selected, null or empty array indicates no active selection
   */
  void notifySelectedFiles(FileObject[] files) throws FileSystemException {
    /* Details:
     * Update the details that are visible on screen.
     */
    if ((files != null) && (files.length != 0))
    {
      numObjectsLabel.setText(getResourceString("details.NumberOfSelectedFiles.text",
        new Object[] { new Integer(files.length) }));
     
      long fileSize = 0L;
     
      for (int i = 0; i < files.length; ++i) {
        //fileSize += files[i].length();
        fileSize += files[i].getContent().getSize();
      }
     
      diskSpaceLabel.setText(getResourceString("details.FileSize.text",
        new Object[] { new Long(fileSize) }));
    } else {
      // No files selected
      diskSpaceLabel.setText("");
      if (getCurrentDirectory() != null)
      {
        int numObjects = getDirectoryList( getCurrentDirectory()).length;
       
        numObjectsLabel.setText(getResourceString("details.DirNumberOfObjects.text",
          new Object[] { new Integer(numObjects) }));
      } else {
        numObjectsLabel.setText("");
      }
    }
  }

  /**
   * Notifies the application components that files must be refreshed
   *
   * @param files the files that need refreshing, empty array is a no-op, null refreshes all
   */
  public void notifyRefreshFiles(FileObject[] files) throws FileSystemException {
    if (files != null && files.length == 0) return;

    if ((deferredRefreshRequested) && (deferredRefreshFiles != null) && (files != null)) {
      // merge requests
      FileObject[] newRequest = new FileObject[deferredRefreshFiles.length + files.length];
      System.arraycopy(deferredRefreshFiles, 0, newRequest, 0, deferredRefreshFiles.length);
      System.arraycopy(files, 0, newRequest, deferredRefreshFiles.length, files.length);
      deferredRefreshFiles = newRequest;
    } else {
      deferredRefreshFiles = files;
      deferredRefreshRequested = true;
    }
   
    handleDeferredRefresh();
  }

  /**
   * Handles deferred Refresh notifications (due to Drag & Drop)
   */
  void handleDeferredRefresh() throws FileSystemException
  {
//System.out.println("handleDeferredRefresh");

    if (isDragging || isDropping || ! deferredRefreshRequested) return;

    deferredRefreshRequested = false;
    FileObject[] files = deferredRefreshFiles;
    deferredRefreshFiles = null;

    shell.setCursor(CFPluginImages.cursors[CFPluginImages.cursorWait]);
   
    /* Table view:
     * Refreshes information about any files in the list and their children.
     */
    boolean refreshTable = false;
   
    if (files != null) {
      for (int i = 0; i < files.length; ++i) {
        final FileObject file = files[i];
        if (file.equals(getCurrentDirectory())) {
          refreshTable = true;
          break;
        }

        FileObject parentFile = file.getParent();
       
        if ((parentFile != null) && (parentFile.equals(getCurrentDirectory()))) {
          refreshTable = true;
          break;
        }
      }
    }
    else
      refreshTable = true;

    if (refreshTable)
      workerUpdate(getCurrentDirectory(), getCurrentConnectionId(), true);

    /* Combo view:
     * Refreshes the list of roots
     */
    final FileObject[] roots = getRoots(fsManager);

    if (files == null) {
      boolean refreshCombo = false;
      final FileObject[] comboRoots = (FileObject[]) combo.getData(COMBODATA_ROOTS);
   
      if ((comboRoots != null) && (comboRoots.length == roots.length)) {
        for (int i = 0; i < roots.length; ++i) {
          if (! roots[i].equals(comboRoots[i])) {
            refreshCombo = true;
            break;
          }
        }
      } else refreshCombo = true;

      if (refreshCombo) {
        combo.removeAll();
        combo.setData(COMBODATA_ROOTS, roots);
       
        for (int i = 0; i < roots.length; ++i) {
          final FileObject file = roots[i];
          combo.add(file.getName().toString() );
        }
      }
    }

    /* Tree view:
     * Refreshes information about any files in the list and their children.
     */
    treeRefresh(roots);
   
    treeInsertRemoteURIs();
   
    // Remind everyone where we are in the filesystem
    final FileObject dir = getCurrentDirectory();
    setCurrentDirectory(null);
    final String con = getCurrentConnectionId();
    setCurrentConnectionId(null);
    notifySelectedDirectory(dir,con);

    shell.setCursor(CFPluginImages.cursors[CFPluginImages.cursorDefault]);
  }

  /**
   * Performs the default action on a set of files.
   *
   * @param files the array of files to process
   */
  void doDefaultFileAction(FileObject[] files) {
    // only uses the 1st file (for now)
    if (files.length == 0) return;
    final FileObject file = files[0];

    if (VFSUtil.isDirectory(file)) {
      try {
        notifySelectedDirectory(file,getCurrentConnectionId());
      } catch (Exception e) {
        debug(e); // "FileViewer.doDefaultFileAction " +
      }
    }
    else {
      //final String fileName = file.getAbsolutePath();
      RemoteFileEditorInput input = new RemoteFileEditorInput(file);
      try {
        IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor(file.getName().getBaseName());       
        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow()
          .getActivePage();
        //page.openEditor(input, desc.getId());

        page.openEditor(input, CFMLEditor.ID);

       
        // wonder how this could work?
//        IFileStore fileStore;
//        fileStore = EFS.getFileSystem(file.getName().getScheme()).getStore(file.getURL().toURI());
//        IDE.openEditorOnFileStore( page, fileStore );
        } catch (Exception e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
     
//      if (! Program.launch(fileName)) { 
//        MessageBox dialog = new MessageBox(shell, SWT.ICON_ERROR | SWT.OK);
//        dialog.setMessage(getResourceString("error.FailedLaunch.message", new Object[] { fileName }));
//        dialog.setText(shell.getText ());
//        dialog.open();
//      }
    }
  }

  /**
   * Navigates to the parent directory
   */
  void doParent() {
    if (getCurrentDirectory() == null) return;
    //FileObject parentDirectory = currentDirectory.getParentFile();
    try {
      FileObject parentDirectory = getCurrentDirectory().getParent();
      notifySelectedDirectory(parentDirectory,getCurrentConnectionId());
    } catch (Exception e) {
      System.err.println("FileViewer::doParent " + e);
    }
  }
  
  /**
   * Performs a refresh
   */
  void doRefresh() {
    try {
      notifyRefreshFiles(null);
    } catch (Exception e) {
      debug (e); // "FileViewer.doRefresh " +
    }
  }

  /**
   * Validates a drop target as a candidate for a drop operation.
   * <p>
   * Used in dragOver() and dropAccept().<br>
   * Note event.detail is set to DND.DROP_NONE by this method if the target is not valid.
   * </p>
   * @param event the DropTargetEvent to validate
   * @param fileAndConnectionId the FileObject representing the drop target location
   *        under inspection, or null if none
   */
  private boolean dropTargetValidate(DropTargetEvent event, FileAndConnectionId fileAndConnectionId)
  {
    //if (targetFile != null && targetFile.isDirectory()) {
    if (fileAndConnectionId.file != null && VFSUtil.isDirectory(fileAndConnectionId.file) ) {
      if (event.detail != DND.DROP_COPY && event.detail != DND.DROP_MOVE) {
        event.detail = DND.DROP_COPY; // DND.DROP_MOVE;
      }
    } else {
      event.detail = DND.DROP_NONE;
    }
    return event.detail != DND.DROP_NONE;
  }

  /**
   * Handles a drop on a dropTarget.
   * <p>
   * Used in drop().<br>
   * Note event.detail is modified by this method.
   * </p>
   * @param event the DropTargetEvent passed as parameter to the drop() method
   * @param targetFile the FileObject representing the drop target location
   *        under inspection, or null if none
   */
  private void dropTargetHandleDrop(DropTargetEvent event, FileAndConnectionId fileAndConnectionId)
    throws IOException
  {

    // Get dropped data (an array of filenames)
//    if (! dropTargetValidate(event, targetFile))
//      return;
   
    final String[] sourceNames = (String[]) event.data;

    if (sourceNames == null) event.detail = DND.DROP_NONE;
    if (event.detail == DND.DROP_NONE) return;
    VFSUtil.copyFiles(fsManager, sourceNames, fConnections.get(fSourceConnectionId), fileAndConnectionId.file, fConnections.get(fileAndConnectionId.connectionId));
    //VFSUtil.copyFiles(fsManager, sourceNames, fileAndConnectionId.file);
   
    // refresh
        notifyRefreshFiles(new FileObject[] { fileAndConnectionId.file });
       
  }

  /**
   * Handles the completion of a drag on a dragSource.
   * <p>
   * Used in dragFinished().<br>
   * </p>
   * @param event the DragSourceEvent passed as parameter to the dragFinished() method
   * @param sourceNames the names of the files that were dragged (event.data is invalid)
   * @param sourceConnectionId
   */
  private void dragSourceHandleDragFinished(DragSourceEvent event, String[] sourceNames) {
    if (sourceNames == null) return;
    if (event.detail != DND.DROP_MOVE) return;
   
    // Get array of files that were actually transferred
    // Do nothing w/ source files
  }


  /**
   * Gets filesystem root entries
   * @param fsManager
   * @return an array of Files corresponding to the root directories on the platform,
   *         may be empty but not null
   */
  FileObject[] getRoots( FileSystemManager fsManager)
    throws FileSystemException
  {
    /*
     * On JDK 1.22 only...
     */
    // return File.listRoots();
    FileObject[] roots     = null;
//    FileObject[] newRequest = null;
    /*
     * On JDK 1.1.7 and beyond...
     * -- PORTABILITY ISSUES HERE --
     */
    if (System.getProperty ("os.name").indexOf ("Windows") != -1) {
      Vector /* of FileObject */ list = new Vector();
      list.add(fsManager.resolveFile(DRIVE_A));
     
      for (char i = 'c'; i <= 'z'; ++i)
      {
        //FileObject drive = new FileObject(i + ":" + FileName.SEPARATOR);
        FileObject drive = fsManager.resolveFile(i + ":" + FileName.SEPARATOR);
       
        if (VFSUtil.isDirectory(drive) && drive.exists()) {
          list.add(drive);
          if (initial && i == 'c') {
            setCurrentDirectory(drive);
            setCurrentConnectionId(drive.getName().toString());
            initial = false;
          }
        }
      }
      roots = (FileObject[]) list.toArray(new FileObject[list.size()]);
      VFSUtil.sortFiles(roots);
     
      //return roots;
    }
    else {
      FileObject root = fsManager.resolveFile (FileName.SEPARATOR);
     
      if (initial) {
        setCurrentDirectory(root);
        setCurrentConnectionId(root.getName().toString());
        initial = false;
      }
      roots = new FileObject[] { root };
    }

   
    return roots; //newRequest;
  }
 

  private void addLocalConnection(String connectionId) {
    FTPConnectionProperties newConProps = new FTPConnectionProperties(connectionId);
    newConProps.setURI(connectionId);
    fConnections.put(connectionId, newConProps);
  }

  /*
   * This worker updates the table with file information in the background.
   * <p>
   * Implementation notes:
   * <ul>
   * <li> It is designed such that it can be interrupted cleanly.
   * <li> It uses asyncExec() in some places to ensure that SWT Widgets are manipulated in the
   *      right thread.  Exclusive use of syncExec() would be inappropriate as it would require a pair
   *      of context switches between each table update operation.
   * </ul>
   * </p>
   */

  /**
   * Stops the worker and waits for it to terminate.
   */
  void workerStop() {
    if (workerThread == null) return;
    synchronized(workerLock) {
      workerCancelled = true;
      workerStopped = true;
      workerLock.notifyAll();
    }
    while (workerThread != null) {
      if (! display.readAndDispatch()) display.sleep();
    }
  }

  /**
   * Notifies the worker that it should update itself with new data.
   * Cancels any previous operation and begins a new one.
   *
   * @param dir the new base directory for the table, null is ignored
   * @param currentConnectionId
   * @param force if true causes a refresh even if the data is the same
   */
  void workerUpdate(FileObject dir, String connectionId, boolean force) {
    if (dir == null) return;
    if ((!force) && (workerNextDir != null) && (workerNextDir.equals(dir))) return;

    synchronized(workerLock) {
      workerNextDir = dir;
      workerNextConnectionId = connectionId;
      workerStopped = false;
      workerCancelled = true;
      workerLock.notifyAll();
    }
    if (workerThread == null) {
      workerThread = new Thread(workerRunnable);
      workerThread.start();
    }
  }

  /**
   * Manages the worker's thread
   */
  private final Runnable workerRunnable = new Runnable() {
    public void run()  {
      while (! workerStopped) {
        synchronized(workerLock) {
          workerCancelled = false;
          workerStateDir = workerNextDir;
          workerStateConnectionId = workerNextConnectionId;
        }
       
        workerExecute();
       
        synchronized(workerLock) {
          try {
            if ((!workerCancelled) && (workerStateDir == workerNextDir)) workerLock.wait();
          } catch (InterruptedException e) {
          }
        }
      }
      workerThread = null;
      // wake up UI thread in case it is in a modal loop awaiting thread termination
      // (see workerStop())
      display.wake();
    }
  };
 
  /**
   * Updates the table's contents
   */
  private void workerExecute() {
    FileObject[] dirList;
   
    // Clear existing information
    display.syncExec(new Runnable()  {
      public void run()
      {
        tabFileItems.setText(VFSView.getResourceString("details.ContentsOf.text",
            new Object[] { VFSUtil.stripUserTokens(workerStateDir.toString()) }));

        table.removeAll();
        table.setData(TABLEDATA_DIR, workerStateDir);
        table.setData(TABLEDATA_CONNECTIONID,workerStateConnectionId);
      }
    });
   
    try {
      dirList = getDirectoryList( workerStateDir);

      for (int i = 0; (! workerCancelled) && (i < dirList.length); i++) {
        workerAddFileDetails(dirList[i],workerStateConnectionId);
      }

    } catch (FileSystemException e) {
      VFSUtil.MessageBoxError( e.getMessage());
    }
  }
   
  /**
   * Adds a file's detail information to the directory list
   */
  private void workerAddFileDetails(final FileObject file, final String connectionId)
  {
    long date = 0;
    try {
      date = file.getContent().getLastModifiedTime();
    } catch (Exception e) {}
   
    final String nameString = file.getName().getBaseName();
    final String dateString = dateFormat.format(new Date(date));
    final String sizeString;
    final String typeString;
    final Image iconImage;
   
    // If directory
    if (VFSUtil.isDirectory(file))
    {
      typeString = getResourceString("filetype.Folder");
      sizeString = "";
      iconImage = iconCache.get(CFPluginImages.ICON_FOLDER);
    }
    else {
      long size = 0;
      try {
        size = file.getContent().getSize();
      } catch (Exception e) {}
     
      // It is a file, get size
      sizeString = getResourceString("filesize.KB",
        new Object[] { new Long((size + 512) / 1024) });
     
      int dot = nameString.lastIndexOf('.');
      if (dot != -1)
      {
        String extension = nameString.substring(dot);
        Program program = Program.findProgram(extension);
       
        // get icon based on extension
        if (program != null)
        {
          typeString = program.getName();
          iconImage = CFPluginImages.getIconFromProgram(program);
        }
        else {
          typeString = getResourceString("filetype.Unknown", new Object[] { extension.toUpperCase() });
          iconImage = iconCache.get(CFPluginImages.ICON_FILE);
        }
      }
      else {
        typeString = getResourceString("filetype.None");
        iconImage = iconCache.get(CFPluginImages.ICON_FILE);
      }
    }
   
    // Table values: Name, Size, Type, Date
    final String[] strings = new String[] { nameString, sizeString, typeString, dateString };

    display.syncExec(new Runnable() {
      public void run () {
        String attrs = VFSUtil.getFileAttributes(file);
       
        // guard against the shell being closed before this runs
        if (shell.isDisposed()) return;
        TableItem tableItem = new TableItem(table, 0);
        tableItem.setText(strings);
        tableItem.setImage(iconImage);
        tableItem.setData(TABLEITEMDATA_FILE, file);
        tableItem.setData(TABLEITEMDATA_CONNECTIONID, connectionId);
       
        if ( attrs.length() > 0 )
          tableItem.setData ("TIP_TEXT", attrs);
      }
    });
  }
 
  /**
   * Resolve a FileSystem Uri seting progress UI messages
   */
  public FileObject resolveURI (String Uri, String connectionId) throws FileSystemException
  {
    shell.setCursor(CFPluginImages.cursors[CFPluginImages.cursorWait]);
    shell.setText(getResourceString("Title.Resolving"
        , new Object[] { Uri }));
    FTPConnectionProperties connectionProperties = fConnections.get(connectionId);

    FileObject fo = fsManager.resolveFile(Uri,connectionProperties.getFileSystemOptions());

    shell.setCursor(CFPluginImages.cursors[CFPluginImages.cursorDefault]);
    shell.setText(getResourceString("Title"
        , new Object[] { Uri }));
   
    return fo;
  }
 
  /**
   * Gets a directory listing
   *
   * @param file the directory to be listed
   * @return an array of files this directory contains, may be empty but not null
   */
  private FileObject[] getDirectoryList(FileObject file)
    throws FileSystemException
  {
    FileObject[] list = file.getChildren();
   
    if (list == null) {
      return new FileObject[0];
    }

    VFSUtil.sortFiles(list);

    return list;
  }
 
  /**
   * Delete selected files
   * @param files
   */
  private void deleteObjects (FileObject[] files)
  {
    // run a delete operation
        try {
          FileOperation delOperation = new FileOperation(fsManager, FileOperation.DELETE, true);
          delOperation.setDeleteArgs(files);
         
          new ProgressMonitorDialog(shell).run(true, true, delOperation);
        }
        catch (InvocationTargetException e)
        {
            VFSUtil.MessageBoxError(e.getMessage());
        } catch (InterruptedException e)
        {
          VFSUtil.MessageBoxInfo(e.getMessage());
        }

        // Refresh
    doRefresh();
  }

  @Override
  public void setFocus() {
    // TODO Auto-generated method stub
   
  }

  public boolean show(ShowInContext context) {
      String filePath;
   
        if (table == null || context == null)
           return false;
        ISelection sel = context.getSelection();
        Object input = context.getInput();
        if (input instanceof CFJavaFileEditorInput) {
          filePath = ((CFJavaFileEditorInput)input).getPath(input).toString();
        }
    else {
      filePath = ((FileEditorInput)input).getFile().getRawLocation().toString();
    }
   
       
        //showFile(filePath);
        return false;
     }
 
  /**
   * @param currentConnectionId the currentConnectionId to set
   */
  public void setCurrentConnectionId(String currentConnectionId) {
    this.currentConnectionId = currentConnectionId;
  }

  /**
   * @return the currentConnectionId
   */
  public String getCurrentConnectionId() {
    return currentConnectionId;
  }

  /**
   * @param currentDirectory the currentDirectory to set
   */
  public void setCurrentDirectory(FileObject currentDirectory) {
    this.currentDirectory = currentDirectory;
  }

  /**
   * @return the currentDirectory
   */
  public FileObject getCurrentDirectory() {
    return currentDirectory;
  }

  public class FileAndConnectionId {
    public FileObject file = null;
    public String connectionId = "";
    public FileAndConnectionId(FileObject file, String connectionId) {
      super();
      this.file = file;
      this.connectionId = connectionId;
    }
   
  }
}
TOP

Related Classes of org.cfeclipse.cfml.views.explorer.vfs.view.VFSView$FileAndConnectionId

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.