From e78d5435d12c2df2100838e090ae6d045a980f8d Mon Sep 17 00:00:00 2001 From: bsinou Date: Fri, 13 Jan 2017 19:49:02 +0100 Subject: [PATCH] Introduce basic NIO Java 7 UI --- org.argeo.cms.ui.workbench/pom.xml | 5 - .../ui/workbench/jcr/NodeFsBrowserView.java | 15 +- .../cms/ui/internal/JcrContentProvider.java | 1 - org.argeo.eclipse.ui/.classpath | 1 + org.argeo.eclipse.ui/build.properties | 15 +- .../eclipse/ui/fs/AdvancedBrowserExample.java | 33 ++ .../eclipse/ui/fs/SimpleBrowserExample.java | 36 ++ .../ui/fs/SimpleTreeBrowserExample.java | 36 ++ .../eclipse/ui/fs/AdvancedFsBrowser.java | 445 ++++++++++++++++++ .../ui/fs/FileIconNameLabelProvider.java | 63 +++ .../argeo/eclipse/ui/fs/FsTableViewer.java | 101 ++++ .../org/argeo/eclipse/ui/fs/FsTreeViewer.java | 144 ++++++ .../argeo/eclipse/ui/fs/FsUiConstants.java | 11 + .../argeo/eclipse/ui/fs/FsUiException.java | 14 + .../org/argeo/eclipse/ui/fs/FsUiUtils.java | 132 ++++++ .../eclipse/ui/fs/NioFileLabelProvider.java | 45 ++ .../argeo/eclipse/ui/fs/SimpleFsBrowser.java | 197 ++++++++ .../eclipse/ui/fs/SimpleFsTreeBrowser.java | 129 +++++ .../src/org/argeo/eclipse/ui/fs/file_obj.gif | Bin 0 -> 354 bytes .../src/org/argeo/eclipse/ui/fs/fldr_obj.gif | Bin 0 -> 216 bytes 20 files changed, 1410 insertions(+), 13 deletions(-) create mode 100644 org.argeo.eclipse.ui/ext/test/org/argeo/eclipse/ui/fs/AdvancedBrowserExample.java create mode 100644 org.argeo.eclipse.ui/ext/test/org/argeo/eclipse/ui/fs/SimpleBrowserExample.java create mode 100644 org.argeo.eclipse.ui/ext/test/org/argeo/eclipse/ui/fs/SimpleTreeBrowserExample.java create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/AdvancedFsBrowser.java create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsTableViewer.java create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiConstants.java create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiException.java create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiUtils.java create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/file_obj.gif create mode 100644 org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/fldr_obj.gif diff --git a/org.argeo.cms.ui.workbench/pom.xml b/org.argeo.cms.ui.workbench/pom.xml index 119326a70..6fc4336b9 100644 --- a/org.argeo.cms.ui.workbench/pom.xml +++ b/org.argeo.cms.ui.workbench/pom.xml @@ -21,11 +21,6 @@ org.argeo.util 2.1.56-SNAPSHOT - - - - - org.argeo.commons org.argeo.enterprise diff --git a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/jcr/NodeFsBrowserView.java b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/jcr/NodeFsBrowserView.java index cfbf5208e..68f45908b 100644 --- a/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/jcr/NodeFsBrowserView.java +++ b/org.argeo.cms.ui.workbench/src/org/argeo/cms/ui/workbench/jcr/NodeFsBrowserView.java @@ -15,9 +15,14 @@ */ package org.argeo.cms.ui.workbench.jcr; +import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.spi.FileSystemProvider; import org.argeo.cms.ui.workbench.WorkbenchUiPlugin; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.fs.SimpleFsBrowser; +import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.part.ViewPart; @@ -31,10 +36,10 @@ public class NodeFsBrowserView extends ViewPart { @Override public void createPartControl(Composite parent) { -// SimpleFsBrowser browser = new SimpleFsBrowser(parent, SWT.NO_FOCUS); -// Path path = Paths.get("/"); -// browser.setInput(path); -// browser.setLayoutData(EclipseUiUtils.fillAll()); + SimpleFsBrowser browser = new SimpleFsBrowser(parent, SWT.NO_FOCUS); + Path path = Paths.get("/"); + browser.setInput(path); + browser.setLayoutData(EclipseUiUtils.fillAll()); } @Override @@ -46,6 +51,4 @@ public class NodeFsBrowserView extends ViewPart { public void setNodeFileSystemProvider(FileSystemProvider nodeFileSystemProvider) { this.nodeFileSystemProvider = nodeFileSystemProvider; } - - } diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java index 3892f94df..44885b1ca 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/internal/JcrContentProvider.java @@ -78,5 +78,4 @@ class JcrContentProvider implements ITreeContentProvider { throw new CmsException("Cannot get elements", e); } } - } diff --git a/org.argeo.eclipse.ui/.classpath b/org.argeo.eclipse.ui/.classpath index 457b11571..4e5da1da6 100644 --- a/org.argeo.eclipse.ui/.classpath +++ b/org.argeo.eclipse.ui/.classpath @@ -1,6 +1,7 @@ + browserCols = new LinkedHashMap<>(); + private Composite scrolledCmpBody; + + public Control createUi(Composite parent, Path basePath) { + if (basePath == null) + throw new IllegalArgumentException("Context cannot be null"); + parent.setLayout(new GridLayout()); + + // top filter + Composite filterCmp = new Composite(parent, SWT.NO_FOCUS); + filterCmp.setLayoutData(EclipseUiUtils.fillWidth()); + addFilterPanel(filterCmp); + + // Bottom part a sash with browser on the left + SashForm form = new SashForm(parent, SWT.HORIZONTAL); + // form.setLayout(new FillLayout()); + form.setLayoutData(EclipseUiUtils.fillAll()); + Composite leftCmp = new Composite(form, SWT.NO_FOCUS); + displayBoxCmp = new Composite(form, SWT.NONE); + form.setWeights(new int[] { 3, 1 }); + + createBrowserPart(leftCmp, basePath); + // leftCmp.addControlListener(new ControlAdapter() { + // @Override + // public void controlResized(ControlEvent e) { + // Rectangle r = leftCmp.getClientArea(); + // log.warn("Browser resized: " + r.toString()); + // scrolledCmp.setMinSize(browserCols.size() * (COLUMN_WIDTH + 2), + // SWT.DEFAULT); + // // scrolledCmp.setMinSize(scrolledCmpBody.computeSize(SWT.DEFAULT, + // // r.height)); + // } + // }); + + populateCurrEditedDisplay(displayBoxCmp, basePath); + + // INIT + setEdited(basePath); + initialPath = basePath; + // form.layout(true, true); + return parent; + } + + private void createBrowserPart(Composite parent, Path context) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // scrolled composite + scrolledCmp = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.BORDER | SWT.NO_FOCUS); + scrolledCmp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + scrolledCmp.setExpandVertical(true); + scrolledCmp.setExpandHorizontal(true); + scrolledCmp.setShowFocusedControl(true); + + scrolledCmpBody = new Composite(scrolledCmp, SWT.NO_FOCUS); + scrolledCmp.setContent(scrolledCmpBody); + scrolledCmpBody.addControlListener(new ControlAdapter() { + + @Override + public void controlResized(ControlEvent e) { + Rectangle r = scrolledCmp.getClientArea(); + scrolledCmp.setMinSize(scrolledCmpBody.computeSize(SWT.DEFAULT, r.height)); + } + }); + initExplorer(scrolledCmpBody, context); + scrolledCmpBody.layout(true, true); + scrolledCmp.layout(); + + } + + private Control initExplorer(Composite parent, Path context) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + return createBrowserColumn(parent, context); + } + + private Control createBrowserColumn(Composite parent, Path context) { + // TODO style is not correctly managed. + FilterEntitiesVirtualTable table = new FilterEntitiesVirtualTable(parent, SWT.BORDER | SWT.NO_FOCUS, context); + // CmsUtils.style(table, ArgeoOrgStyle.browserColumn.style()); + table.filterList("*"); + table.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true)); + browserCols.put(context, table); + parent.layout(true, true); + return table; + } + + public void addFilterPanel(Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, false))); + + parentPathTxt = new Text(parent, SWT.NO_FOCUS); + parentPathTxt.setEditable(false); + + filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); + filterTxt.setMessage("Filter current list"); + filterTxt.setLayoutData(EclipseUiUtils.fillWidth()); + filterTxt.addModifyListener(new ModifyListener() { + private static final long serialVersionUID = 1L; + + public void modifyText(ModifyEvent event) { + modifyFilter(false); + } + }); + filterTxt.addKeyListener(new KeyListener() { + private static final long serialVersionUID = 2533535233583035527L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0; + // boolean altPressed = (e.stateMask & SWT.ALT) != 0; + FilterEntitiesVirtualTable currTable = null; + if (currEdited != null) { + FilterEntitiesVirtualTable table = browserCols.get(currEdited); + if (table != null && !table.isDisposed()) + currTable = table; + } + + if (e.keyCode == SWT.ARROW_DOWN) + currTable.setFocus(); + else if (e.keyCode == SWT.BS) { + if (filterTxt.getText().equals("") + && !(currEdited.getNameCount() == 1 || currEdited.equals(initialPath))) { + Path oldEdited = currEdited; + Path parentPath = currEdited.getParent(); + setEdited(parentPath); + if (browserCols.containsKey(parentPath)) + browserCols.get(parentPath).setSelected(oldEdited); + filterTxt.setFocus(); + e.doit = false; + } + } else if (e.keyCode == SWT.TAB && !shiftPressed) { + Path uniqueChild = getOnlyChild(currEdited, filterTxt.getText()); + if (uniqueChild != null) { + // Highlight the unique chosen child + currTable.setSelected(uniqueChild); + setEdited(uniqueChild); + } + filterTxt.setFocus(); + e.doit = false; + } + } + }); + } + + private Path getOnlyChild(Path parent, String filter) { + try (DirectoryStream stream = Files.newDirectoryStream(currEdited, filter + "*")) { + Path uniqueChild = null; + boolean moreThanOne = false; + loop: for (Path entry : stream) { + if (uniqueChild == null) { + uniqueChild = entry; + } else { + moreThanOne = true; + break loop; + } + } + if (!moreThanOne) + return uniqueChild; + return null; + } catch (IOException ioe) { + throw new FsUiException( + "Unable to determine unique child existence and get it under " + parent + " with filter " + filter, + ioe); + } + } + + private void setEdited(Path path) { + currEdited = path; + EclipseUiUtils.clear(displayBoxCmp); + populateCurrEditedDisplay(displayBoxCmp, currEdited); + refreshFilters(path); + refreshBrowser(path); + } + + private void refreshFilters(Path path) { + parentPathTxt.setText(path.toUri().toString()); + filterTxt.setText(""); + filterTxt.getParent().layout(); + } + + private void refreshBrowser(Path currPath) { + Path currParPath = currPath.getParent(); + Object[][] colMatrix = new Object[browserCols.size()][2]; + + int i = 0, currPathIndex = -1, lastLeftOpenedIndex = -1; + for (Path path : browserCols.keySet()) { + colMatrix[i][0] = path; + colMatrix[i][1] = browserCols.get(path); + if (currPathIndex >= 0 && lastLeftOpenedIndex < 0 && currParPath != null) { + boolean leaveOpened = path.startsWith(currPath); + if (!leaveOpened) + lastLeftOpenedIndex = i; + } + if (currParPath.equals(path)) + currPathIndex = i; + i++; + } + + if (currPathIndex >= 0 && lastLeftOpenedIndex >= 0) { + // dispose and remove useless cols + for (int l = i - 1; l >= lastLeftOpenedIndex; l--) { + ((FilterEntitiesVirtualTable) colMatrix[l][1]).dispose(); + browserCols.remove(colMatrix[l][0]); + } + } + + if (browserCols.containsKey(currPath)) { + FilterEntitiesVirtualTable currCol = browserCols.get(currPath); + if (currCol.isDisposed()) { + // Does it still happen ? + log.warn(currPath + " browser column was disposed and still listed"); + browserCols.remove(currPath); + } + } + + if (!browserCols.containsKey(currPath) && Files.isDirectory(currPath)) + createBrowserColumn(scrolledCmpBody, currPath); + + scrolledCmpBody.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(browserCols.size(), false))); + scrolledCmpBody.layout(true, true); + // also resize the scrolled composite + scrolledCmp.layout(); + } + + private void modifyFilter(boolean fromOutside) { + if (!fromOutside) + if (currEdited != null) { + String filter = filterTxt.getText() + "*"; + FilterEntitiesVirtualTable table = browserCols.get(currEdited); + if (table != null && !table.isDisposed()) + table.filterList(filter); + } + } + + private Point imageWidth = new Point(250, 0); + + /** + * Recreates the content of the box that displays information about the + * current selected node. + */ + private void populateCurrEditedDisplay(Composite parent, Path context) { + parent.setLayout(new GridLayout()); + + // if (isImg(context)) { + // EditableImage image = new Img(parent, RIGHT, context, imageWidth); + // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, + // 2, 1)); + // } + + try { + Label contextL = new Label(parent, SWT.NONE); + contextL.setText(context.getFileName().toString()); + contextL.setFont(EclipseUiUtils.getBoldFont(parent)); + addProperty(parent, "Last modified", Files.getLastModifiedTime(context).toString()); + addProperty(parent, "Owner", Files.getOwner(context).getName()); + if (Files.isDirectory(context)) { + addProperty(parent, "Type", "Folder"); + } else { + String mimeType = Files.probeContentType(context); + if (EclipseUiUtils.isEmpty(mimeType)) + mimeType = "Unknown"; + addProperty(parent, "Type", mimeType); + addProperty(parent, "Size", FsUiUtils.humanReadableByteCount(Files.size(context), false)); + } + parent.layout(true, true); + } catch (IOException e) { + throw new FsUiException("Cannot display details for " + context, e); + } + } + + private void addProperty(Composite parent, String propName, String value) { + Label contextL = new Label(parent, SWT.NONE); + contextL.setText(propName + ": " + value); + } + + /** + * Almost canonical implementation of a table that displays the content of a + * directory + */ + private class FilterEntitiesVirtualTable extends Composite { + private static final long serialVersionUID = 2223410043691844875L; + + // Context + private Path context; + private Path currSelected = null; + + // UI Objects + private FsTableViewer viewer; + + @Override + public boolean setFocus() { + if (viewer.getTable().isDisposed()) + return false; + if (currSelected != null) + viewer.setSelection(new StructuredSelection(currSelected), true); + else if (viewer.getSelection().isEmpty()) { + Object first = viewer.getElementAt(0); + if (first != null) + viewer.setSelection(new StructuredSelection(first), true); + } + return viewer.getTable().setFocus(); + } + + /** + * Enable highlighting the correct element in the table when externally + * browsing (typically via the command-line-like Text field) + */ + void setSelected(Path selected) { + // to prevent change selection event to be thrown + currSelected = selected; + viewer.setSelection(new StructuredSelection(currSelected), true); + } + + void filterList(String filter) { + viewer.setInput(context, filter); + } + + public FilterEntitiesVirtualTable(Composite parent, int style, Path context) { + super(parent, SWT.NO_FOCUS); + this.context = context; + createTableViewer(this); + } + + private void createTableViewer(final Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // We must limit the size of the table otherwise the full list is + // loaded before the layout happens + // Composite listCmp = new Composite(parent, SWT.NO_FOCUS); + // GridData gd = new GridData(SWT.LEFT, SWT.FILL, false, true); + // gd.widthHint = COLUMN_WIDTH; + // listCmp.setLayoutData(gd); + // listCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + // viewer = new TableViewer(listCmp, SWT.VIRTUAL | SWT.MULTI | + // SWT.V_SCROLL); + // Table table = viewer.getTable(); + // table.setLayoutData(EclipseUiUtils.fillAll()); + + viewer = new FsTableViewer(parent, SWT.MULTI); + Table table = viewer.configureDefaultSingleColumnTable(COLUMN_WIDTH); + + viewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); + if (selection.isEmpty()) + return; + else { + Path newSelected = (Path) selection.getFirstElement(); + if (newSelected.equals(currSelected)) + return; + currSelected = newSelected; + setEdited(newSelected); + } + } + }); + + table.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -8083424284436715709L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); + Path selected = null; + if (!selection.isEmpty()) + selected = ((Path) selection.getFirstElement()); + if (e.keyCode == SWT.ARROW_RIGHT) { + if (!Files.isDirectory(selected)) + return; + if (selected != null) { + setEdited(selected); + browserCols.get(selected).setFocus(); + } + } else if (e.keyCode == SWT.ARROW_LEFT) { + if (context.equals(initialPath)) + return; + Path parent = context.getParent(); + if (parent == null) + return; + + setEdited(parent); + browserCols.get(parent).setFocus(); + } + } + }); + } + } +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java new file mode 100644 index 000000000..777d57567 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FileIconNameLabelProvider.java @@ -0,0 +1,63 @@ +package org.argeo.eclipse.ui.fs; + +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Image; + +public class FileIconNameLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = 8187902187946523148L; + + private Image folderIcon; + private Image fileIcon; + + public FileIconNameLabelProvider() { + // if (!PlatformUI.isWorkbenchRunning()) { + folderIcon = ImageDescriptor.createFromFile(getClass(), "fldr_obj.gif").createImage(); + fileIcon = ImageDescriptor.createFromFile(getClass(), "file_obj.gif").createImage(); + // } + } + + @Override + public void dispose() { + if (folderIcon != null) + folderIcon.dispose(); + if (fileIcon != null) + fileIcon.dispose(); + super.dispose(); + } + + @Override + public String getText(Object element) { + if (element instanceof Path) { + Path curr = ((Path) element); + Path name = curr.getFileName(); + if (name == null) + return "[No name]"; + else + return name.toString(); + } + return null; + } + + @Override + public Image getImage(Object element) { + if (element instanceof Path) { + Path curr = ((Path) element); + if (Files.isDirectory(curr)) + // if (folderIcon != null) + return folderIcon; + // else + // return + // PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER); + // else if (fileIcon != null) + return fileIcon; + // else + // return + // PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE); + } + return null; + } +} \ No newline at end of file diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsTableViewer.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsTableViewer.java new file mode 100644 index 000000000..7e8e94fb6 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsTableViewer.java @@ -0,0 +1,101 @@ +package org.argeo.eclipse.ui.fs; + +import java.nio.file.Path; +import java.util.List; + +import org.argeo.eclipse.ui.ColumnDefinition; +import org.eclipse.jface.viewers.ILazyContentProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; + +/** + * Canonical implementation of a JFace table viewer to display the content of a + * file folder + */ +public class FsTableViewer extends TableViewer { + private static final long serialVersionUID = -5632407542678477234L; + + private boolean showHiddenItems = false; + private boolean folderFirst = true; + private boolean reverseOrder = false; + private String orderProperty = FsUiConstants.PROPERTY_NAME; + + public FsTableViewer(Composite parent, int style) { + super(parent, style | SWT.VIRTUAL); + } + + public Table configureDefaultSingleColumnTable(int tableWidthHint) { + Table table = this.getTable(); + table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + table.setLinesVisible(true); + table.setHeaderVisible(false); +// CmsUtils.markup(table); +// CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); + + TableViewerColumn column = new TableViewerColumn(this, SWT.NONE); + TableColumn tcol = column.getColumn(); + tcol.setWidth(tableWidthHint); + column.setLabelProvider(new FileIconNameLabelProvider()); + + this.setContentProvider(new MyLazyCP()); + return table; + } + + public Table configureDefaultTable(List columns) { + this.setContentProvider(new MyLazyCP()); + Table table = this.getTable(); + table.setLinesVisible(true); + table.setHeaderVisible(true); + // CmsUtils.markup(table); + // CmsUtils.style(table, MaintenanceStyles.BROWSER_COLUMN); + for (ColumnDefinition colDef : columns) { + TableViewerColumn column = new TableViewerColumn(this, SWT.NONE); + column.setLabelProvider(colDef.getLabelProvider()); + TableColumn tcol = column.getColumn(); + tcol.setResizable(true); + tcol.setText(colDef.getLabel()); + tcol.setWidth(colDef.getMinWidth()); + } + return table; + } + + public void setInput(Path dir, String filter) { + Object[] rows = FsUiUtils.getChildren(dir, filter, showHiddenItems, folderFirst, orderProperty, reverseOrder); + this.setInput(rows); + int length = rows == null ? 0 : rows.length; + this.setItemCount(length); + this.refresh(); + } + + /** Directly displays bookmarks **/ + public void setPathsInput(Path... paths) { + this.setInput((Object[]) paths); + this.setItemCount(paths.length); + this.refresh(); + } + + private class MyLazyCP implements ILazyContentProvider { + private static final long serialVersionUID = 9096550041395433128L; + private Object[] elements; + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // IMPORTANT: don't forget this: an exception will be thrown if + // a selected object is not part of the results anymore. + viewer.setSelection(null); + this.elements = (Object[]) newInput; + } + + public void updateElement(int index) { + FsTableViewer.this.replace(elements[index], index); + } + } +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java new file mode 100644 index 000000000..f55ead718 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsTreeViewer.java @@ -0,0 +1,144 @@ +package org.argeo.eclipse.ui.fs; + +import java.io.IOException; +import java.nio.file.DirectoryIteratorException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.argeo.eclipse.ui.ColumnDefinition; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.TreeViewerColumn; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; + +/** + * Canonical implementation of a JFace TreeViewer to display the content of a + * repository + */ +public class FsTreeViewer extends TreeViewer { + private static final long serialVersionUID = -5632407542678477234L; + + private boolean showHiddenItems = false; + private boolean showDirectoryFirst = true; + private String orderingProperty = FsUiConstants.PROPERTY_NAME; + + public FsTreeViewer(Composite parent, int style) { + super(parent, style | SWT.VIRTUAL); + } + + public Tree configureDefaultSingleColumnTable(int tableWidthHint) { + Tree tree = this.getTree(); + tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + tree.setLinesVisible(true); + tree.setHeaderVisible(false); +// CmsUtils.markup(tree); + + TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE); + TreeColumn tcol = column.getColumn(); + tcol.setWidth(tableWidthHint); + column.setLabelProvider(new FileIconNameLabelProvider()); + + this.setContentProvider(new MyCP()); + return tree; + } + + public Tree configureDefaultTable(List columns) { + this.setContentProvider(new MyCP()); + Tree tree = this.getTree(); + tree.setLinesVisible(true); + tree.setHeaderVisible(true); +// CmsUtils.markup(tree); +// CmsUtils.style(tree, MaintenanceStyles.BROWSER_COLUMN); + for (ColumnDefinition colDef : columns) { + TreeViewerColumn column = new TreeViewerColumn(this, SWT.NONE); + column.setLabelProvider(colDef.getLabelProvider()); + TreeColumn tcol = column.getColumn(); + tcol.setResizable(true); + tcol.setText(colDef.getLabel()); + tcol.setWidth(colDef.getMinWidth()); + } + return tree; + } + + public void setInput(Path dir, String filter) { + try (DirectoryStream stream = Files.newDirectoryStream(dir, filter)) { + // TODO make this lazy + List paths = new ArrayList<>(); + for (Path entry : stream) { + paths.add(entry); + } + Object[] rows = paths.toArray(new Object[0]); + this.setInput(rows); + // this.setItemCount(rows.length); + this.refresh(); + } catch (IOException | DirectoryIteratorException e) { + throw new FsUiException("Unable to filter " + dir + " children with filter " + filter, e); + } + } + + /** Directly displays bookmarks **/ + public void setPathsInput(Path... paths) { + this.setInput((Object[]) paths); + // this.setItemCount(paths.length); + this.refresh(); + } + + private class MyCP implements ITreeContentProvider { + private static final long serialVersionUID = 9096550041395433128L; + private Object[] elements; + + public void dispose() { + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // IMPORTANT: don't forget this: an exception will be thrown if + // a selected object is not part of the results anymore. + viewer.setSelection(null); + this.elements = (Object[]) newInput; + } + + @Override + public Object[] getElements(Object inputElement) { + return elements; + } + + @Override + public Object[] getChildren(Object parentElement) { + Path path = (Path) parentElement; + if (!Files.isDirectory(path)) + return null; + else + return FsUiUtils.getChildren(path, "*", showHiddenItems, showDirectoryFirst, orderingProperty, false); + } + + @Override + public Object getParent(Object element) { + Path path = (Path) element; + return path.getParent(); + } + + @Override + public boolean hasChildren(Object element) { + Path path = (Path) element; + try { + if (!Files.isDirectory(path)) + return false; + else + try (DirectoryStream children = Files.newDirectoryStream(path, "*")) { + return children.iterator().hasNext(); + } + } catch (IOException e) { + throw new FsUiException("Unable to check child existence on " + path, e); + } + } + + } +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiConstants.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiConstants.java new file mode 100644 index 000000000..2b51e71a2 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiConstants.java @@ -0,0 +1,11 @@ +package org.argeo.eclipse.ui.fs; + +/** Centralize constants used by the Nio FS UI parts */ +public interface FsUiConstants { + + // TODO use standard properties + String PROPERTY_NAME = "name"; + String PROPERTY_SIZE = "size"; + String PROPERTY_LAST_MODIFIED = "last-modified"; + String PROPERTY_TYPE = "type"; +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiException.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiException.java new file mode 100644 index 000000000..422b0e1ad --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiException.java @@ -0,0 +1,14 @@ +package org.argeo.eclipse.ui.fs; + +/** Files specific exception */ +public class FsUiException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public FsUiException(String message) { + super(message); + } + + public FsUiException(String message, Throwable e) { + super(message, e); + } +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiUtils.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiUtils.java new file mode 100644 index 000000000..e0cad2944 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/FsUiUtils.java @@ -0,0 +1,132 @@ +package org.argeo.eclipse.ui.fs; + +import java.io.IOException; +import java.nio.file.DirectoryIteratorException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** Centralize additionnal utilitary methods to manage Java7 NIO files */ +public class FsUiUtils { + + /** + * thanks to + * http://programming.guide/java/formatting-byte-size-to-human-readable-format.html + */ + public static String humanReadableByteCount(long bytes, boolean si) { + int unit = si ? 1000 : 1024; + if (bytes < unit) + return bytes + " B"; + int exp = (int) (Math.log(bytes) / Math.log(unit)); + String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); + return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); + } + + public static Path[] getChildren(Path parent, String filter, boolean showHiddenItems, boolean folderFirst, + String orderProperty, boolean reverseOrder) { + if (!Files.isDirectory(parent)) + return null; + List pairs = new ArrayList<>(); + try (DirectoryStream stream = Files.newDirectoryStream(parent, filter)) { + loop: for (Path entry : stream) { + if (!showHiddenItems) + if (Files.isHidden(entry)) + continue loop; + switch (orderProperty) { + case FsUiConstants.PROPERTY_SIZE: + if (folderFirst) + pairs.add(new LPair(entry, Files.size(entry), Files.isDirectory(entry))); + else + pairs.add(new LPair(entry, Files.size(entry))); + break; + case FsUiConstants.PROPERTY_LAST_MODIFIED: + if (folderFirst) + pairs.add(new LPair(entry, Files.getLastModifiedTime(entry).toMillis(), + Files.isDirectory(entry))); + else + pairs.add(new LPair(entry, Files.getLastModifiedTime(entry).toMillis())); + break; + case FsUiConstants.PROPERTY_NAME: + if (folderFirst) + pairs.add(new SPair(entry, entry.getFileName().toString(), Files.isDirectory(entry))); + else + pairs.add(new SPair(entry, entry.getFileName().toString())); + break; + default: + throw new FsUiException("Unable to prepare sort for property " + orderProperty); + } + } + Pair[] rows = pairs.toArray(new Pair[0]); + Arrays.sort(rows); + Path[] results = new Path[rows.length]; + if (reverseOrder) { + int j = rows.length - 1; + for (int i = 0; i < rows.length; i++) + results[i] = rows[j - i].p; + } else + for (int i = 0; i < rows.length; i++) + results[i] = rows[i].p; + return results; + } catch (IOException | DirectoryIteratorException e) { + throw new FsUiException("Unable to filter " + parent + " children with filter " + filter, e); + } + } + + static abstract class Pair implements Comparable { + Path p; + Boolean i; + }; + + static class LPair extends Pair { + long v; + + public LPair(Path path, long propValue) { + p = path; + v = propValue; + } + + public LPair(Path path, long propValue, boolean isDir) { + p = path; + v = propValue; + i = isDir; + } + + public int compareTo(Object o) { + if (i != null) { + Boolean j = ((LPair) o).i; + if (i.booleanValue() != j.booleanValue()) + return i.booleanValue() ? -1 : 1; + } + long u = ((LPair) o).v; + return v < u ? -1 : v == u ? 0 : 1; + } + }; + + static class SPair extends Pair { + String v; + + public SPair(Path path, String propValue) { + p = path; + v = propValue; + } + + public SPair(Path path, String propValue, boolean isDir) { + p = path; + v = propValue; + i = isDir; + } + + public int compareTo(Object o) { + if (i != null) { + Boolean j = ((SPair) o).i; + if (i.booleanValue() != j.booleanValue()) + return i.booleanValue() ? -1 : 1; + } + String u = ((SPair) o).v; + return v.compareTo(u); + } + }; +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java new file mode 100644 index 000000000..391f250eb --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/NioFileLabelProvider.java @@ -0,0 +1,45 @@ +package org.argeo.eclipse.ui.fs; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.viewers.ColumnLabelProvider; + +/** Expect a {@link Path} as input element */ +public class NioFileLabelProvider extends ColumnLabelProvider { + private static final long serialVersionUID = 2160026425187796930L; + private final String propName; + + public NioFileLabelProvider(String propName) { + this.propName = propName; + } + + @Override + public String getText(Object element) { + Path path = (Path) element; + try { + switch (propName) { + case FsUiConstants.PROPERTY_SIZE: + return FsUiUtils.humanReadableByteCount(Files.size(path), false); + case FsUiConstants.PROPERTY_LAST_MODIFIED: + return Files.getLastModifiedTime(path).toString(); + case FsUiConstants.PROPERTY_TYPE: + if (Files.isDirectory(path)) + return "Folder"; + else { + String mimeType = Files.probeContentType(path); + if (EclipseUiUtils.isEmpty(mimeType)) + return "Unknown"; + else + return mimeType; + } + default: + throw new IllegalArgumentException("Unsupported property " + propName); + } + } catch (IOException ioe) { + throw new FsUiException("Cannot get property " + propName + " on " + path.toString()); + } + } +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java new file mode 100644 index 000000000..67bdcc974 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/SimpleFsBrowser.java @@ -0,0 +1,197 @@ +package org.argeo.eclipse.ui.fs; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; + +/** + * Experimental UI upon Java 7 nio files api: SashForm layout with bookmarks on + * the left hand side and a simple table on the right hand side. + */ +public class SimpleFsBrowser extends Composite { + private final static Log log = LogFactory.getLog(SimpleFsBrowser.class); + private static final long serialVersionUID = -40347919096946585L; + + private Path currSelected; + private FsTableViewer bookmarksViewer; + private FsTableViewer directoryDisplayViewer; + + public SimpleFsBrowser(Composite parent, int style) { + super(parent, style); + createContent(this); + // parent.layout(true, true); + } + + private void createContent(Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + SashForm form = new SashForm(parent, SWT.HORIZONTAL); + Composite leftCmp = new Composite(form, SWT.NONE); + populateBookmarks(leftCmp); + + Composite rightCmp = new Composite(form, SWT.BORDER); + populateDisplay(rightCmp); + form.setLayoutData(EclipseUiUtils.fillAll()); + form.setWeights(new int[] { 1, 3 }); + } + + public void setInput(Path... paths) { + bookmarksViewer.setPathsInput(paths); + bookmarksViewer.getTable().getParent().layout(true, true); + } + + private void populateBookmarks(final Composite parent) { + // GridLayout layout = EclipseUiUtils.noSpaceGridLayout(); + // layout.verticalSpacing = 5; + parent.setLayout(new GridLayout()); + + ISelectionChangedListener selList = new MySelectionChangedListener(); + + appendTitle(parent, "My bookmarks"); + bookmarksViewer = new FsTableViewer(parent, SWT.MULTI | SWT.NO_SCROLL); + Table table = bookmarksViewer.configureDefaultSingleColumnTable(500); + GridData gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + bookmarksViewer.addSelectionChangedListener(selList); + + appendTitle(parent, "Jcr + File"); + + FsTableViewer jcrFilesViewers = new FsTableViewer(parent, SWT.MULTI | SWT.NO_SCROLL); + table = jcrFilesViewers.configureDefaultSingleColumnTable(500); + gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + jcrFilesViewers.addSelectionChangedListener(selList); + + // FileSystemProvider fsProvider = new JackrabbitMemoryFsProvider(); + // try { + // Path testPath = fsProvider.getPath(new URI("jcr+memory:/")); + // jcrFilesViewers.setPathsInput(testPath); + // } catch (URISyntaxException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + } + + private Label appendTitle(Composite parent, String value) { + Label titleLbl = new Label(parent, SWT.NONE); + titleLbl.setText(value); + titleLbl.setFont(EclipseUiUtils.getBoldFont(parent)); + GridData gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 5; + gd.verticalIndent = 5; + titleLbl.setLayoutData(gd); + return titleLbl; + } + + private class MySelectionChangedListener implements ISelectionChangedListener { + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) bookmarksViewer.getSelection(); + if (selection.isEmpty()) + return; + else { + Path newSelected = (Path) selection.getFirstElement(); + if (newSelected.equals(currSelected)) + return; + currSelected = newSelected; + directoryDisplayViewer.setInput(currSelected, "*"); + } + } + } + + private void populateDisplay(final Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); + List colDefs = new ArrayList<>(); + colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 200)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 250)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), + "Last modified", 200)); + Table table = directoryDisplayViewer.configureDefaultTable(colDefs); + table.setLayoutData(EclipseUiUtils.fillAll()); + + table.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -8083424284436715709L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + log.debug("Key event received: " + e.keyCode); + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (!selection.isEmpty()) + selected = ((Path) selection.getFirstElement()); + if (e.keyCode == SWT.CR) { + if (!Files.isDirectory(selected)) + return; + if (selected != null) { + currSelected = selected; + directoryDisplayViewer.setInput(currSelected, "*"); + } + } else if (e.keyCode == SWT.BS) { + currSelected = currSelected.getParent(); + directoryDisplayViewer.setInput(currSelected, "*"); + directoryDisplayViewer.getTable().setFocus(); + } + } + }); + + directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() { + @Override + public void doubleClick(DoubleClickEvent event) { + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (!selection.isEmpty()) + selected = ((Path) selection.getFirstElement()); + if (selected != null) { + if (!Files.isDirectory(selected)) + return; + currSelected = selected; + directoryDisplayViewer.setInput(currSelected, "*"); + } + } + }); + + directoryDisplayViewer.addDoubleClickListener(new IDoubleClickListener() { + @Override + public void doubleClick(DoubleClickEvent event) { + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (!selection.isEmpty()) + selected = ((Path) selection.getFirstElement()); + if (selected != null) { + if (!Files.isDirectory(selected)) + return; + currSelected = selected; + directoryDisplayViewer.setInput(currSelected, "*"); + } + } + }); + } +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java new file mode 100644 index 000000000..8e921bd25 --- /dev/null +++ b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/SimpleFsTreeBrowser.java @@ -0,0 +1,129 @@ +package org.argeo.eclipse.ui.fs; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Tree; + +/** Draft for some UI upon Java 7 nio files api */ +public class SimpleFsTreeBrowser extends Composite { + private final static Log log = LogFactory.getLog(SimpleFsTreeBrowser.class); + private static final long serialVersionUID = -40347919096946585L; + + private Path currSelected; + private FsTreeViewer treeViewer; + private FsTableViewer directoryDisplayViewer; + + public SimpleFsTreeBrowser(Composite parent, int style) { + super(parent, style); + createContent(this); + // parent.layout(true, true); + } + + private void createContent(Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + SashForm form = new SashForm(parent, SWT.HORIZONTAL); + Composite child1 = new Composite(form, SWT.NONE); + populateTree(child1); + Composite child2 = new Composite(form, SWT.BORDER); + populateDisplay(child2); + form.setLayoutData(EclipseUiUtils.fillAll()); + form.setWeights(new int[] { 1, 3 }); + } + + public void setInput(Path... paths) { + treeViewer.setPathsInput(paths); + treeViewer.getControl().getParent().layout(true, true); + } + + private void populateTree(final Composite parent) { + // GridLayout layout = EclipseUiUtils.noSpaceGridLayout(); + // layout.verticalSpacing = 5; + parent.setLayout(new GridLayout()); + + ISelectionChangedListener selList = new MySelectionChangedListener(); + + treeViewer = new FsTreeViewer(parent, SWT.MULTI); + Tree tree = treeViewer.configureDefaultSingleColumnTable(500); + GridData gd = EclipseUiUtils.fillAll(); + // gd.horizontalIndent = 10; + tree.setLayoutData(gd); + treeViewer.addSelectionChangedListener(selList); + } + + private class MySelectionChangedListener implements ISelectionChangedListener { + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) treeViewer.getSelection(); + if (selection.isEmpty()) + return; + else { + Path newSelected = (Path) selection.getFirstElement(); + if (newSelected.equals(currSelected)) + return; + currSelected = newSelected; + if (Files.isDirectory(currSelected)) + directoryDisplayViewer.setInput(currSelected, "*"); + } + } + } + + private void populateDisplay(final Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); + List colDefs = new ArrayList<>(); + colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 200, 200)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100, 100)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 300, 300)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), + "Last modified", 100, 100)); + Table table = directoryDisplayViewer.configureDefaultTable(colDefs); + table.setLayoutData(EclipseUiUtils.fillAll()); + + table.addKeyListener(new KeyListener() { + private static final long serialVersionUID = -8083424284436715709L; + + @Override + public void keyReleased(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + log.debug("Key event received: " + e.keyCode); + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (!selection.isEmpty()) + selected = ((Path) selection.getFirstElement()); + if (e.keyCode == SWT.CR) { + if (!Files.isDirectory(selected)) + return; + if (selected != null) { + currSelected = selected; + directoryDisplayViewer.setInput(currSelected, "*"); + } + } else if (e.keyCode == SWT.BS) { + currSelected = currSelected.getParent(); + directoryDisplayViewer.setInput(currSelected, "*"); + directoryDisplayViewer.getTable().setFocus(); + } + } + }); + } +} diff --git a/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/file_obj.gif b/org.argeo.eclipse.ui/src/org/argeo/eclipse/ui/fs/file_obj.gif new file mode 100644 index 0000000000000000000000000000000000000000..7ccc6a70317bf439c4178f66c40005ebf9e72314 GIT binary patch literal 354 zcmV-o0iFIwNk%w1VGsZi0M!5h^Y#1q`TT>Cv4fGYhm)>@lCgr3u!EAY?(+Kb_51ny z{P_9%hMK;NpTdTeu8Wqd@bvqWrpB7A%c8T-sJPRtyw%?5_}%CC-{|+`?D^;J`R(%h z^Y;7P=l9>~_}=LE;p+J6@cH)m`;eNb=I;6M_51Sn`<9)g_4xbu`TLcfq?@6kp`@Ir zsF8yg6Lgc~kJEFpM$VhkZHMJps4l$Db! zMJgGKdkh&TMI#`iq@*K74<3wT7#|i7MGhSu7qqms1VsV`2L`;o2Lc03#6v*wj(b5!P~sjn^4pBFDXFJ7TPNb)}zFdzZN zpDc_F4AKlbATf}g46LCK)cR5~=VfGBty!EmMJ+qTijl!k<;;`cOWYDXbj&=28jDUI znIPA;;-J&oD09KqYe6rnb~B%5E3$|