文件阅读器SWT : 文件浏览器 « SWT-JFace-Eclipse « Java

En
Java
1. 图形用户界面
2. 三维图形动画
3. 高级图形
4. 蚂蚁编译
5. Apache类库
6. 统计图
7. 
8. 集合数据结构
9. 数据类型
10. 数据库JDBC
11. 设计模式
12. 开发相关类
13. EJB3
14. 电子邮件
15. 事件
16. 文件输入输出
17. 游戏
18. 泛型
19. GWT
20. Hibernate
21. 本地化
22. J2EE平台
23. 基于J2ME
24. JDK-6
25. JNDI的LDAP
26. JPA
27. JSP技术
28. JSTL
29. 语言基础知识
30. 网络协议
31. PDF格式RTF格式
32. 映射
33. 常规表达式
34. 脚本
35. 安全
36. Servlets
37. Spring
38. Swing组件
39. 图形用户界面
40. SWT-JFace-Eclipse
41. 线程
42. 应用程序
43. Velocity
44. Web服务SOA
45. 可扩展标记语言
Java 教程
Java » SWT-JFace-Eclipse » 文件浏览器屏幕截图 
文件阅读器SWT
文件阅读器SWT



import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Vector;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.TreeAdapter;
import org.eclipse.swt.events.TreeEvent;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

/**
 * File Viewer example
 */
public class SWTFileViewerDemo {
  private final static String DRIVE_A = "a:" + File.separator;

  private final static String DRIVE_B = "b:" + File.separator;

  /* UI elements */
  private Display display;

  private Shell shell;

  private ToolBar toolBar;

  private Label numObjectsLabel;

  private Label diskSpaceLabel;

  private File currentDirectory = null;

  private boolean initial = true;

  /* Drag and drop optimizations */
  private boolean isDragging = false// if this app is dragging

  private boolean isDropping = false// if this app is dropping

  private File[] processedDropFiles = null// so Drag only deletes what it
                        // needs to

  private File[] deferredRefreshFiles = null// to defer notifyRefreshFiles
                        // while we do DND

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

  private ProgressDialog progressDialog = null// progress dialog for
                          // locally-initiated
                          // operations

  /* 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 Combo combo;

  /* Tree view */
  private IconCache iconCache = new IconCache();

  private static final String TREEITEMDATA_FILE = "TreeItem.file";

  // File: File associated with tree item
  private static final String TREEITEMDATA_IMAGEEXPANDED = "TreeItem.imageExpanded";

  // Image: shown when item is expanded
  private 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

  private Tree tree;

  private Label treeScopeLabel;

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

  private static final String TABLEITEMDATA_FILE = "TableItem.file";

  // File: File associated with table row
  private static final String TABLEDATA_DIR = "Table.dir";

  // File: Currently visible directory
  private static final int[] tableWidths = new int[] { 1506075150 };

  private final String[] tableTitles = new String[] {
      SWTFileViewerDemo.getResourceString("table.Name.title"),
      SWTFileViewerDemo.getResourceString("table.Size.title"),
      SWTFileViewerDemo.getResourceString("table.Type.title"),
      SWTFileViewerDemo.getResourceString("table.Modified.title") };

  private Table table;

  private Label tableContentsOfLabel;

  /* 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 File workerStateDir = null;

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

  /* Simulate only flag */
  // when true, disables actual filesystem manipulations and outputs results
  // to standard out
  private boolean simulateOnly = true;

  /**
   * Runs main program.
   */
  public static void main(String[] args) {
    Display display = new Display();
    SWTFileViewerDemo application = new SWTFileViewerDemo();
    Shell shell = application.open(display);
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch())
        display.sleep();
    }
    application.close();
    display.dispose();
  }

  /**
   * Opens the main program.
   */
  public Shell open(Display display) {
    // Create the window
    this.display = display;
    iconCache.initResources(display);
    shell = new Shell();
    createShellContents();
    notifyRefreshFiles(null);
    shell.open();
    return shell;
  }

  /**
   * Closes the main program.
   */
  void close() {
    workerStop();
    iconCache.freeResources();
  }

  /**
   * Returns a string from the resource bundle. We don't want to crash because
   * of a missing String. Returns the key if not found.
   */
  static String getResourceString(String key) {
      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.
   */
  static String getResourceString(String key, Object[] args) {
    try {
      return MessageFormat.format(getResourceString(key), args);
    catch (MissingResourceException e) {
      return key;
    catch (NullPointerException e) {
      return "!" + key + "!";
    }
  }

  /**
   * Construct the UI
   
   @param container
   *            the ShellContainer managing the Shell we are rendering inside
   */
  private void createShellContents() {
    shell.setText(getResourceString("Title"new Object[] { "" }));
    shell.setImage(iconCache.stockImages[iconCache.shellIcon]);
    Menu bar = new Menu(shell, SWT.BAR);
    shell.setMenuBar(bar);
    createFileMenu(bar);
    createHelpMenu(bar);

    GridLayout gridLayout = new GridLayout();
    gridLayout.numColumns = 3;
    gridLayout.marginHeight = gridLayout.marginWidth = 0;
    shell.setLayout(gridLayout);

    GridData gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
    gridData.widthHint = 185;
    createComboView(shell, gridData);
    gridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
    gridData.horizontalSpan = 2;
    createToolBar(shell, gridData);

    SashForm sashForm = new SashForm(shell, SWT.NONE);
    sashForm.setOrientation(SWT.HORIZONTAL);
    gridData = new GridData(GridData.FILL_HORIZONTAL
        | GridData.FILL_VERTICAL);
    gridData.horizontalSpan = 3;
    sashForm.setLayoutData(gridData);
    createTreeView(sashForm);
    createTableView(sashForm);
    sashForm.setWeights(new int[] { 2});

    numObjectsLabel = new Label(shell, SWT.BORDER);
    gridData = new GridData(GridData.FILL_HORIZONTAL
        | GridData.VERTICAL_ALIGN_FILL);
    gridData.widthHint = 185;
    numObjectsLabel.setLayoutData(gridData);

    diskSpaceLabel = new Label(shell, SWT.BORDER);
    gridData = new GridData(GridData.FILL_HORIZONTAL
        | GridData.VERTICAL_ALIGN_FILL);
    gridData.horizontalSpan = 2;
    diskSpaceLabel.setLayoutData(gridData);
  }

  /**
   * Creates the File Menu.
   
   @param parent
   *            the parent menu
   */
  private void createFileMenu(Menu parent) {
    Menu menu = new Menu(parent);
    MenuItem header = new MenuItem(parent, SWT.CASCADE);
    header.setText(getResourceString("menu.File.text"));
    header.setMenu(menu);

    final MenuItem simulateItem = new MenuItem(menu, SWT.CHECK);
    simulateItem.setText(getResourceString("menu.File.SimulateOnly.text"));
    simulateItem.setSelection(simulateOnly);
    simulateItem.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        simulateOnly = simulateItem.getSelection();
      }
    });

    MenuItem item = new MenuItem(menu, SWT.PUSH);
    item.setText(getResourceString("menu.File.Close.text"));
    item.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        shell.close();
      }
    });
  }

  /**
   * Creates the Help Menu.
   
   @param parent
   *            the parent menu
   */
  private void createHelpMenu(Menu parent) {
    Menu menu = new Menu(parent);
    MenuItem header = new MenuItem(parent, SWT.CASCADE);
    header.setText(getResourceString("menu.Help.text"));
    header.setMenu(menu);

    MenuItem item = new MenuItem(menu, SWT.PUSH);
    item.setText(getResourceString("menu.Help.About.text"));
    item.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        MessageBox box = new MessageBox(shell, SWT.ICON_INFORMATION
            | SWT.OK);
        box.setText(getResourceString("dialog.About.title"));
        box.setMessage(getResourceString("dialog.About.description",
            new Object[] { System.getProperty("os.name") }));
        box.open();
      }
    });
  }

  /**
   * Creates the toolbar
   
   @param shell
   *            the shell on which to attach the toolbar
   @param layoutData
   *            the layout data
   */
  private void createToolBar(final Shell shell, Object layoutData) {
    toolBar = new ToolBar(shell, SWT.NULL);
    toolBar.setLayoutData(layoutData);
    ToolItem item = new ToolItem(toolBar, SWT.SEPARATOR);
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.stockImages[iconCache.cmdParent]);
    item.setToolTipText(getResourceString("tool.Parent.tiptext"));
    item.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        doParent();
      }
    });
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.stockImages[iconCache.cmdRefresh]);
    item.setToolTipText(getResourceString("tool.Refresh.tiptext"));
    item.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        doRefresh();
      }
    });
    SelectionAdapter unimplementedListener = new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        MessageBox box = new MessageBox(shell, SWT.ICON_INFORMATION
            | SWT.OK);
        box.setText(getResourceString("dialog.NotImplemented.title"));
        box
            .setMessage(getResourceString("dialog.ActionNotImplemented.description"));
        box.open();
      }
    };

    item = new ToolItem(toolBar, SWT.SEPARATOR);
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.stockImages[iconCache.cmdCut]);
    item.setToolTipText(getResourceString("tool.Cut.tiptext"));
    item.addSelectionListener(unimplementedListener);
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.stockImages[iconCache.cmdCopy]);
    item.setToolTipText(getResourceString("tool.Copy.tiptext"));
    item.addSelectionListener(unimplementedListener);
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.stockImages[iconCache.cmdPaste]);
    item.setToolTipText(getResourceString("tool.Paste.tiptext"));
    item.addSelectionListener(unimplementedListener);

    item = new ToolItem(toolBar, SWT.SEPARATOR);
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.stockImages[iconCache.cmdDelete]);
    item.setToolTipText(getResourceString("tool.Delete.tiptext"));
    item.addSelectionListener(unimplementedListener);
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.stockImages[iconCache.cmdRename]);
    item.setToolTipText(getResourceString("tool.Rename.tiptext"));
    item.addSelectionListener(unimplementedListener);

    item = new ToolItem(toolBar, SWT.SEPARATOR);
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.stockImages[iconCache.cmdSearch]);
    item.setToolTipText(getResourceString("tool.Search.tiptext"));
    item.addSelectionListener(unimplementedListener);
    item = new ToolItem(toolBar, SWT.PUSH);
    item.setImage(iconCache.stockImages[iconCache.cmdPrint]);
    item.setToolTipText(getResourceString("tool.Print.tiptext"));
    item.addSelectionListener(unimplementedListener);
  }

  /**
   * Creates the combo box view.
   
   @param parent
   *            the parent control
   */
  private void createComboView(Composite parent, Object layoutData) {
    combo = new Combo(parent, SWT.NONE);
    combo.setLayoutData(layoutData);
    combo.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        final File[] roots = (File[]) combo.getData(COMBODATA_ROOTS);
        if (roots == null)
          return;
        int selection = combo.getSelectionIndex();
        if (selection >= && selection < roots.length) {
          notifySelectedDirectory(roots[selection]);
        }
      }

      public void widgetDefaultSelected(SelectionEvent e) {
        final String lastText = (Stringcombo
            .getData(COMBODATA_LASTTEXT);
        String text = combo.getText();
        if (text == null)
          return;
        if (lastText != null && lastText.equals(text))
          return;
        combo.setData(COMBODATA_LASTTEXT, text);
        notifySelectedDirectory(new File(text));
      }
    });
  }

  /**
   * 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(SWTFileViewerDemo
        .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];
          File file = (Fileitem.getData(TREEITEMDATA_FILE);

          notifySelectedDirectory(file);
        }
      }

      public void widgetDefaultSelected(SelectionEvent event) {
        final TreeItem[] selection = tree.getSelection();
        if (selection != null && selection.length != 0) {
          TreeItem item = selection[0];
          item.setExpanded(true);
          treeExpandItem(item);
        }
      }
    });
    tree.addTreeListener(new TreeAdapter() {
      public void treeExpanded(TreeEvent event) {
        final TreeItem item = (TreeItemevent.item;
        final Image image = (Imageitem
            .getData(TREEITEMDATA_IMAGEEXPANDED);
        if (image != null)
          item.setImage(image);
        treeExpandItem(item);
      }

      public void treeCollapsed(TreeEvent event) {
        final TreeItem item = (TreeItemevent.item;
        final Image image = (Imageitem
            .getData(TREEITEMDATA_IMAGECOLLAPSED);
        if (image != null)
          item.setImage(image);
      }
    });
    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_MOVE
        | DND.DROP_COPY);
    dragSource.setTransfer(new Transfer[] { FileTransfer.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;
        handleDeferredRefresh();
      }

      public void dragSetData(DragSourceEvent event) {
        if (dndSelection == null || dndSelection.length == 0)
          return;
        if (!FileTransfer.getInstance().isSupportedType(event.dataType))
          return;

        sourceNames = new String[dndSelection.length];
        for (int i = 0; i < dndSelection.length; i++) {
          File file = (FiledndSelection[i]
              .getData(TREEITEMDATA_FILE);
          sourceNames[i= file.getAbsolutePath();
        }
        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_MOVE
        | DND.DROP_COPY);
    dropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() });
    dropTarget.addDropListener(new DropTargetAdapter() {
      public void dragEnter(DropTargetEvent event) {
        isDropping = true;
      }

      public void dragLeave(DropTargetEvent event) {
        isDropping = false;
        handleDeferredRefresh();
      }

      public void dragOver(DropTargetEvent event) {
        dropTargetValidate(event, getTargetFile(event));
        event.feedback |= DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL;
      }

      public void drop(DropTargetEvent event) {
        File targetFile = getTargetFile(event);
        if (dropTargetValidate(event, targetFile))
          dropTargetHandleDrop(event, targetFile);
      }

      private File getTargetFile(DropTargetEvent event) {
        // Determine the target File for the drop
        TreeItem item = tree.getItem(tree.toControl(new Point(event.x,
            event.y)));
        File targetFile = null;
        if (item != null) {
          // We are over a particular item in the tree, use the item's
          // file
          targetFile = (Fileitem.getData(TREEITEMDATA_FILE);
        }
        return targetFile;
      }
    });
    return dropTarget;
  }

  /**
   * Handles expand events on a tree item.
   
   @param item
   *            the TreeItem to fill in
   */
  private void treeExpandItem(TreeItem item) {
    shell.setCursor(iconCache.stockCursors[iconCache.cursorWait]);
    final Object stub = item.getData(TREEITEMDATA_STUB);
    if (stub == null)
      treeRefreshItem(item, true);
    shell.setCursor(iconCache.stockCursors[iconCache.cursorDefault]);
  }

  /**
   * Traverse the entire tree and update only what has changed.
   
   @param roots
   *            the root directory listing
   */
  private void treeRefresh(File[] 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 File itemFile = (Fileitem.getData(TREEITEMDATA_FILE);
      if ((itemFile == null|| (masterIndex == masterFiles.length)) {
        // remove bad item or placeholder
        item.dispose();
        continue;
      }
      final File masterFile = masterFiles[masterIndex];
      int compare = compareFiles(masterFile, itemFile);
      if (compare == 0) {
        // same file, update it
        treeRefreshItem(item, false);
        ++itemIndex;
        ++masterIndex;
      else if (compare < 0) {
        // should appear before file, insert it
        TreeItem newItem = new TreeItem(tree, SWT.NULL, itemIndex);
        treeInitVolume(newItem, 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 File masterFile = masterFiles[masterIndex];
      TreeItem newItem = new TreeItem(tree, SWT.NULL);
      treeInitVolume(newItem, masterFile);
      new TreeItem(newItem, SWT.NULL)// placeholder child item to get
                        // "expand" button
    }
  }

  /**
   * 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) {
    final File dir = (FiledirItem.getData(TREEITEMDATA_FILE);

    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 */
    File[] subFiles = (dir != null? SWTFileViewerDemo.getDirectoryList(dir)
        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 File[] masterFiles = subFiles;
    int masterIndex = 0;
    int itemIndex = 0;
    File masterFile = null;
    for (int i = 0; i < items.length; ++i) {
      while ((masterFile == null&& (masterIndex < masterFiles.length)) {
        masterFile = masterFiles[masterIndex++];
        if (!masterFile.isDirectory())
          masterFile = null;
      }

      final TreeItem item = items[i];
      final File itemFile = (Fileitem.getData(TREEITEMDATA_FILE);
      if ((itemFile == null|| (masterFile == null)) {
        // remove bad item or placeholder
        item.dispose();
        continue;
      }
      int compare = compareFiles(masterFile, itemFile);
      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);
        treeInitFolder(newItem, masterFile);
        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);
        treeInitFolder(newItem, masterFile);
        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 (!masterFile.isDirectory())
        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 File associated with this TreeItem
   */
  private void treeInitFolder(TreeItem item, File folder) {
    item.setText(folder.getName());
    item.setImage(iconCache.stockImages[iconCache.iconClosedFolder]);
    item.setData(TREEITEMDATA_FILE, folder);
    item.setData(TREEITEMDATA_IMAGEEXPANDED,
        iconCache.stockImages[iconCache.iconOpenFolder]);
    item.setData(TREEITEMDATA_IMAGECOLLAPSED,
        iconCache.stockImages[iconCache.iconClosedFolder]);
  }

  /**
   * Initializes a volume item.
   
   @param item
   *            the TreeItem to initialize
   @param volume
   *            the File associated with this TreeItem
   */
  private void treeInitVolume(TreeItem item, File volume) {
    item.setText(volume.getPath());
    item.setImage(iconCache.stockImages[iconCache.iconClosedDrive]);
    item.setData(TREEITEMDATA_FILE, volume);
    item.setData(TREEITEMDATA_IMAGEEXPANDED,
        iconCache.stockImages[iconCache.iconOpenDrive]);
    item.setData(TREEITEMDATA_IMAGECOLLAPSED,
        iconCache.stockImages[iconCache.iconClosedDrive]);
  }

  /**
   * Creates the file details table.
   
   @param parent
   *            the parent control
   */
  private void createTableView(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);
    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));

    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);
    table.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent event) {
        notifySelectedFiles(getSelectedFiles());
      }

      public void widgetDefaultSelected(SelectionEvent event) {
        doDefaultFileAction(getSelectedFiles());
      }

      private File[] getSelectedFiles() {
        final TableItem[] items = table.getSelection();
        final File[] files = new File[items.length];

        for (int i = 0; i < items.length; ++i) {
          files[i(Fileitems[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_MOVE
        | DND.DROP_COPY);
    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;
        handleDeferredRefresh();
      }

      public void dragSetData(DragSourceEvent event) {
        if (dndSelection == null || dndSelection.length == 0)
          return;
        if (!FileTransfer.getInstance().isSupportedType(event.dataType))
          return;

        sourceNames = new String[dndSelection.length];
        for (int i = 0; i < dndSelection.length; i++) {
          File file = (FiledndSelection[i]
              .getData(TABLEITEMDATA_FILE);
          sourceNames[i= file.getAbsolutePath();
        }
        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_MOVE
        | DND.DROP_COPY);
    dropTarget.setTransfer(new Transfer[] { FileTransfer.getInstance() });
    dropTarget.addDropListener(new DropTargetAdapter() {
      public void dragEnter(DropTargetEvent event) {
        isDropping = true;
      }

      public void dragLeave(DropTargetEvent event) {
        isDropping = false;
        handleDeferredRefresh();
      }

      public void dragOver(DropTargetEvent event) {
        dropTargetValidate(event, getTargetFile(event));
        event.feedback |= DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL;
      }

      public void drop(DropTargetEvent event) {
        File targetFile = getTargetFile(event);
        if (dropTargetValidate(event, targetFile))
          dropTargetHandleDrop(event, targetFile);
      }

      private File getTargetFile(DropTargetEvent event) {
        // Determine the target File for the drop
        TableItem item = table.getItem(table.toControl(new Point(
            event.x, event.y)));
        File targetFile = null;
        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 = (Filetable.getData(TABLEDATA_DIR);
          }
        else {
          // We are over a particular item in the table, use the
          // item's file
          targetFile = (Fileitem.getData(TABLEITEMDATA_FILE);
        }
        return targetFile;
      }
    });
    return dropTarget;
  }

  /**
   * Notifies the application components that a new current directory has been
   * selected
   
   @param dir
   *            the directory that was selected, null is ignored
   */
  void notifySelectedDirectory(File dir) {
    if (dir == null)
      return;
    if (currentDirectory != null && dir.equals(currentDirectory))
      return;
    currentDirectory = dir;
    notifySelectedFiles(null);

    /*
     * Shell: Sets the title to indicate the selected directory
     */
    shell.setText(getResourceString("Title",
        new Object[] { currentDirectory.getPath() }));

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

    /*
     * Combo view: Sets the combo box to point to the selected directory.
     */
    final File[] comboRoots = (File[]) 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.getPath());
    else
      combo.select(comboEntry);

    /*
     * Tree view: If not already expanded, recursively expands the parents
     * of the specified directory until it is visible.
     */
    Vector /* of File */path = new Vector();
    // Build a stack of paths from the root of the tree
    while (dir != null) {
      path.add(dir);
      dir = dir.getParentFile();
    }
    // Recursively expand the tree to get to the specified directory
    TreeItem[] items = tree.getItems();
    TreeItem lastItem = null;
    for (int i = path.size() 1; i >= 0; --i) {
      final File pathElement = (Filepath.elementAt(i);

      // Search for a particular File 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.
      TreeItem item = null;
      for (int k = 0; k < items.length; ++k) {
        item = items[k];
        if (item.isDisposed())
          continue;
        final File itemFile = (Fileitem.getData(TREEITEMDATA_FILE);
        if (itemFile != null && itemFile.equals(pathElement))
          break;
      }
      if (item == null)
        break;
      lastItem = item;
      if (i != && !item.getExpanded()) {
        treeExpandItem(item);
        item.setExpanded(true);
      }
      items = item.getItems();
    }
    tree.setSelection((lastItem != nullnew TreeItem[] { lastItem }
        new TreeItem[0]);
  }

  /**
   * 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(File[] files) {
    /*
     * 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();
      }
      diskSpaceLabel.setText(getResourceString("details.FileSize.text",
          new Object[] { new Long(fileSize) }));
    else {
      // No files selected
      diskSpaceLabel.setText("");
      if (currentDirectory != null) {
        int numObjects = getDirectoryList(currentDirectory).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
   */
  void notifyRefreshFiles(File[] files) {
    if (files != null && files.length == 0)
      return;

    if ((deferredRefreshRequested&& (deferredRefreshFiles != null)
        && (files != null)) {
      // merge requests
      File[] newRequest = new File[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() {
    if (isDragging || isDropping || !deferredRefreshRequested)
      return;
    if (progressDialog != null) {
      progressDialog.close();
      progressDialog = null;
    }

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

    shell.setCursor(iconCache.stockCursors[iconCache.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 File file = files[i];
        if (file.equals(currentDirectory)) {
          refreshTable = true;
          break;
        }
        File parentFile = file.getParentFile();
        if ((parentFile != null)
            && (parentFile.equals(currentDirectory))) {
          refreshTable = true;
          break;
        }
      }
    else
      refreshTable = true;
    if (refreshTable)
      workerUpdate(currentDirectory, true);

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

    if (files == null) {
      boolean refreshCombo = false;
      final File[] comboRoots = (File[]) 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 File file = roots[i];
          combo.add(file.getPath());
        }
      }
    }

    /*
     * Tree view: Refreshes information about any files in the list and
     * their children.
     */
    treeRefresh(roots);

    // Remind everyone where we are in the filesystem
    final File dir = currentDirectory;
    currentDirectory = null;
    notifySelectedDirectory(dir);

    shell.setCursor(iconCache.stockCursors[iconCache.cursorDefault]);
  }

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

    if (file.isDirectory()) {
      notifySelectedDirectory(file);
    else {
      final String fileName = file.getAbsolutePath();
      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 (currentDirectory == null)
      return;
    File parentDirectory = currentDirectory.getParentFile();
    notifySelectedDirectory(parentDirectory);
  }

  /**
   * Performs a refresh
   */
  void doRefresh() {
    notifyRefreshFiles(null);
  }

  /**
   * 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 targetFile
   *            the File representing the drop target location under
   *            inspection, or null if none
   */
  private boolean dropTargetValidate(DropTargetEvent event, File targetFile) {
    if (targetFile != null && targetFile.isDirectory()) {
      if (event.detail != DND.DROP_COPY && event.detail != DND.DROP_MOVE) {
        event.detail = 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 File representing the drop target location under
   *            inspection, or null if none
   */
  private void dropTargetHandleDrop(DropTargetEvent event, File targetFile) {
    // 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;

    // Open progress dialog
    progressDialog = new ProgressDialog(shell,
        (event.detail == DND.DROP_MOVE? ProgressDialog.MOVE
            : ProgressDialog.COPY);
    progressDialog.setTotalWorkUnits(sourceNames.length);
    progressDialog.open();

    // Copy each file
    Vector /* of File */processedFiles = new Vector();
    for (int i = 0(i < sourceNames.length)
        && (!progressDialog.isCancelled()); i++) {
      final File source = new File(sourceNames[i]);
      final File dest = new File(targetFile, source.getName());
      if (source.equals(dest))
        continue// ignore if in same location

      progressDialog.setDetailFile(source, ProgressDialog.COPY);
      while (!progressDialog.isCancelled()) {
        if (copyFileStructure(source, dest)) {
          processedFiles.add(source);
          break;
        else if (!progressDialog.isCancelled()) {
          if (event.detail == DND.DROP_MOVE && (!isDragging)) {
            // It is not possible to notify an external drag source
            // that a drop
            // operation was only partially successful. This is
            // particularly a
            // problem for DROP_MOVE operations since unless the
            // source gets
            // DROP_NONE, it will delete the original data including
            // bits that
            // may not have been transferred successfully.
            MessageBox box = new MessageBox(shell, SWT.ICON_ERROR
                | SWT.RETRY | SWT.CANCEL);
            box
                .setText(getResourceString("dialog.FailedCopy.title"));
            box.setMessage(getResourceString(
                "dialog.FailedCopy.description"new Object[] {
                    source, dest }));
            int button = box.open();
            if (button == SWT.CANCEL) {
              i = sourceNames.length;
              event.detail = DND.DROP_NONE;
              break;
            }
          else {
            // We can recover gracefully from errors if the drag
            // source belongs
            // to this application since it will look at
            // processedDropFiles.
            MessageBox box = new MessageBox(shell, SWT.ICON_ERROR
                | SWT.ABORT | SWT.RETRY | SWT.IGNORE);
            box
                .setText(getResourceString("dialog.FailedCopy.title"));
            box.setMessage(getResourceString(
                "dialog.FailedCopy.description"new Object[] {
                    source, dest }));
            int button = box.open();
            if (button == SWT.ABORT)
              i = sourceNames.length;
            if (button != SWT.RETRY)
              break;
          }
        }
        progressDialog.addProgress(1);
      }
    }
    if (isDragging) {
      // Remember exactly which files we processed
      processedDropFiles = ((File[]) processedFiles
          .toArray(new File[processedFiles.size()]));
    else {
      progressDialog.close();
      progressDialog = null;
    }
    notifyRefreshFiles(new File[] { targetFile });
  }

  /**
   * 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)
   */
  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
    final File[] sourceFiles;
    if (processedDropFiles != null) {
      sourceFiles = processedDropFiles;
    else {
      sourceFiles = new File[sourceNames.length];
      for (int i = 0; i < sourceNames.length; ++i)
        sourceFiles[inew File(sourceNames[i]);
    }
    if (progressDialog == null)
      progressDialog = new ProgressDialog(shell, ProgressDialog.MOVE);
    progressDialog.setTotalWorkUnits(sourceFiles.length);
    progressDialog.setProgress(0);
    progressDialog.open();

    // Delete each file
    for (int i = 0(i < sourceFiles.length)
        && (!progressDialog.isCancelled()); i++) {
      final File source = sourceFiles[i];
      progressDialog.setDetailFile(source, ProgressDialog.DELETE);
      while (!progressDialog.isCancelled()) {
        if (deleteFileStructure(source)) {
          break;
        else if (!progressDialog.isCancelled()) {
          MessageBox box = new MessageBox(shell, SWT.ICON_ERROR
              | SWT.ABORT | SWT.RETRY | SWT.IGNORE);
          box.setText(getResourceString("dialog.FailedDelete.title"));
          box.setMessage(getResourceString(
              "dialog.FailedDelete.description",
              new Object[] { source }));
          int button = box.open();
          if (button == SWT.ABORT)
            i = sourceNames.length;
          if (button == SWT.RETRY)
            break;
        }
      }
      progressDialog.addProgress(1);
    }
    notifyRefreshFiles(sourceFiles);
    progressDialog.close();
    progressDialog = null;
  }

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

  /**
   * 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
   */
  static File[] getDirectoryList(File file) {
    File[] list = file.listFiles();
    if (list == null)
      return new File[0];
    sortFiles(list);
    return list;
  }

  /**
   * Copies a file or entire directory structure.
   
   @param oldFile
   *            the location of the old file or directory
   @param newFile
   *            the location of the new file or directory
   @return true iff the operation succeeds without errors
   */
  boolean copyFileStructure(File oldFile, File newFile) {
    if (oldFile == null || newFile == null)
      return false;

    // ensure that newFile is not a child of oldFile or a dupe
    File searchFile = newFile;
    do {
      if (oldFile.equals(searchFile))
        return false;
      searchFile = searchFile.getParentFile();
    while (searchFile != null);

    if (oldFile.isDirectory()) {
      /*
       * Copy a directory
       */
      if (progressDialog != null) {
        progressDialog.setDetailFile(oldFile, ProgressDialog.COPY);
      }
      if (simulateOnly) {
        // System.out.println(getResourceString("simulate.DirectoriesCreated.text",
        // new Object[] { newFile.getPath() }));
      else {
        if (!newFile.mkdirs())
          return false;
      }
      File[] subFiles = oldFile.listFiles();
      if (subFiles != null) {
        if (progressDialog != null) {
          progressDialog.addWorkUnits(subFiles.length);
        }
        for (int i = 0; i < subFiles.length; i++) {
          File oldSubFile = subFiles[i];
          File newSubFile = new File(newFile, oldSubFile.getName());
          if (!copyFileStructure(oldSubFile, newSubFile))
            return false;
          if (progressDialog != null) {
            progressDialog.addProgress(1);
            if (progressDialog.isCancelled())
              return false;
          }
        }
      }
    else {
      /*
       * Copy a file
       */
      if (simulateOnly) {
        // System.out.println(getResourceString("simulate.CopyFromTo.text",
        // new Object[] { oldFile.getPath(), newFile.getPath() }));
      else {
        FileReader in = null;
        FileWriter out = null;
        try {
          in = new FileReader(oldFile);
          out = new FileWriter(newFile);

          int count;
          while ((count = in.read()) != -1)
            out.write(count);
        catch (FileNotFoundException e) {
          return false;
        catch (IOException e) {
          return false;
        finally {
          try {
            if (in != null)
              in.close();
            if (out != null)
              out.close();
          catch (IOException e) {
            return false;
          }
        }
      }
    }
    return true;
  }

  /**
   * Deletes a file or entire directory structure.
   
   @param oldFile
   *            the location of the old file or directory
   @return true iff the operation succeeds without errors
   */
  boolean deleteFileStructure(File oldFile) {
    if (oldFile == null)
      return false;
    if (oldFile.isDirectory()) {
      /*
       * Delete a directory
       */
      if (progressDialog != null) {
        progressDialog.setDetailFile(oldFile, ProgressDialog.DELETE);
      }
      File[] subFiles = oldFile.listFiles();
      if (subFiles != null) {
        if (progressDialog != null) {
          progressDialog.addWorkUnits(subFiles.length);
        }
        for (int i = 0; i < subFiles.length; i++) {
          File oldSubFile = subFiles[i];
          if (!deleteFileStructure(oldSubFile))
            return false;
          if (progressDialog != null) {
            progressDialog.addProgress(1);
            if (progressDialog.isCancelled())
              return false;
          }
        }
      }
    }
    if (simulateOnly) {
      // System.out.println(getResourceString("simulate.Delete.text",
      // new Object[] { oldFile.getPath(), oldFile.getPath() }));
      return true;
    else {
      return oldFile.delete();
    }
  }

  /**
   * Sorts files lexicographically by name.
   
   @param files
   *            the array of Files to be sorted
   */
  static void sortFiles(File[] files) {
    /* Very lazy merge sort algorithm */
    sortBlock(files, 0, files.length - 1new File[files.length]);
  }

  private static void sortBlock(File[] files, int start, int end,
      File[] mergeTemp) {
    final int length = end - start + 1;
    if (length < 8) {
      for (int i = end; i > start; --i) {
        for (int j = end; j > start; --j) {
          if (compareFiles(files[j - 1], files[j]) 0) {
            final File temp = files[j];
            files[j= files[j - 1];
            files[j - 1= temp;
          }
        }
      }
      return;
    }
    final int mid = (start + end2;
    sortBlock(files, start, mid, mergeTemp);
    sortBlock(files, mid + 1, end, mergeTemp);
    int x = start;
    int y = mid + 1;
    for (int i = 0; i < length; ++i) {
      if ((x > mid)
          || ((y <= end&& compareFiles(files[x], files[y]) 0)) {
        mergeTemp[i= files[y++];
      else {
        mergeTemp[i= files[x++];
      }
    }
    for (int i = 0; i < length; ++i)
      files[i + start= mergeTemp[i];
  }

  private static int compareFiles(File a, File b) {
    // boolean aIsDir = a.isDirectory();
    // boolean bIsDir = b.isDirectory();
    // if (aIsDir && ! bIsDir) return -1;
    // if (bIsDir && ! aIsDir) return 1;

    // sort case-sensitive files in a case-insensitive manner
    int compare = a.getName().compareToIgnoreCase(b.getName());
    if (compare == 0)
      compare = a.getName().compareTo(b.getName());
    return compare;
  }

  /*
   * 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 force
   *            if true causes a refresh even if the data is the same
   */
  void workerUpdate(File dir, boolean force) {
    if (dir == null)
      return;
    if ((!force&& (workerNextDir != null&& (workerNextDir.equals(dir)))
      return;

    synchronized (workerLock) {
      workerNextDir = dir;
      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;
        }
        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() {
    File[] dirList;
    // Clear existing information
    display.syncExec(new Runnable() {
      public void run() {
        tableContentsOfLabel.setText(SWTFileViewerDemo.getResourceString(
            "details.ContentsOf.text",
            new Object[] { workerStateDir.getPath() }));
        table.removeAll();
        table.setData(TABLEDATA_DIR, workerStateDir);
      }
    });
    dirList = getDirectoryList(workerStateDir);

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

  }

  /**
   * Adds a file's detail information to the directory list
   */
  private void workerAddFileDetails(final File file) {
    final String nameString = file.getName();
    final String dateString = dateFormat.format(new Date(file
        .lastModified()));
    final String sizeString;
    final String typeString;
    final Image iconImage;

    if (file.isDirectory()) {
      typeString = getResourceString("filetype.Folder");
      sizeString = "";
      iconImage = iconCache.stockImages[iconCache.iconClosedFolder];
    else {
      sizeString = getResourceString("filesize.KB",
          new Object[] { new Long((file.length() 5121024) });

      int dot = nameString.lastIndexOf('.');
      if (dot != -1) {
        String extension = nameString.substring(dot);
        Program program = Program.findProgram(extension);
        if (program != null) {
          typeString = program.getName();
          iconImage = iconCache.getIconFromProgram(program);
        else {
          typeString = getResourceString("filetype.Unknown",
              new Object[] { extension.toUpperCase() });
          iconImage = iconCache.stockImages[iconCache.iconFile];
        }
      else {
        typeString = getResourceString("filetype.None");
        iconImage = iconCache.stockImages[iconCache.iconFile];
      }
    }
    final String[] strings = new String[] { nameString, sizeString,
        typeString, dateString };

    display.syncExec(new Runnable() {
      public void run() {
        // 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);
      }
    });
  }

  /**
   * Instances of this class manage a progress dialog for file operations.
   */
  class ProgressDialog {
    public final static int COPY = 0;

    public final static int DELETE 1;

    public final static int MOVE = 2;

    Shell shell;

    Label messageLabel, detailLabel;

    ProgressBar progressBar;

    Button cancelButton;

    boolean isCancelled = false;

    final String operationKeyName[] "Copy""Delete""Move" };

    /**
     * Creates a progress dialog but does not open it immediately.
     
     @param parent
     *            the parent Shell
     @param style
     *            one of COPY, MOVE
     */
    public ProgressDialog(Shell parent, int style) {
      shell = new Shell(parent, SWT.BORDER | SWT.TITLE
          | SWT.APPLICATION_MODAL);
      GridLayout gridLayout = new GridLayout();
      shell.setLayout(gridLayout);
      shell.setText(getResourceString("progressDialog."
          + operationKeyName[style".title"));
      shell.addShellListener(new ShellAdapter() {
        public void shellClosed(ShellEvent e) {
          isCancelled = true;
        }
      });

      messageLabel = new Label(shell, SWT.HORIZONTAL);
      messageLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL
          | GridData.VERTICAL_ALIGN_FILL));
      messageLabel.setText(getResourceString("progressDialog."
          + operationKeyName[style".description"));

      progressBar = new ProgressBar(shell, SWT.HORIZONTAL | SWT.WRAP);
      progressBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL
          | GridData.VERTICAL_ALIGN_FILL));
      progressBar.setMinimum(0);
      progressBar.setMaximum(0);

      detailLabel = new Label(shell, SWT.HORIZONTAL);
      GridData gridData = new GridData(GridData.FILL_HORIZONTAL
          | GridData.VERTICAL_ALIGN_BEGINNING);
      gridData.widthHint = 400;
      detailLabel.setLayoutData(gridData);

      cancelButton = new Button(shell, SWT.PUSH);
      cancelButton.setLayoutData(new GridData(
          GridData.HORIZONTAL_ALIGN_END
              | GridData.VERTICAL_ALIGN_FILL));
      cancelButton
          .setText(getResourceString("progressDialog.cancelButton.text"));
      cancelButton.addSelectionListener(new SelectionAdapter() {
        public void widgetSelected(SelectionEvent e) {
          isCancelled = true;
          cancelButton.setEnabled(false);
        }
      });
    }

    /**
     * Sets the detail text to show the filename along with a string
     * representing the operation being performed on that file.
     
     @param file
     *            the file to be detailed
     @param operation
     *            one of COPY, DELETE
     */
    public void setDetailFile(File file, int operation) {
      detailLabel.setText(getResourceString("progressDialog."
          + operationKeyName[operation".operation",
          new Object[] { file }));
    }

    /**
     * Returns true if the Cancel button was been clicked.
     
     @return true if the Cancel button was clicked.
     */
    public boolean isCancelled() {
      return isCancelled;
    }

    /**
     * Sets the total number of work units to be performed.
     
     @param work
     *            the total number of work units
     */
    public void setTotalWorkUnits(int work) {
      progressBar.setMaximum(work);
    }

    /**
     * Adds to the total number of work units to be performed.
     
     @param work
     *            the number of work units to add
     */
    public void addWorkUnits(int work) {
      setTotalWorkUnits(progressBar.getMaximum() + work);
    }

    /**
     * Sets the progress of completion of the total work units.
     
     @param work
     *            the total number of work units completed
     */
    public void setProgress(int work) {
      progressBar.setSelection(work);
      while (display.readAndDispatch()) {
      // enable event processing
    }

    /**
     * Adds to the progress of completion of the total work units.
     
     @param work
     *            the number of work units completed to add
     */
    public void addProgress(int work) {
      setProgress(progressBar.getSelection() + work);
    }

    /**
     * Opens the dialog.
     */
    public void open() {
      shell.pack();
      final Shell parentShell = (Shellshell.getParent();
      Rectangle rect = parentShell.getBounds();
      Rectangle bounds = shell.getBounds();
      bounds.x = rect.x + (rect.width - bounds.width2;
      bounds.y = rect.y + (rect.height - bounds.height2;
      shell.setBounds(bounds);
      shell.open();
    }

    /**
     * Closes the dialog and disposes its resources.
     */
    public void close() {
      shell.close();
      shell.dispose();
      shell = null;
      messageLabel = null;
      detailLabel = null;
      progressBar = null;
      cancelButton = null;
    }
  }
}

/*******************************************************************************
 * Copyright (c) 2000, 2004 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
 ******************************************************************************/

/**
 * Manages icons for the application. This is necessary as we could easily end
 * up creating thousands of icons bearing the same image.
 */
class IconCache {
  // Stock images
  public final int shellIcon = 0, iconClosedDrive = 1, iconClosedFolder = 2,
      iconFile = 3, iconOpenDrive = 4, iconOpenFolder = 5, cmdCopy = 6,
      cmdCut = 7, cmdDelete = 8, cmdParent = 9, cmdPaste = 10,
      cmdPrint = 11, cmdRefresh = 12, cmdRename = 13, cmdSearch = 14;

  public final String[] stockImageLocations = "generic_example.gif",
      "icon_ClosedDrive.gif""icon_ClosedFolder.gif""icon_File.gif",
      "icon_OpenDrive.gif""icon_OpenFolder.gif""cmd_Copy.gif",
      "cmd_Cut.gif""cmd_Delete.gif""cmd_Parent.gif""cmd_Paste.gif",
      "cmd_Print.gif""cmd_Refresh.gif""cmd_Rename.gif",
      "cmd_Search.gif" };

  public Image stockImages[];

  // Stock cursors
  public final int cursorDefault = 0, cursorWait = 1;

  public Cursor stockCursors[];

  // Cached icons
  private Hashtable iconCache; /* map Program to Image */

  public IconCache() {
  }

  /**
   * Loads the resources
   
   @param display
   *            the display
   */
  public void initResources(Display display) {
    if (stockImages == null) {
      stockImages = new Image[stockImageLocations.length];

      for (int i = 0; i < stockImageLocations.length; ++i) {
        Image image = createStockImage(display, stockImageLocations[i]);
        if (image == null) {
          freeResources();
          throw new IllegalStateException(SWTFileViewerDemo
              .getResourceString("error.CouldNotLoadResources"));
        }
        stockImages[i= image;
      }
    }
    if (stockCursors == null) {
      stockCursors = new Cursor[] { null,
          new Cursor(display, SWT.CURSOR_WAIT) };
    }
    iconCache = new Hashtable();
  }

  /**
   * Frees the resources
   */
  public void freeResources() {
    if (stockImages != null) {
      for (int i = 0; i < stockImages.length; ++i) {
        final Image image = stockImages[i];
        if (image != null)
          image.dispose();
      }
      stockImages = null;
    }
    if (iconCache != null) {
      for (Enumeration it = iconCache.elements(); it.hasMoreElements();) {
        Image image = (Imageit.nextElement();
        image.dispose();
      }
    }
    if (stockCursors != null) {
      for (int i = 0; i < stockCursors.length; ++i) {
        final Cursor cursor = stockCursors[i];
        if (cursor != null)
          cursor.dispose();
      }
      stockCursors = null;
    }
  }

  /**
   * Creates a stock image
   
   @param display
   *            the display
   @param path
   *            the relative path to the icon
   */
  private Image createStockImage(Display display, String path) {
    InputStream stream = IconCache.class.getResourceAsStream(path);
    ImageData imageData = new ImageData(stream);
    ImageData mask = imageData.getTransparencyMask();
    Image result = new Image(display, imageData, mask);
    try {
      stream.close();
    catch (IOException e) {
      e.printStackTrace();
    }
    return result;
  }

  /**
   * Gets an image for a file associated with a given program
   
   @param program
   *            the Program
   */
  public Image getIconFromProgram(Program program) {
    Image image = (ImageiconCache.get(program);
    if (image == null) {
      ImageData imageData = program.getImageData();
      if (imageData != null) {
        image = new Image(null, imageData, imageData
            .getTransparencyMask());
        iconCache.put(program, image);
      else {
        image = stockImages[iconFile];
      }
    }
    return image;
  }
}


           
       
Related examples in the same category
1. SWT文件浏览器
www.java2java.com | Contact Us
Copyright 2010 - 2030 Java Source and Support. All rights reserved.
All other trademarks are property of their respective owners.