package org.eclipse.swt.examples.fileviewer; /* * Copyright (c) 2000, 2002 IBM Corp. All rights reserved. * This file is made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html */ import org.eclipse.swt.*; import org.eclipse.swt.custom.*; import org.eclipse.swt.dnd.*; import org.eclipse.swt.events.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.layout.*; import org.eclipse.swt.program.*; import org.eclipse.swt.widgets.*; import java.io.*; import java.text.*; import java.util.*; /** * File Viewer example */ public class FileViewer { private static ResourceBundle resourceBundle = ResourceBundle.getBundle("examples_fileviewer"); private final static String DRIVE_A = "a:" + File.separator; private final static String DRIVE_B = "b:" + File.separator; //X private final static Class GNU_CALENDAR=gnu.java.locale.Calendar.class; private final static Class GNU_LOCALE=gnu.java.locale.LocaleInformation.class; //X /* 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 [] { FileViewer.getResourceString("table.Name.title"), FileViewer.getResourceString("table.Size.title"), FileViewer.getResourceString("table.Type.title"), FileViewer.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 (); FileViewer application = new FileViewer(); 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) { try { return resourceBundle.getString(key); } catch (MissingResourceException e) { return key; } catch (NullPointerException e) { return "!" + key + "!"; } } /** * Returns a string from the resource bundle and binds it * with the given arguments. If the key is not found, * return the key. */ 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(FileViewer.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) ? FileViewer.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. *
* Used in dragOver() and dropAccept().
* Note event.detail is set to DND.DROP_NONE by this method if the target is not valid.
*
* Used in drop().
* Note event.detail is modified by this method.
*
* Used in dragFinished().
*
* Implementation notes: *