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;
}
}
|