From: bsinou Date: Sat, 21 Jan 2017 17:44:43 +0000 (+0100) Subject: Move Fs Ui classes to relevant package X-Git-Tag: argeo-commons-2.1.58~3 X-Git-Url: http://git.argeo.org/?a=commitdiff_plain;h=2d6bb34c3fa3f9fd4201bfb422e412a555b3dceb;p=lgpl%2Fargeo-commons.git Move Fs Ui classes to relevant package --- diff --git a/org.argeo.cms.ui/src/org/argeo/cms/fs/CmsFsBrowser.java b/org.argeo.cms.ui/src/org/argeo/cms/fs/CmsFsBrowser.java deleted file mode 100644 index dfa1afcca..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/fs/CmsFsBrowser.java +++ /dev/null @@ -1,521 +0,0 @@ -package org.argeo.cms.fs; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.DirectoryStream; -import java.nio.file.FileSystem; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.spi.FileSystemProvider; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.Node; -import javax.jcr.Property; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.nodetype.NodeType; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.auth.CurrentUser; -import org.argeo.cms.util.CmsUtils; -import org.argeo.cms.util.UserAdminUtils; -import org.argeo.eclipse.ui.ColumnDefinition; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.fs.FileIconNameLabelProvider; -import org.argeo.eclipse.ui.fs.FsTableViewer; -import org.argeo.eclipse.ui.fs.FsUiConstants; -import org.argeo.eclipse.ui.fs.FsUiUtils; -import org.argeo.eclipse.ui.fs.NioFileLabelProvider; -import org.argeo.jcr.JcrUtils; -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.jface.viewers.Viewer; -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.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.events.MouseAdapter; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.layout.RowData; -import org.eclipse.swt.layout.RowLayout; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Table; -import org.eclipse.swt.widgets.Text; - -/** - * Default CMS single page browser: a sashForm layout with bookmarks at the left - * hand side, a simple table in the middle and an overview at right hand side. - */ -public class CmsFsBrowser extends Composite { - private final static Log log = LogFactory.getLog(CmsFsBrowser.class); - private static final long serialVersionUID = -40347919096946585L; - - private final FileSystemProvider nodeFileSystemProvider; - private final Node currentBaseContext; - - // UI Parts for the browser - private Composite leftPannelCmp; - private Composite filterCmp; - private Text filterTxt; - private FsTableViewer directoryDisplayViewer; - private Composite rightPannelCmp; - - private FsContextMenu contextMenu; - - // Local context (this composite is state full) - private Path initialPath; - private Path currDisplayedFolder; - private Path currSelected; - - // local variables (to be cleaned) - private int bookmarkColWith = 500; - - public CmsFsBrowser(Composite parent, int style, Node context, FileSystemProvider fileSystemProvider) { - super(parent, style); - this.nodeFileSystemProvider = fileSystemProvider; - this.currentBaseContext = context; - - this.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - SashForm form = new SashForm(this, SWT.HORIZONTAL); - - leftPannelCmp = new Composite(form, SWT.NO_FOCUS); - // Bookmarks are still static - populateBookmarks(leftPannelCmp); - - Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS); - createDisplay(centerCmp); - - rightPannelCmp = new Composite(form, SWT.NO_FOCUS); - - form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); - form.setWeights(new int[] { 15, 40, 20 }); - } - - void refresh() { - modifyFilter(false); - // also refresh bookmarks and groups - } - - private void createDisplay(final Composite parent) { - parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); - - // top filter - filterCmp = new Composite(parent, SWT.NO_FOCUS); - filterCmp.setLayoutData(EclipseUiUtils.fillWidth()); - addFilterPanel(filterCmp); - - // Main display - directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); - List colDefs = new ArrayList<>(); - colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 250)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 150)); - colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), - "Last modified", 400)); - final 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) { - // if (log.isDebugEnabled()) - // 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) { - // currDisplayedFolder = selected; - // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); - // } - // } else if (e.keyCode == SWT.BS) { - // currDisplayedFolder = currDisplayedFolder.getParent(); - // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); - // directoryDisplayViewer.getTable().setFocus(); - // } - // } - // }); - - directoryDisplayViewer.addSelectionChangedListener(new ISelectionChangedListener() { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); - Path selected = null; - if (selection.isEmpty()) - setSelected(null); - else - selected = ((Path) selection.getFirstElement()); - if (selected != null) { - // TODO manage multiple selection - setSelected(selected); - } - } - }); - - 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; - setInput(selected); - } - } - }); - - // The context menu - contextMenu = new FsContextMenu(this); - - table.addMouseListener(new MouseAdapter() { - private static final long serialVersionUID = 6737579410648595940L; - - @Override - public void mouseDown(MouseEvent e) { - if (e.button == 3) { - // contextMenu.setCurrFolderPath(currDisplayedFolder); - contextMenu.show(table, new Point(e.x, e.y), currDisplayedFolder); - } - } - }); - } - - private void addPathElementBtn(Path path) { - Button elemBtn = new Button(filterCmp, SWT.PUSH); - elemBtn.setText(path.getFileName().toString() + " >> "); - CmsUtils.style(elemBtn, FsStyles.BREAD_CRUMB_BTN); - elemBtn.addSelectionListener(new SelectionAdapter() { - private static final long serialVersionUID = -4103695476023480651L; - - @Override - public void widgetSelected(SelectionEvent e) { - setInput(path); - } - }); - } - - public void setInput(Path path) { - if (path.equals(currDisplayedFolder)) - return; - currDisplayedFolder = path; - - Path diff = initialPath.relativize(currDisplayedFolder); - - for (Control child : filterCmp.getChildren()) - if (!child.equals(filterTxt)) - child.dispose(); - - addPathElementBtn(initialPath); - Path currTarget = initialPath; - if (!diff.toString().equals("")) - for (Path pathElem : diff) { - currTarget = currTarget.resolve(pathElem); - addPathElementBtn(currTarget); - } - - filterTxt.setText(""); - filterTxt.moveBelow(null); - setSelected(null); - filterCmp.getParent().layout(true, true); - } - - private void setSelected(Path path) { - currSelected = path; - setOverviewInput(path); - } - - private String getCurrentHomePath() { - // FIXME dirty retrieval of the default home path - String id = UserAdminUtils.getUserLocalId(CurrentUser.getUsername()); - String homepath = "/home/" + JcrUtils.firstCharsToPath(id, 2) + "/" + id; - // FIXME also insure the file subfolder is here - try { - Session session = currentBaseContext.getSession(); - Node home = session.getNode(homepath); - Node fileParent = JcrUtils.mkdirs(home, FS_FILES, NodeType.NT_FOLDER); - fileParent.addMixin(NodeType.MIX_TITLE); - fileParent.setProperty(Property.JCR_TITLE, "My Files"); - if (session.hasPendingChanges()) - session.save(); - } catch (RepositoryException e) { - throw new CmsException("Cannot initialise parent node for My files at " + homepath, e); - } - return homepath; - } - - private final static String NODE_PREFIX = "node://"; - private final static String FS_FILES = "files"; - - protected Path getMyFilesPath() { - String currHomeUriStr = NODE_PREFIX + getCurrentHomePath() + "/" + FS_FILES; - try { - URI uri = new URI(currHomeUriStr); - FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri); - if (fileSystem == null) { - PrivilegedExceptionAction pea = new PrivilegedExceptionAction() { - @Override - public FileSystem run() throws Exception { - return nodeFileSystemProvider.newFileSystem(uri, null); - } - - }; - fileSystem = CurrentUser.tryAs(pea); - } - return fileSystem.getPath(getCurrentHomePath() + "/" + FS_FILES); - } catch (URISyntaxException | PrivilegedActionException e) { - throw new RuntimeException("unable to initialise home file system for " + currHomeUriStr, e); - } - // return Paths.get(System.getProperty("user.dir")); - } - - private Path[] getMyGroupsFilesPath() { - // TODO - Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp") }; - return paths; - } - - private Path[] getMyBookmarks() { - // TODO - Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp"), Paths.get("/opt") }; - return paths; - } - - public Viewer getViewer() { - return directoryDisplayViewer; - } - - private void populateBookmarks(Composite parent) { - CmsUtils.clear(parent); - parent.setLayout(new GridLayout()); - ISelectionChangedListener selList = new BookmarksSelChangeListener(); - - FsTableViewer homeViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); - Table table = homeViewer.configureDefaultSingleColumnTable(bookmarkColWith); - GridData gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 10; - table.setLayoutData(gd); - homeViewer.addSelectionChangedListener(selList); - homeViewer.setPathsInput(getMyFilesPath()); - - appendTitle(parent, "Shared files"); - FsTableViewer groupsViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); - table = groupsViewer.configureDefaultSingleColumnTable(bookmarkColWith); - gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 10; - table.setLayoutData(gd); - groupsViewer.addSelectionChangedListener(selList); - groupsViewer.setPathsInput(getMyGroupsFilesPath()); - - appendTitle(parent, "My bookmarks"); - FsTableViewer bookmarksViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); - table = bookmarksViewer.configureDefaultSingleColumnTable(bookmarkColWith); - gd = EclipseUiUtils.fillWidth(); - gd.horizontalIndent = 10; - table.setLayoutData(gd); - bookmarksViewer.addSelectionChangedListener(selList); - bookmarksViewer.setPathsInput(getMyBookmarks()); - } - - /** - * Recreates the content of the box that displays information about the - * current selected Path. - */ - private void setOverviewInput(Path path) { - try { - EclipseUiUtils.clear(rightPannelCmp); - rightPannelCmp.setLayout(new GridLayout()); - if (path != null) { - // if (isImg(context)) { - // EditableImage image = new Img(parent, RIGHT, context, - // imageWidth); - // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, - // true, false, - // 2, 1)); - // } - - Label contextL = new Label(rightPannelCmp, SWT.NONE); - contextL.setText(path.getFileName().toString()); - contextL.setFont(EclipseUiUtils.getBoldFont(rightPannelCmp)); - addProperty(rightPannelCmp, "Last modified", Files.getLastModifiedTime(path).toString()); - // addProperty(rightPannelCmp, "Owner", - // Files.getOwner(path).getName()); - if (Files.isDirectory(path)) { - addProperty(rightPannelCmp, "Type", "Folder"); - } else { - String mimeType = Files.probeContentType(path); - if (EclipseUiUtils.isEmpty(mimeType)) - mimeType = "Unknown"; - addProperty(rightPannelCmp, "Type", mimeType); - addProperty(rightPannelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false)); - } - } - rightPannelCmp.layout(true, true); - } catch (IOException e) { - throw new CmsException("Cannot display details for " + path.toString(), e); - } - } - - private void addFilterPanel(Composite parent) { - RowLayout rl = new RowLayout(SWT.HORIZONTAL); - rl.wrap = true; - parent.setLayout(rl); - // parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, - // false))); - - filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); - filterTxt.setMessage("Search current folder"); - filterTxt.setLayoutData(new RowData(250, SWT.DEFAULT)); - 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(currDisplayedFolder, 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 CmsException( - "Unable to determine unique child existence and get it under " + parent + " with filter " + filter, - ioe); - } - } - - private void modifyFilter(boolean fromOutside) { - if (!fromOutside) - if (currDisplayedFolder != null) { - String filter = filterTxt.getText() + "*"; - directoryDisplayViewer.setInput(currDisplayedFolder, filter); - } - } - - private class BookmarksSelChangeListener implements ISelectionChangedListener { - - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection selection = (IStructuredSelection) event.getSelection(); - if (selection.isEmpty()) - return; - else { - Path newSelected = (Path) selection.getFirstElement(); - if (newSelected.equals(currDisplayedFolder) && newSelected.equals(initialPath)) - return; - initialPath = newSelected; - setInput(newSelected); - } - } - } - - // Simplify UI implementation - private void addProperty(Composite parent, String propName, String value) { - Label contextL = new Label(parent, SWT.NONE); - contextL.setText(propName + ": " + value); - } - - 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; - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/fs/FsContextMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/fs/FsContextMenu.java deleted file mode 100644 index 1eeccb308..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/fs/FsContextMenu.java +++ /dev/null @@ -1,388 +0,0 @@ -package org.argeo.cms.fs; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Method; -import java.net.URI; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.argeo.cms.CmsException; -import org.argeo.cms.util.CmsUtils; -import org.argeo.eclipse.ui.EclipseUiUtils; -import org.argeo.eclipse.ui.dialogs.SingleValue; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.ShellEvent; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.FileDialog; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; - -/** Generic popup context menu to manage NIO Path in a Viewer. */ -public class FsContextMenu extends Shell { - private static final long serialVersionUID = -9120261153509855795L; - - private final static Log log = LogFactory.getLog(FsContextMenu.class); - - // Default known actions - public final static String ACTION_ID_CREATE_FOLDER = "createFolder"; - public final static String ACTION_ID_BOOKMARK_FOLDER = "bookmarkFolder"; - public final static String ACTION_ID_SHARE_FOLDER = "shareFolder"; - public final static String ACTION_ID_DOWNLOAD_FOLDER = "downloadFolder"; - public final static String ACTION_ID_DELETE = "delete"; - public final static String ACTION_ID_UPLOAD_FILE = "uploadFiles"; - public final static String ACTION_ID_OPEN = "open"; - - // TODO - private final static String cmBox = "contextMenu_box"; - private final static String button_suffix = "_btn"; - - // Local context - private final CmsFsBrowser browser; - // private final Viewer viewer; - private final static String KEY_ACTION_ID = "actionId"; - private final static String[] DEFAULT_ACTIONS = { ACTION_ID_CREATE_FOLDER, ACTION_ID_BOOKMARK_FOLDER, - ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_UPLOAD_FILE, - ACTION_ID_OPEN }; - private Map actionButtons = new HashMap(); - - private Path currFolderPath; - - public FsContextMenu(CmsFsBrowser browser) { // Viewer viewer, Display - // display) { - super(browser.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); - this.browser = browser; - setLayout(EclipseUiUtils.noSpaceGridLayout()); - - Composite boxCmp = new Composite(this, SWT.NO_FOCUS | SWT.BORDER); - boxCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); - CmsUtils.style(boxCmp, cmBox); - createContextMenu(boxCmp); - - addShellListener(new ActionsShellListener()); - } - - protected void createContextMenu(Composite boxCmp) { - ActionsSelListener asl = new ActionsSelListener(); - for (String actionId : DEFAULT_ACTIONS) { - Button btn = new Button(boxCmp, SWT.FLAT | SWT.PUSH | SWT.LEAD); - btn.setText(getLabel(actionId)); - btn.setLayoutData(EclipseUiUtils.fillWidth()); - CmsUtils.markup(btn); - CmsUtils.style(btn, actionId + button_suffix); - btn.setData(KEY_ACTION_ID, actionId); - btn.addSelectionListener(asl); - actionButtons.put(actionId, btn); - } - } - - protected String getLabel(String actionId) { - switch (actionId) { - case ACTION_ID_CREATE_FOLDER: - return "Create Folder"; - case ACTION_ID_BOOKMARK_FOLDER: - return "Bookmark Folder"; - case ACTION_ID_SHARE_FOLDER: - return "Share Folder"; - case ACTION_ID_DOWNLOAD_FOLDER: - return "Download as zip archive"; - case ACTION_ID_DELETE: - return "Delete"; - case ACTION_ID_UPLOAD_FILE: - return "Upload Files"; - case ACTION_ID_OPEN: - return "Open"; - default: - throw new IllegalArgumentException("Unknown action ID " + actionId); - } - } - - protected void aboutToShow(Control source, Point location) { - IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); - boolean emptySel = true; - boolean multiSel = false; - boolean isFolder = true; - if (selection != null && !selection.isEmpty()) { - emptySel = false; - multiSel = selection.size() > 1; - if (!multiSel && selection.getFirstElement() instanceof Path) { - isFolder = Files.isDirectory((Path) selection.getFirstElement()); - } - } - if (emptySel) { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE); - setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_OPEN, - // to be implemented - ACTION_ID_BOOKMARK_FOLDER); - } else if (multiSel) { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); - setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_OPEN, - // to be implemented - ACTION_ID_BOOKMARK_FOLDER); - } else if (isFolder) { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); - setVisible(false, ACTION_ID_OPEN, - // to be implemented - ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_BOOKMARK_FOLDER); - } else { - setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_OPEN, ACTION_ID_DELETE); - setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, - // to be implemented - ACTION_ID_BOOKMARK_FOLDER); - } - } - - private void setVisible(boolean visible, String... buttonIds) { - for (String id : buttonIds) { - Button button = actionButtons.get(id); - button.setVisible(visible); - GridData gd = (GridData) button.getLayoutData(); - gd.heightHint = visible ? SWT.DEFAULT : 0; - } - } - - public void show(Control source, Point location, Path currFolderPath) { - if (isVisible()) - setVisible(false); - // TODO find a better way to retrieve the parent path (cannot be deduced - // from table content because it will fail on an empty folder) - this.currFolderPath = currFolderPath; - aboutToShow(source, location); - pack(); - layout(); - if (source instanceof Control) - setLocation(((Control) source).toDisplay(location.x, location.y)); - open(); - } - - class StyleButton extends Label { - private static final long serialVersionUID = 7731102609123946115L; - - public StyleButton(Composite parent, int swtStyle) { - super(parent, swtStyle); - } - - } - - // class ActionsMouseListener extends MouseAdapter { - // private static final long serialVersionUID = -1041871937815812149L; - // - // @Override - // public void mouseDown(MouseEvent e) { - // Object eventSource = e.getSource(); - // if (e.button == 1) { - // if (eventSource instanceof Button) { - // Button pressedBtn = (Button) eventSource; - // String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); - // switch (actionId) { - // case ACTION_ID_CREATE_FOLDER: - // createFolder(); - // break; - // case ACTION_ID_DELETE: - // deleteItems(); - // break; - // default: - // throw new IllegalArgumentException("Unimplemented action " + actionId); - // // case ACTION_ID_SHARE_FOLDER: - // // return "Share Folder"; - // // case ACTION_ID_DOWNLOAD_FOLDER: - // // return "Download as zip archive"; - // // case ACTION_ID_UPLOAD_FILE: - // // return "Upload Files"; - // // case ACTION_ID_OPEN: - // // return "Open"; - // } - // } - // } - // viewer.getControl().setFocus(); - // // setVisible(false); - // } - // } - - class ActionsSelListener extends SelectionAdapter { - private static final long serialVersionUID = -1041871937815812149L; - - @Override - public void widgetSelected(SelectionEvent e) { - Object eventSource = e.getSource(); - if (eventSource instanceof Button) { - Button pressedBtn = (Button) eventSource; - String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); - switch (actionId) { - case ACTION_ID_CREATE_FOLDER: - createFolder(); - break; - case ACTION_ID_DELETE: - deleteItems(); - break; - case ACTION_ID_OPEN: - openFile(); - break; - case ACTION_ID_UPLOAD_FILE: - uploadFiles(); - break; - default: - throw new IllegalArgumentException("Unimplemented action " + actionId); - // case ACTION_ID_SHARE_FOLDER: - // return "Share Folder"; - // case ACTION_ID_DOWNLOAD_FOLDER: - // return "Download as zip archive"; - // case ACTION_ID_OPEN: - // return "Open"; - } - } - browser.setFocus(); - // viewer.getControl().setFocus(); - // setVisible(false); - - } - } - - class ActionsShellListener extends org.eclipse.swt.events.ShellAdapter { - private static final long serialVersionUID = -5092341449523150827L; - - @Override - public void shellDeactivated(ShellEvent e) { - setVisible(false); - } - } - - private void openFile() { - log.warn("Implement single sourced, workbench independant \"Open File\" action"); - } - - private void deleteItems() { - IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); - if (selection.isEmpty()) - return; - - StringBuilder builder = new StringBuilder(); - @SuppressWarnings("unchecked") - Iterator iterator = selection.iterator(); - List paths = new ArrayList<>(); - - while (iterator.hasNext()) { - Path path = (Path) iterator.next(); - builder.append(path.getFileName() + ", "); - paths.add(path); - } - String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2) - + ". Are you sure?"; - if (MessageDialog.openConfirm(this, "Confirm deletion", msg)) { - for (Path path : paths) { - try { - // Might have already been deleted if we are in a tree - Files.deleteIfExists(path); - } catch (IOException e) { - throw new CmsException("Cannot delete path " + path, e); - } - } - browser.refresh(); - } - } - - private void createFolder() { - String msg = "Please provide a name."; - String name = SingleValue.ask("Create folder", msg); - // TODO enhance check of name validity - if (EclipseUiUtils.notEmpty(name)) { - try { - Path child = currFolderPath.resolve(name); - if (Files.exists(child)) - throw new CmsException("An item with name " + name + " already exists at " - + currFolderPath.toString() + ", cannot create"); - else - Files.createDirectories(child); - browser.refresh(); - } catch (IOException e) { - throw new CmsException("Cannot create folder " + name + " at " + currFolderPath.toString(), e); - } - } - } - - private void uploadFiles() { - try { - FileDialog dialog = new FileDialog(browser.getShell(), SWT.MULTI); - dialog.setText("Choose one or more files to upload"); - - if (EclipseUiUtils.notEmpty(dialog.open())) { - String[] names = dialog.getFileNames(); - // Workaround small differences between RAP and RCP - // 1. returned names are absolute path on RAP and - // relative in RCP - // 2. in RCP we must use getFilterPath that does not - // exists on RAP - Method filterMethod = null; - Path parPath = null; - try { - filterMethod = dialog.getClass().getDeclaredMethod("getFilterPath"); - String filterPath = (String) filterMethod.invoke(dialog); - parPath = Paths.get(filterPath); - } catch (NoSuchMethodException nsme) { // RAP - } - if (names.length == 0) - return; - else { - loop: for (String name : names) { - Path tmpPath = Paths.get(name); - if (parPath != null) - tmpPath = parPath.resolve(tmpPath); - if (Files.exists(tmpPath)) { - URI uri = tmpPath.toUri(); - String uriStr = uri.toString(); - - if (Files.isDirectory(tmpPath)) { - MessageDialog.openError(browser.getShell(), "Unimplemented directory import", - "Upload of directories in the system is not yet implemented"); - continue loop; - } - Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString()); - InputStream in = null; - try { - in = new ByteArrayInputStream(Files.readAllBytes(tmpPath)); - Files.copy(in, targetPath); - Files.delete(tmpPath); - } finally { - IOUtils.closeQuietly(in); - } - if (log.isDebugEnabled()) - log.debug("copied uploaded file " + uriStr + " to " + targetPath.toString()); - } else { - String msg = "Cannot copy tmp file from " + tmpPath.toString(); - if (parPath != null) - msg += "\nPlease remember that file upload fails when choosing files from the \"Recently Used\" bookmarks on some OS"; - MessageDialog.openError(browser.getShell(), "Missing file", msg); - continue loop; - } - } - browser.refresh(); - } - } - } catch (Exception e) { - e.printStackTrace(); - MessageDialog.openError(getShell(), "Upload has failed", "Cannot import files to " + currFolderPath); - } - } - - public void setCurrFolderPath(Path currFolderPath) { - this.currFolderPath = currFolderPath; - } -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/fs/FsStyles.java b/org.argeo.cms.ui/src/org/argeo/cms/fs/FsStyles.java deleted file mode 100644 index 6801a61eb..000000000 --- a/org.argeo.cms.ui/src/org/argeo/cms/fs/FsStyles.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.argeo.cms.fs; - -public interface FsStyles { - - String BREAD_CRUMB_BTN = "breadCrumb_btn"; -} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java new file mode 100644 index 000000000..8562138a9 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/CmsFsBrowser.java @@ -0,0 +1,520 @@ +package org.argeo.cms.ui.fs; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.spi.FileSystemProvider; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.Node; +import javax.jcr.Repository; +import javax.jcr.Session; + +import org.argeo.cms.CmsException; +import org.argeo.cms.auth.CurrentUser; +import org.argeo.cms.util.CmsUtils; +import org.argeo.eclipse.ui.ColumnDefinition; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.fs.FileIconNameLabelProvider; +import org.argeo.eclipse.ui.fs.FsTableViewer; +import org.argeo.eclipse.ui.fs.FsUiConstants; +import org.argeo.eclipse.ui.fs.FsUiUtils; +import org.argeo.eclipse.ui.fs.NioFileLabelProvider; +import org.argeo.node.NodeUtils; +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.jface.viewers.Viewer; +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.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowData; +import org.eclipse.swt.layout.RowLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Text; + +/** + * Default CMS browser composite: a sashForm layout with bookmarks at the left + * hand side, a simple table in the middle and an overview at right hand side. + */ +public class CmsFsBrowser extends Composite { + // private final static Log log = LogFactory.getLog(CmsFsBrowser.class); + private static final long serialVersionUID = -40347919096946585L; + + private final FileSystemProvider nodeFileSystemProvider; + private final Node currentBaseContext; + + // UI Parts for the browser + private Composite leftPannelCmp; + private Composite filterCmp; + private Text filterTxt; + private FsTableViewer directoryDisplayViewer; + private Composite rightPannelCmp; + + private FsContextMenu contextMenu; + + // Local context (this composite is state full) + private Path initialPath; + private Path currDisplayedFolder; + private Path currSelected; + + // local variables (to be cleaned) + private int bookmarkColWith = 500; + + /* + * WARNING: unfinalised implementation of the mechanism to retrieve base + * paths + */ + + private final static String NODE_PREFIX = "node://"; + + private String getCurrentHomePath() { + try { + Repository repo = currentBaseContext.getSession().getRepository(); + Session session = CurrentUser.tryAs(() -> repo.login()); + String homepath = NodeUtils.getUserHome(session).getPath(); + return homepath; + } catch (Exception e) { + throw new CmsException("Cannot retrieve Current User Home Path", e); + } + } + + protected Path[] getMyFilesPath() { + // return Paths.get(System.getProperty("user.dir")); + String currHomeUriStr = NODE_PREFIX + getCurrentHomePath(); + try { + URI uri = new URI(currHomeUriStr); + FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri); + if (fileSystem == null) { + PrivilegedExceptionAction pea = new PrivilegedExceptionAction() { + @Override + public FileSystem run() throws Exception { + return nodeFileSystemProvider.newFileSystem(uri, null); + } + + }; + fileSystem = CurrentUser.tryAs(pea); + } + Path[] paths = { fileSystem.getPath(getCurrentHomePath()), fileSystem.getPath("/") }; + return paths; + } catch (URISyntaxException | PrivilegedActionException e) { + throw new RuntimeException("unable to initialise home file system for " + currHomeUriStr, e); + } + } + + private Path[] getMyGroupsFilesPath() { + // TODO + Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp") }; + return paths; + } + + private Path[] getMyBookmarks() { + // TODO + Path[] paths = { Paths.get(System.getProperty("user.dir")), Paths.get("/tmp"), Paths.get("/opt") }; + return paths; + } + + /* End of warning */ + + public CmsFsBrowser(Composite parent, int style, Node context, FileSystemProvider fileSystemProvider) { + super(parent, style); + this.nodeFileSystemProvider = fileSystemProvider; + this.currentBaseContext = context; + + this.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + SashForm form = new SashForm(this, SWT.HORIZONTAL); + + leftPannelCmp = new Composite(form, SWT.NO_FOCUS); + // Bookmarks are still static + populateBookmarks(leftPannelCmp); + + Composite centerCmp = new Composite(form, SWT.BORDER | SWT.NO_FOCUS); + createDisplay(centerCmp); + + rightPannelCmp = new Composite(form, SWT.NO_FOCUS); + + form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + form.setWeights(new int[] { 15, 40, 20 }); + } + + void refresh() { + modifyFilter(false); + // also refresh bookmarks and groups + } + + private void createDisplay(final Composite parent) { + parent.setLayout(EclipseUiUtils.noSpaceGridLayout()); + + // top filter + filterCmp = new Composite(parent, SWT.NO_FOCUS); + filterCmp.setLayoutData(EclipseUiUtils.fillWidth()); + addFilterPanel(filterCmp); + + // Main display + directoryDisplayViewer = new FsTableViewer(parent, SWT.MULTI); + List colDefs = new ArrayList<>(); + colDefs.add(new ColumnDefinition(new FileIconNameLabelProvider(), "Name", 250)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_SIZE), "Size", 100)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_TYPE), "Type", 150)); + colDefs.add(new ColumnDefinition(new NioFileLabelProvider(FsUiConstants.PROPERTY_LAST_MODIFIED), + "Last modified", 400)); + final 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) { + // if (log.isDebugEnabled()) + // 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) { + // currDisplayedFolder = selected; + // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); + // } + // } else if (e.keyCode == SWT.BS) { + // currDisplayedFolder = currDisplayedFolder.getParent(); + // directoryDisplayViewer.setInput(currDisplayedFolder, "*"); + // directoryDisplayViewer.getTable().setFocus(); + // } + // } + // }); + + directoryDisplayViewer.addSelectionChangedListener(new ISelectionChangedListener() { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) directoryDisplayViewer.getSelection(); + Path selected = null; + if (selection.isEmpty()) + setSelected(null); + else + selected = ((Path) selection.getFirstElement()); + if (selected != null) { + // TODO manage multiple selection + setSelected(selected); + } + } + }); + + 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; + setInput(selected); + } + } + }); + + // The context menu + contextMenu = new FsContextMenu(this); + + table.addMouseListener(new MouseAdapter() { + private static final long serialVersionUID = 6737579410648595940L; + + @Override + public void mouseDown(MouseEvent e) { + if (e.button == 3) { + // contextMenu.setCurrFolderPath(currDisplayedFolder); + contextMenu.show(table, new Point(e.x, e.y), currDisplayedFolder); + } + } + }); + } + + private void addPathElementBtn(Path path) { + Button elemBtn = new Button(filterCmp, SWT.PUSH); + String nameStr; + if (path.toString().equals("/")) + nameStr = "[jcr:root]"; + else + nameStr = path.getFileName().toString(); + elemBtn.setText(nameStr + " >> "); + CmsUtils.style(elemBtn, FsStyles.BREAD_CRUMB_BTN); + elemBtn.addSelectionListener(new SelectionAdapter() { + private static final long serialVersionUID = -4103695476023480651L; + + @Override + public void widgetSelected(SelectionEvent e) { + setInput(path); + } + }); + } + + public void setInput(Path path) { + if (path.equals(currDisplayedFolder)) + return; + currDisplayedFolder = path; + + Path diff = initialPath.relativize(currDisplayedFolder); + + for (Control child : filterCmp.getChildren()) + if (!child.equals(filterTxt)) + child.dispose(); + + addPathElementBtn(initialPath); + Path currTarget = initialPath; + if (!diff.toString().equals("")) + for (Path pathElem : diff) { + currTarget = currTarget.resolve(pathElem); + addPathElementBtn(currTarget); + } + + filterTxt.setText(""); + filterTxt.moveBelow(null); + setSelected(null); + filterCmp.getParent().layout(true, true); + } + + private void setSelected(Path path) { + currSelected = path; + setOverviewInput(path); + } + + public Viewer getViewer() { + return directoryDisplayViewer; + } + + private void populateBookmarks(Composite parent) { + CmsUtils.clear(parent); + parent.setLayout(new GridLayout()); + ISelectionChangedListener selList = new BookmarksSelChangeListener(); + + FsTableViewer homeViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); + Table table = homeViewer.configureDefaultSingleColumnTable(bookmarkColWith); + GridData gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + homeViewer.addSelectionChangedListener(selList); + homeViewer.setPathsInput(getMyFilesPath()); + + appendTitle(parent, "Shared files"); + FsTableViewer groupsViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); + table = groupsViewer.configureDefaultSingleColumnTable(bookmarkColWith); + gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + groupsViewer.addSelectionChangedListener(selList); + groupsViewer.setPathsInput(getMyGroupsFilesPath()); + + appendTitle(parent, "My bookmarks"); + FsTableViewer bookmarksViewer = new FsTableViewer(parent, SWT.SINGLE | SWT.NO_SCROLL); + table = bookmarksViewer.configureDefaultSingleColumnTable(bookmarkColWith); + gd = EclipseUiUtils.fillWidth(); + gd.horizontalIndent = 10; + table.setLayoutData(gd); + bookmarksViewer.addSelectionChangedListener(selList); + bookmarksViewer.setPathsInput(getMyBookmarks()); + } + + /** + * Recreates the content of the box that displays information about the + * current selected Path. + */ + private void setOverviewInput(Path path) { + try { + EclipseUiUtils.clear(rightPannelCmp); + rightPannelCmp.setLayout(new GridLayout()); + if (path != null) { + // if (isImg(context)) { + // EditableImage image = new Img(parent, RIGHT, context, + // imageWidth); + // image.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, + // true, false, + // 2, 1)); + // } + + Label contextL = new Label(rightPannelCmp, SWT.NONE); + contextL.setText(path.getFileName().toString()); + contextL.setFont(EclipseUiUtils.getBoldFont(rightPannelCmp)); + addProperty(rightPannelCmp, "Last modified", Files.getLastModifiedTime(path).toString()); + // addProperty(rightPannelCmp, "Owner", + // Files.getOwner(path).getName()); + if (Files.isDirectory(path)) { + addProperty(rightPannelCmp, "Type", "Folder"); + } else { + String mimeType = Files.probeContentType(path); + if (EclipseUiUtils.isEmpty(mimeType)) + mimeType = "Unknown"; + addProperty(rightPannelCmp, "Type", mimeType); + addProperty(rightPannelCmp, "Size", FsUiUtils.humanReadableByteCount(Files.size(path), false)); + } + } + rightPannelCmp.layout(true, true); + } catch (IOException e) { + throw new CmsException("Cannot display details for " + path.toString(), e); + } + } + + private void addFilterPanel(Composite parent) { + RowLayout rl = new RowLayout(SWT.HORIZONTAL); + rl.wrap = true; + parent.setLayout(rl); + // parent.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, + // false))); + + filterTxt = new Text(parent, SWT.SEARCH | SWT.ICON_CANCEL); + filterTxt.setMessage("Search current folder"); + filterTxt.setLayoutData(new RowData(250, SWT.DEFAULT)); + 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(currDisplayedFolder, 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 CmsException( + "Unable to determine unique child existence and get it under " + parent + " with filter " + filter, + ioe); + } + } + + private void modifyFilter(boolean fromOutside) { + if (!fromOutside) + if (currDisplayedFolder != null) { + String filter = filterTxt.getText() + "*"; + directoryDisplayViewer.setInput(currDisplayedFolder, filter); + } + } + + private class BookmarksSelChangeListener implements ISelectionChangedListener { + + @Override + public void selectionChanged(SelectionChangedEvent event) { + IStructuredSelection selection = (IStructuredSelection) event.getSelection(); + if (selection.isEmpty()) + return; + else { + Path newSelected = (Path) selection.getFirstElement(); + if (newSelected.equals(currDisplayedFolder) && newSelected.equals(initialPath)) + return; + initialPath = newSelected; + setInput(newSelected); + } + } + } + + // Simplify UI implementation + private void addProperty(Composite parent, String propName, String value) { + Label contextL = new Label(parent, SWT.NONE); + contextL.setText(propName + ": " + value); + } + + 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; + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java new file mode 100644 index 000000000..ffb5f0a83 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsContextMenu.java @@ -0,0 +1,384 @@ +package org.argeo.cms.ui.fs; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.cms.CmsException; +import org.argeo.cms.util.CmsUtils; +import org.argeo.eclipse.ui.EclipseUiUtils; +import org.argeo.eclipse.ui.dialogs.SingleValue; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; + +/** Generic popup context menu to manage NIO Path in a Viewer. */ +public class FsContextMenu extends Shell { + private static final long serialVersionUID = -9120261153509855795L; + + private final static Log log = LogFactory.getLog(FsContextMenu.class); + + // Default known actions + public final static String ACTION_ID_CREATE_FOLDER = "createFolder"; + public final static String ACTION_ID_BOOKMARK_FOLDER = "bookmarkFolder"; + public final static String ACTION_ID_SHARE_FOLDER = "shareFolder"; + public final static String ACTION_ID_DOWNLOAD_FOLDER = "downloadFolder"; + public final static String ACTION_ID_DELETE = "delete"; + public final static String ACTION_ID_UPLOAD_FILE = "uploadFiles"; + public final static String ACTION_ID_OPEN = "open"; + + // Local context + private final CmsFsBrowser browser; + // private final Viewer viewer; + private final static String KEY_ACTION_ID = "actionId"; + private final static String[] DEFAULT_ACTIONS = { ACTION_ID_CREATE_FOLDER, ACTION_ID_BOOKMARK_FOLDER, + ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_UPLOAD_FILE, + ACTION_ID_OPEN }; + private Map actionButtons = new HashMap(); + + private Path currFolderPath; + + public FsContextMenu(CmsFsBrowser browser) { // Viewer viewer, Display + // display) { + super(browser.getDisplay(), SWT.NO_TRIM | SWT.BORDER | SWT.ON_TOP); + this.browser = browser; + setLayout(EclipseUiUtils.noSpaceGridLayout()); + + Composite boxCmp = new Composite(this, SWT.NO_FOCUS | SWT.BORDER); + boxCmp.setLayout(EclipseUiUtils.noSpaceGridLayout()); + CmsUtils.style(boxCmp, FsStyles.CONTEXT_MENU_BOX); + createContextMenu(boxCmp); + + addShellListener(new ActionsShellListener()); + } + + protected void createContextMenu(Composite boxCmp) { + ActionsSelListener asl = new ActionsSelListener(); + for (String actionId : DEFAULT_ACTIONS) { + Button btn = new Button(boxCmp, SWT.FLAT | SWT.PUSH | SWT.LEAD); + btn.setText(getLabel(actionId)); + btn.setLayoutData(EclipseUiUtils.fillWidth()); + CmsUtils.markup(btn); + CmsUtils.style(btn, actionId + FsStyles.BUTTON_SUFFIX); + btn.setData(KEY_ACTION_ID, actionId); + btn.addSelectionListener(asl); + actionButtons.put(actionId, btn); + } + } + + protected String getLabel(String actionId) { + switch (actionId) { + case ACTION_ID_CREATE_FOLDER: + return "Create Folder"; + case ACTION_ID_BOOKMARK_FOLDER: + return "Bookmark Folder"; + case ACTION_ID_SHARE_FOLDER: + return "Share Folder"; + case ACTION_ID_DOWNLOAD_FOLDER: + return "Download as zip archive"; + case ACTION_ID_DELETE: + return "Delete"; + case ACTION_ID_UPLOAD_FILE: + return "Upload Files"; + case ACTION_ID_OPEN: + return "Open"; + default: + throw new IllegalArgumentException("Unknown action ID " + actionId); + } + } + + protected void aboutToShow(Control source, Point location) { + IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); + boolean emptySel = true; + boolean multiSel = false; + boolean isFolder = true; + if (selection != null && !selection.isEmpty()) { + emptySel = false; + multiSel = selection.size() > 1; + if (!multiSel && selection.getFirstElement() instanceof Path) { + isFolder = Files.isDirectory((Path) selection.getFirstElement()); + } + } + if (emptySel) { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE); + setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_DELETE, ACTION_ID_OPEN, + // to be implemented + ACTION_ID_BOOKMARK_FOLDER); + } else if (multiSel) { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); + setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_OPEN, + // to be implemented + ACTION_ID_BOOKMARK_FOLDER); + } else if (isFolder) { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_DELETE); + setVisible(false, ACTION_ID_OPEN, + // to be implemented + ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, ACTION_ID_BOOKMARK_FOLDER); + } else { + setVisible(true, ACTION_ID_CREATE_FOLDER, ACTION_ID_UPLOAD_FILE, ACTION_ID_OPEN, ACTION_ID_DELETE); + setVisible(false, ACTION_ID_SHARE_FOLDER, ACTION_ID_DOWNLOAD_FOLDER, + // to be implemented + ACTION_ID_BOOKMARK_FOLDER); + } + } + + private void setVisible(boolean visible, String... buttonIds) { + for (String id : buttonIds) { + Button button = actionButtons.get(id); + button.setVisible(visible); + GridData gd = (GridData) button.getLayoutData(); + gd.heightHint = visible ? SWT.DEFAULT : 0; + } + } + + public void show(Control source, Point location, Path currFolderPath) { + if (isVisible()) + setVisible(false); + // TODO find a better way to retrieve the parent path (cannot be deduced + // from table content because it will fail on an empty folder) + this.currFolderPath = currFolderPath; + aboutToShow(source, location); + pack(); + layout(); + if (source instanceof Control) + setLocation(((Control) source).toDisplay(location.x, location.y)); + open(); + } + + class StyleButton extends Label { + private static final long serialVersionUID = 7731102609123946115L; + + public StyleButton(Composite parent, int swtStyle) { + super(parent, swtStyle); + } + + } + + // class ActionsMouseListener extends MouseAdapter { + // private static final long serialVersionUID = -1041871937815812149L; + // + // @Override + // public void mouseDown(MouseEvent e) { + // Object eventSource = e.getSource(); + // if (e.button == 1) { + // if (eventSource instanceof Button) { + // Button pressedBtn = (Button) eventSource; + // String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); + // switch (actionId) { + // case ACTION_ID_CREATE_FOLDER: + // createFolder(); + // break; + // case ACTION_ID_DELETE: + // deleteItems(); + // break; + // default: + // throw new IllegalArgumentException("Unimplemented action " + actionId); + // // case ACTION_ID_SHARE_FOLDER: + // // return "Share Folder"; + // // case ACTION_ID_DOWNLOAD_FOLDER: + // // return "Download as zip archive"; + // // case ACTION_ID_UPLOAD_FILE: + // // return "Upload Files"; + // // case ACTION_ID_OPEN: + // // return "Open"; + // } + // } + // } + // viewer.getControl().setFocus(); + // // setVisible(false); + // } + // } + + class ActionsSelListener extends SelectionAdapter { + private static final long serialVersionUID = -1041871937815812149L; + + @Override + public void widgetSelected(SelectionEvent e) { + Object eventSource = e.getSource(); + if (eventSource instanceof Button) { + Button pressedBtn = (Button) eventSource; + String actionId = (String) pressedBtn.getData(KEY_ACTION_ID); + switch (actionId) { + case ACTION_ID_CREATE_FOLDER: + createFolder(); + break; + case ACTION_ID_DELETE: + deleteItems(); + break; + case ACTION_ID_OPEN: + openFile(); + break; + case ACTION_ID_UPLOAD_FILE: + uploadFiles(); + break; + default: + throw new IllegalArgumentException("Unimplemented action " + actionId); + // case ACTION_ID_SHARE_FOLDER: + // return "Share Folder"; + // case ACTION_ID_DOWNLOAD_FOLDER: + // return "Download as zip archive"; + // case ACTION_ID_OPEN: + // return "Open"; + } + } + browser.setFocus(); + // viewer.getControl().setFocus(); + // setVisible(false); + + } + } + + class ActionsShellListener extends org.eclipse.swt.events.ShellAdapter { + private static final long serialVersionUID = -5092341449523150827L; + + @Override + public void shellDeactivated(ShellEvent e) { + setVisible(false); + } + } + + private void openFile() { + log.warn("Implement single sourced, workbench independant \"Open File\" action"); + } + + private void deleteItems() { + IStructuredSelection selection = ((IStructuredSelection) browser.getViewer().getSelection()); + if (selection.isEmpty()) + return; + + StringBuilder builder = new StringBuilder(); + @SuppressWarnings("unchecked") + Iterator iterator = selection.iterator(); + List paths = new ArrayList<>(); + + while (iterator.hasNext()) { + Path path = (Path) iterator.next(); + builder.append(path.getFileName() + ", "); + paths.add(path); + } + String msg = "You are about to delete following elements: " + builder.substring(0, builder.length() - 2) + + ". Are you sure?"; + if (MessageDialog.openConfirm(this, "Confirm deletion", msg)) { + for (Path path : paths) { + try { + // Might have already been deleted if we are in a tree + Files.deleteIfExists(path); + } catch (IOException e) { + throw new CmsException("Cannot delete path " + path, e); + } + } + browser.refresh(); + } + } + + private void createFolder() { + String msg = "Please provide a name."; + String name = SingleValue.ask("Create folder", msg); + // TODO enhance check of name validity + if (EclipseUiUtils.notEmpty(name)) { + try { + Path child = currFolderPath.resolve(name); + if (Files.exists(child)) + throw new CmsException("An item with name " + name + " already exists at " + + currFolderPath.toString() + ", cannot create"); + else + Files.createDirectories(child); + browser.refresh(); + } catch (IOException e) { + throw new CmsException("Cannot create folder " + name + " at " + currFolderPath.toString(), e); + } + } + } + + private void uploadFiles() { + try { + FileDialog dialog = new FileDialog(browser.getShell(), SWT.MULTI); + dialog.setText("Choose one or more files to upload"); + + if (EclipseUiUtils.notEmpty(dialog.open())) { + String[] names = dialog.getFileNames(); + // Workaround small differences between RAP and RCP + // 1. returned names are absolute path on RAP and + // relative in RCP + // 2. in RCP we must use getFilterPath that does not + // exists on RAP + Method filterMethod = null; + Path parPath = null; + try { + filterMethod = dialog.getClass().getDeclaredMethod("getFilterPath"); + String filterPath = (String) filterMethod.invoke(dialog); + parPath = Paths.get(filterPath); + } catch (NoSuchMethodException nsme) { // RAP + } + if (names.length == 0) + return; + else { + loop: for (String name : names) { + Path tmpPath = Paths.get(name); + if (parPath != null) + tmpPath = parPath.resolve(tmpPath); + if (Files.exists(tmpPath)) { + URI uri = tmpPath.toUri(); + String uriStr = uri.toString(); + + if (Files.isDirectory(tmpPath)) { + MessageDialog.openError(browser.getShell(), "Unimplemented directory import", + "Upload of directories in the system is not yet implemented"); + continue loop; + } + Path targetPath = currFolderPath.resolve(tmpPath.getFileName().toString()); + InputStream in = null; + try { + in = new ByteArrayInputStream(Files.readAllBytes(tmpPath)); + Files.copy(in, targetPath); + Files.delete(tmpPath); + } finally { + IOUtils.closeQuietly(in); + } + if (log.isDebugEnabled()) + log.debug("copied uploaded file " + uriStr + " to " + targetPath.toString()); + } else { + String msg = "Cannot copy tmp file from " + tmpPath.toString(); + if (parPath != null) + msg += "\nPlease remember that file upload fails when choosing files from the \"Recently Used\" bookmarks on some OS"; + MessageDialog.openError(browser.getShell(), "Missing file", msg); + continue loop; + } + } + browser.refresh(); + } + } + } catch (Exception e) { + e.printStackTrace(); + MessageDialog.openError(getShell(), "Upload has failed", "Cannot import files to " + currFolderPath); + } + } + + public void setCurrFolderPath(Path currFolderPath) { + this.currFolderPath = currFolderPath; + } +} diff --git a/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsStyles.java b/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsStyles.java new file mode 100644 index 000000000..9ae319282 --- /dev/null +++ b/org.argeo.cms.ui/src/org/argeo/cms/ui/fs/FsStyles.java @@ -0,0 +1,8 @@ +package org.argeo.cms.ui.fs; + +/** FS Ui specific CSS styles */ +public interface FsStyles { + String BREAD_CRUMB_BTN = "breadCrumb_btn"; + String CONTEXT_MENU_BOX = "contextMenu_box"; + String BUTTON_SUFFIX = "_btn"; +}