Java tutorial
/* * Copyright (c) 2012 Diamond Light Source Ltd. * * 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 */ package uk.ac.diamond.sda.navigator.views; import static java.nio.file.LinkOption.NOFOLLOW_LINKS; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import org.dawb.common.ui.menu.CheckableActionGroup; import org.dawb.common.ui.util.EclipseUtils; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.dawnsci.plotting.api.image.IFileIconService; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.fieldassist.ContentProposalAdapter; import org.eclipse.jface.fieldassist.IContentProposal; import org.eclipse.jface.fieldassist.IContentProposalListener; import org.eclipse.jface.fieldassist.TextContentAdapter; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferenceDialog; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.TreeViewerColumn; import org.eclipse.richbeans.widgets.content.FileContentProposalProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DragSource; import org.eclipse.swt.dnd.DragSourceAdapter; import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.dnd.FileTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.ui.IMemento; import org.eclipse.ui.IViewSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.dialogs.PreferencesUtil; import org.eclipse.ui.part.ViewPart; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.diamond.scisoft.analysis.utils.OSUtils; import uk.ac.diamond.sda.intro.navigator.NavigatorRCPActivator; import uk.ac.diamond.sda.navigator.preference.FileNavigatorPreferenceConstants; import uk.ac.diamond.sda.navigator.util.NIOUtils; import uk.ac.diamond.sda.navigator.views.FileContentProvider.FileSortType; /** * This class navigates a file system and remembers where you last left it. * * It is lazy in loading the file tree. * */ public final class FileView extends ViewPart implements IFileView { public static final String ID = "uk.ac.diamond.sda.navigator.views.FileView"; private static final Logger logger = LoggerFactory.getLogger(FileView.class); private TreeViewer tree; private Path savedSelection; private Text filePath; public FileView() { super(); } @Override public void init(IViewSite site, IMemento memento) throws PartInitException { super.init(site, memento); String path = null; if (memento != null) path = memento.getString("DIR"); if (path == null) path = System.getProperty("uk.ac.diamond.sda.navigator.default.file.view.location"); if (path == null) path = System.getProperty("user.home"); if (path != null) { savedSelection = Paths.get(path); } } @Override public void saveState(IMemento memento) { if (memento == null) return; if (getSelectedPath() != null) { final String path = getSelectedPath().toAbsolutePath().toString(); memento.putString("DIR", path); } } /** * Get the file path selected * * @return String */ @Override public Path getSelectedPath() { Path sel = (Path) ((IStructuredSelection) tree.getSelection()).getFirstElement(); if (sel == null) sel = savedSelection; return sel; } /** * Get the file paths selected * * @return String[] */ public String[] getSelectedPaths() { Object[] objects = ((IStructuredSelection) tree.getSelection()).toArray(); if (tree.getSelection() == null || tree.getSelection().isEmpty()) objects = new Object[] { savedSelection }; String absolutePaths[] = new String[objects.length]; for (int i = 0; i < objects.length; i++) { absolutePaths[i] = ((Path) (objects[i])).toAbsolutePath().toString(); } return absolutePaths; } private boolean updatingTextFromTreeSelections = true; @Override public void createPartControl(final Composite parent) { parent.setLayout(new GridLayout(1, false)); final Composite top = new Composite(parent, SWT.NONE); top.setLayout(new GridLayout(2, false)); top.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); final Label fileLabel = new Label(top, SWT.NONE); fileLabel.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false)); try { IFileIconService service = FileIconServiceHolder.getFileIconService(); final Image icon = service.getIconForFile(OSUtils.isWindowsOS() ? "C:/Windows/" : "/"); fileLabel.setImage(icon); } catch (Exception e) { logger.error("Cannot get icon for system root!", e); } this.filePath = new Text(top, SWT.BORDER); if (savedSelection != null) filePath.setText(savedSelection.toAbsolutePath().toString()); filePath.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); final FileContentProposalProvider prov = new FileContentProposalProvider(); final TextContentAdapter adapter = new TextContentAdapter(); final ContentProposalAdapter ad = new ContentProposalAdapter(filePath, adapter, prov, null, null); ad.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE); ad.addContentProposalListener(new IContentProposalListener() { @Override public void proposalAccepted(IContentProposal proposal) { final String path = proposal.getContent(); try { updatingTextFromTreeSelections = false; setSelectedFile(path); } finally { updatingTextFromTreeSelections = true; } } }); filePath.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.character == '\t') { if (ad.isProposalPopupOpen()) { if (prov.getFirstPath() != null) { final String path = prov.getFirstPath(); filePath.getDisplay().asyncExec(new Runnable() { @Override public void run() { try { updatingTextFromTreeSelections = false; filePath.setFocus(); filePath.setText(path); setSelectedFile(path); filePath.setFocus(); filePath.setSelection(path.length(), path.length()); } finally { updatingTextFromTreeSelections = true; } } }); } } } else if (e.character == '\t' || e.character == '\n' || e.character == '\r') { final String path = filePath.getText(); try { updatingTextFromTreeSelections = false; setSelectedFile(path); } finally { updatingTextFromTreeSelections = true; } } } }); tree = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER | SWT.VIRTUAL); tree.getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); tree.getTree().setHeaderVisible(true); tree.setUseHashlookup(true); tree.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { if (!updatingTextFromTreeSelections) return; final Path file = getSelectedPath(); if (file != null && Files.isDirectory(file)) { try { ad.setEnabled(false); filePath.setText(file.toAbsolutePath().toString()); filePath.setSelection(filePath.getText().length()); } finally { ad.setEnabled(true); } } } }); final String[] titles = { "Name", "Date", "Type", "Size", "Comment", "Scan Command" }; IPreferenceStore store = NavigatorRCPActivator.getDefault().getPreferenceStore(); boolean showDate = store.getBoolean(FileNavigatorPreferenceConstants.SHOW_DATE_COLUMN); boolean showType = store.getBoolean(FileNavigatorPreferenceConstants.SHOW_TYPE_COLUMN); boolean showSize = store.getBoolean(FileNavigatorPreferenceConstants.SHOW_SIZE_COLUMN); boolean showComment = store.getBoolean(FileNavigatorPreferenceConstants.SHOW_COMMENT_COLUMN); boolean showScanCmd = store.getBoolean(FileNavigatorPreferenceConstants.SHOW_SCANCMD_COLUMN); // we listen to the preference store property changes store.addPropertyChangeListener(new IPropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent event) { if (event.getProperty().equals(FileNavigatorPreferenceConstants.SHOW_DATE_COLUMN)) { setColumnVisible(1, 120, (Boolean) event.getNewValue()); } else if (event.getProperty().equals(FileNavigatorPreferenceConstants.SHOW_TYPE_COLUMN)) { setColumnVisible(2, 80, (Boolean) event.getNewValue()); } else if (event.getProperty().equals(FileNavigatorPreferenceConstants.SHOW_SIZE_COLUMN)) { setColumnVisible(3, 150, (Boolean) event.getNewValue()); } else if (event.getProperty().equals(FileNavigatorPreferenceConstants.SHOW_COMMENT_COLUMN)) { setColumnVisible(4, 250, (Boolean) event.getNewValue()); } else if (event.getProperty().equals(FileNavigatorPreferenceConstants.SHOW_SCANCMD_COLUMN)) { setColumnVisible(5, 300, (Boolean) event.getNewValue()); } } }); int dateWidth = showDate ? 120 : 0; int typeWidth = showType ? 80 : 0; int sizeWidth = showSize ? 150 : 0; int commentWidth = showComment ? 250 : 0; int scanCmdWidth = showScanCmd ? 300 : 0; final int[] widths = { 350, dateWidth, typeWidth, sizeWidth, commentWidth, scanCmdWidth }; TreeViewerColumn tVCol; for (int i = 0; i < titles.length; i++) { tVCol = new TreeViewerColumn(tree, SWT.NONE); TreeColumn tCol = tVCol.getColumn(); tCol.setText(titles[i]); tCol.setWidth(widths[i]); try { tVCol.setLabelProvider(new FileLabelProvider(tree, i)); } catch (Exception e1) { logger.error("Cannot create label provider " + i, e1); } } getSite().setSelectionProvider(tree); createContent(true); // Make drag source, it can then drag into projects final DragSource dragSource = new DragSource(tree.getControl(), DND.DROP_MOVE | DND.DROP_DEFAULT | DND.DROP_COPY); dragSource.setTransfer(new Transfer[] { FileTransfer.getInstance() }); dragSource.addDragListener(new DragSourceAdapter() { @Override public void dragSetData(DragSourceEvent event) { if (getSelectedPaths() == null) return; event.data = getSelectedPaths(); } }); // Add ability to open any file double clicked on (folders open in Image Monitor View) tree.getTree().addListener(SWT.MouseDoubleClick, new Listener() { @Override public void handleEvent(Event event) { openSelectedFile(); } }); tree.getTree().addKeyListener(new KeyListener() { @Override public void keyReleased(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { if (e.character == '\n' || e.character == '\r') { openSelectedFile(); } } }); createRightClickMenu(); createActions(); if (savedSelection != null) { if (savedSelection.toFile().exists()) { tree.setSelection(new StructuredSelection(savedSelection)); } else if (savedSelection.getParent() != null && savedSelection.getParent().toFile().exists()) { // If file deleted, select parent. tree.setSelection(new StructuredSelection(savedSelection.getParent())); } } Display.getDefault().asyncExec(new Runnable() { @Override public void run() { final TreePath path = new TreePath(new Object[] { NIOUtils.getRoots().get(0) }); tree.setExpandedState(path, true); } }); } @Override public void collapseAll() { this.tree.collapseAll(); } @Override public void showPreferences() { PreferenceDialog pref = PreferencesUtil.createPreferenceDialogOn(getViewSite().getShell(), "uk.ac.diamond.sda.navigator.fileNavigatorPreferencePage", null, null); if (pref != null) { pref.open(); } } private void setColumnVisible(final int col, final int width, boolean isVis) { if (this.tree == null || this.tree.getControl().isDisposed()) return; tree.getTree().getColumn(col).setWidth(isVis ? width : 0); tree.getTree().getColumn(col).setResizable(isVis); tree.getTree().getColumn(col).setMoveable(isVis); } @Override public void refresh() { final Path file = getSelectedPath(); refresh(file); } protected void refresh(Path file) { try { updatingTextFromTreeSelections = false; final Object[] elements = file == null ? this.tree.getExpandedElements() : null; if (file != null) { final FileContentProvider fileCont = (FileContentProvider) tree.getContentProvider(); fileCont.clear(file, file.getParent()); } tree.refresh(file != null ? file.getParent() : tree.getInput()); if (elements != null) this.tree.setExpandedElements(elements); } finally { updatingTextFromTreeSelections = true; } } public void refreshAll() { NIOUtils.getRoots(true); tree.refresh(); } private void createContent(boolean setItemCount) { final List<Path> roots = NIOUtils.getRoots(); if (setItemCount) tree.getTree().setItemCount(roots.size()); tree.setContentProvider(new FileContentProvider(getViewSite().getActionBars().getStatusLineManager())); if (roots.size() == 1) { tree.setInput(roots.get(0)); } else { tree.setInput("Root"); } tree.expandToLevel(1); } public void setSelectedFile(String path) { final Path file = Paths.get(path); setSelectedFile(file); } public void setSelectedFile(final Path file) { if (Files.exists(file, NOFOLLOW_LINKS)) { final Job expandJob = new Job("Update tree expanded state") { // Job needed because lazy tree - do not copy for all trees! // Required this funny way because tree is lazy @Override protected IStatus run(IProgressMonitor monitor) { for (Path f : file) { if (monitor.isCanceled()) break; int count = 0; while (!getExpandedState(f) && count < 1000) { expand(f); try { if (monitor.isCanceled()) break; Thread.sleep(20); count++; } catch (InterruptedException e) { break; } } } Display.getDefault().syncExec(new Runnable() { @Override public void run() { tree.setSelection(new StructuredSelection(file)); } }); return Status.OK_STATUS; } private void expand(final Path f) { Display.getDefault().syncExec(new Runnable() { @Override public void run() { tree.setExpandedState(f, true); } }); } private boolean getExpandedState(final Path f) { final List<Boolean> res = new ArrayList<Boolean>(1); Display.getDefault().syncExec(new Runnable() { @Override public void run() { res.add(tree.getExpandedState(f)); } }); return res.get(0); } }; expandJob.setUser(false); expandJob.setSystem(true); expandJob.setPriority(Job.INTERACTIVE); expandJob.schedule(); } } private void createRightClickMenu() { final MenuManager menuManager = new MenuManager(); tree.getControl().setMenu(menuManager.createContextMenu(tree.getControl())); getSite().registerContextMenu(menuManager, tree); } /** * Never really figured out how to made toggle buttons work properly with * contributions. Use hard coded actions * * TODO Move this to contributions */ private void createActions() { // TODO Save preference as property final IPreferenceStore store = NavigatorRCPActivator.getDefault().getPreferenceStore(); final IToolBarManager toolMan = getViewSite().getActionBars().getToolBarManager(); final Action filterCollections = new Action("Compress data collections with the same name", IAction.AS_CHECK_BOX) { @Override public void run() { FileContentProvider prov = (FileContentProvider) tree.getContentProvider(); prov.setCollapseDatacollections(isChecked()); store.setValue(FileNavigatorPreferenceConstants.SHOW_COLLAPSED_FILES, isChecked()); prov.clearAndStop(); refreshAll(); } }; filterCollections.setImageDescriptor(NavigatorRCPActivator.getImageDescriptor("icons/zip.png")); filterCollections.setChecked(store.getBoolean(FileNavigatorPreferenceConstants.SHOW_COLLAPSED_FILES)); toolMan.add(filterCollections); toolMan.add(new Separator("sorting")); final CheckableActionGroup grp = new CheckableActionGroup(); final Action dirsTop = new Action("Sort alphanumeric, directories at top.", IAction.AS_CHECK_BOX) { @Override public void run() { final Path selection = getSelectedPath(); ((FileContentProvider) tree.getContentProvider()).setSort(FileSortType.ALPHA_NUMERIC_DIRS_FIRST); tree.refresh(); if (selection != null) tree.setSelection(new StructuredSelection(selection)); } }; dirsTop.setImageDescriptor(NavigatorRCPActivator.getImageDescriptor("icons/alpha_mode_folder.png")); dirsTop.setChecked(true); grp.add(dirsTop); toolMan.add(dirsTop); final Action alpha = new Action("Alphanumeric sort for everything.", IAction.AS_CHECK_BOX) { @Override public void run() { final Path selection = getSelectedPath(); ((FileContentProvider) tree.getContentProvider()).setSort(FileSortType.ALPHA_NUMERIC); tree.refresh(); if (selection != null) tree.setSelection(new StructuredSelection(selection)); } }; alpha.setImageDescriptor(NavigatorRCPActivator.getImageDescriptor("icons/alpha_mode.gif")); grp.add(alpha); toolMan.add(alpha); toolMan.add(new Separator("uk.ac.diamond.sda.navigator.views.monitorSep")); // NO MONITORING! There are some issues with monitoring, the Images Monitor part should // be used for this. } /** * Opens the file selected in the tree. If a openFile extension is active for this perspective * this is called instead to delegate opening the file. * */ @Override public void openSelectedFile() { final Path file = getSelectedPath(); if (file == null) return; final IOpenFileAction action = getFirstPertinentAction(); if (action != null) { action.openFile(file); return; } if (Files.isDirectory(file)) { // Disabled, this causes a UI blocking thread to execute. } else { // Open file try { EclipseUtils.openExternalEditor(file.toAbsolutePath().toString()); } catch (PartInitException e) { logger.error("Cannot open file " + file, e); } } } private IOpenFileAction getFirstPertinentAction() { try { IConfigurationElement[] eles = Platform.getExtensionRegistry() .getConfigurationElementsFor("uk.ac.diamond.sda.navigator.openFile"); final String perspectiveId = EclipseUtils.getPage().getPerspective().getId(); for (IConfigurationElement e : eles) { final String perspective = e.getAttribute("perspective"); if (perspectiveId.equals(perspective) || perspective == null) { return (IOpenFileAction) e.createExecutableExtension("class"); } } return null; } catch (CoreException coreEx) { coreEx.printStackTrace(); return null; } } @Override public void dispose() { super.dispose(); // TODO Any other disposals? } @Override public void setFocus() { tree.getControl().setFocus(); } /** * The adapter IContentProvider gives the value of the H5Dataset */ @SuppressWarnings("rawtypes") @Override public Object getAdapter(final Class clazz) { return super.getAdapter(clazz); // TODO returns an adapter part for 'IPage' which is a page summary for the file or folder? } }