Mega Code Archive

 
Categories / Java / SWT Jface Eclipse
 

File Viewer example in 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[] { 150, 60, 75, 150 };   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, 5 });     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 >= 0 && selection < roots.length) {           notifySelectedDirectory(roots[selection]);         }       }       public void widgetDefaultSelected(SelectionEvent e) {         final String lastText = (String) combo             .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 = (File) item.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 = (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);       }     });     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 = (File) dndSelection[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 = (File) item.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 = (File) item.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 = (File) dirItem.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 = (File) item.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] = (File) 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_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 = (File) dndSelection[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 = (File) table.getData(TABLEDATA_DIR);           }         } else {           // We are over a particular item in the table, use the           // item's file           targetFile = (File) item.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 = (File) path.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 = (File) item.getData(TREEITEMDATA_FILE);         if (itemFile != null && itemFile.equals(pathElement))           break;       }       if (item == null)         break;       lastItem = item;       if (i != 0 && !item.getExpanded()) {         treeExpandItem(item);         item.setExpanded(true);       }       items = item.getItems();     }     tree.setSelection((lastItem != null) ? new 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[i] = new 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 - 1, new 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 + end) / 2;     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() + 512) / 1024) });       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 = (Shell) shell.getParent();       Rectangle rect = parentShell.getBounds();       Rectangle bounds = shell.getBounds();       bounds.x = rect.x + (rect.width - bounds.width) / 2;       bounds.y = rect.y + (rect.height - bounds.height) / 2;       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 = (Image) it.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 = (Image) iconCache.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;   } }