/*
* Copyright (c) 2006 Stiftung Deutsches Elektronen-Synchroton,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
*
* THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
* WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
* IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
* CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
* NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
* DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
* THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
* USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
* PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
* AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
*/
package org.csstudio.ui.util.composites;
import java.util.ArrayList;
import java.util.List;
import org.csstudio.ui.util.Activator;
import org.csstudio.ui.util.ImageUtil;
import org.csstudio.ui.util.ResourceUtil;
import org.csstudio.ui.util.internal.localization.Messages;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.model.WorkbenchLabelProvider;
/**
* Workbench-level composite for choosing a resource.
*
* <p>
* <b>Code is based upon
* <code>org.eclipse.ui.internal.ide.misc.ContainerSelectionGroup</code> in
* plugin <code>org.eclipse.ui.ide</code>.</b>
* </p>
*
* @author Alexander Will, Joerg Rathlev
* @version $Revision$
*/
//TODO: Copied from org.csstudio.platform.ui. Review is needed.
public final class ResourceSelectionGroup extends Composite {
/**
* This action is for creating a new folder.
*
* @author Kai Meyer
*
*/
private final class NewFolderAction extends Action {
/**
* The Shell.
*/
private final Shell _shell;
/**
* Constructor.
* @param shell
* The Shell for this Action
*/
public NewFolderAction(final Shell shell) {
_shell = shell;
this.setText("Create new folder");
this.setToolTipText("Creates a new folder");
this.setImageDescriptor(
ImageUtil.getInstance().getImageDescriptor(Activator.ID, "icons/new_folder.png")); //$NON-NLS-1$
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
final IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(getFullPath());
final StringBuffer buffer = new StringBuffer(Messages.CreateFolderAction_DIALOG_MESSAGE);
buffer.append(" (");
buffer.append(resource.getFullPath());
buffer.append("/..)");
final InputDialog inputDialog = new InputDialog(_shell, Messages.CreateFolderAction_DIALOG_TITLE,
buffer.toString(), "", null);
final int ret = inputDialog.open();
if (ret == Window.OK) {
final String folderName = inputDialog.getValue();
if (folderName != null) {
if (resource instanceof IContainer) {
if (ResourceUtil.getInstance().createFolder((IContainer) resource, folderName)==ResourceUtil.FOLDEREXISTS) {
MessageDialog.openInformation(_shell, Messages.CreateFolderAction_ERROR_TITLE,
Messages.CreateFolderAction_ERROR_MESSAGE);
}
refreshTree();
}
}
}
}
}
/**
* This action is for creating a new project.
*
* @author Kai Meyer
*
*/
private final class NewProjectAction extends Action {
/**
* The Shell.
*/
private final Shell _shell;
/**
* Constructor.
* @param shell
* The Shell for this Action
*/
public NewProjectAction(final Shell shell) {
_shell = shell;
this.setText("Create new project");
this.setToolTipText("Creates a new project");
this.setImageDescriptor(ImageUtil.getInstance().getImageDescriptor(Activator.ID, "icons/new_project.png"));
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
InputDialog inputDialog = new InputDialog(_shell, Messages.CreateProjectAction_DIALOG_TITLE,
Messages.CreateProjectAction_DIALOG_MESSAGE, "", null); //$NON-NLS-1$ //$NON-NLS-2$
int ret = inputDialog.open();
if (ret == Window.OK) {
String projectName = inputDialog.getValue();
if (projectName != null) {
if (ResourceUtil.getInstance().createProject(projectName)==ResourceUtil.PROJECTEXISTS) {
MessageDialog.openInformation(_shell, Messages.CreateProjectAction_ERROR_TITLE,
Messages.CreateProjectAction_ERROR_MESSAGE);
}
refreshTree();
}
}
}
}
/**
* The listener to notify of events.
*/
private Listener _listener;
/**
* Show all projects by default.
*/
private boolean _showClosedProjects = true;
/**
* Last selection made by user.
*/
private IResource _selectedResource;
/**
* The tree widget.
*/
private TreeViewer _treeViewer;
/**
* The NewFolderAction.
*/
private Action _newFolderAction;
/**
* The NewProjectAction.
*/
private Action _newProjectAction;
/**
* Whether to show the New Folder and New Project actions.
*/
private boolean _showNewContainerActions;
/**
* Sizing constant for the width of the tree.
*/
private static final int SIZING_SELECTION_PANE_WIDTH = 320;
/**
* Sizing constant for the height of the tree.
*/
private static final int SIZING_SELECTION_PANE_HEIGHT = 300;
/**
* Creates a new instance of the widget.
*
* @param parent
* The parent widget of the group.
* @param listener
* A listener to forward events to. Can be null if no listener is
* required.
* @param fileExtensions
* File extensions of files to include in the tree view. Pass
* an empty array or <code>null</code> to show only container
* resources.
* @param showNewContainerActions
* Whether to show the New Folder and New Project actions.
*/
public ResourceSelectionGroup(final Composite parent,
final Listener listener, final String[] fileExtensions,
final boolean showNewContainerActions) {
this(parent, listener, fileExtensions, null, showNewContainerActions);
}
/**
* Creates a new instance of the widget.
*
* @param parent
* The parent widget of the group.
* @param listener
* A listener to forward events to. Can be null if no listener is
* required.
* @param fileExtensions
* File extensions of files to include in the tree view. Pass
* an empty array or <code>null</code> to show only container
* resources.
* @param message
* The text to present to the user.
* @param showNewContainerActions
* Whether to show the New Folder and New Project actions.
*/
public ResourceSelectionGroup(final Composite parent,
final Listener listener, final String[] fileExtensions,
final String message, final boolean showNewContainerActions) {
this(parent, listener, fileExtensions, message, true, showNewContainerActions);
}
/**
* Creates a new instance of the widget.
*
* @param parent
* The parent widget of the group.
* @param listener
* A listener to forward events to. Can be null if no listener is
* required.
* @param fileExtensions
* File extensions of files to include in the tree view. Pass
* an empty array or <code>null</code> to show only container
* resources.
* @param message
* The text to present to the user.
* @param showClosedProjects
* Whether or not to show closed projects.
* @param showNewContainerActions
* Whether to show the New Folder and New Project actions.
*/
public ResourceSelectionGroup(final Composite parent,
final Listener listener, final String[] fileExtensions,
final String message,
final boolean showClosedProjects,
final boolean showNewContainerActions) {
this(parent, listener, fileExtensions, message, showClosedProjects,
showNewContainerActions,
SIZING_SELECTION_PANE_HEIGHT, SIZING_SELECTION_PANE_WIDTH);
}
/**
* Creates a new instance of the widget.
*
* @param parent
* The parent widget of the group.
* @param listener
* A listener to forward events to. Can be null if no listener is
* required.
* @param fileExtensions
* File extensions of files to include in the tree view. Pass
* an empty array or <code>null</code> to show only container
* resources.
* @param message
* The text to present to the user.
* @param showClosedProjects
* Whether or not to show closed projects.
* @param showNewContainerActions
* Whether to show the New Folder and New Project actions.
* @param heightHint
* height hint for the drill down composite
* @param widthHint
* width hint for the drill down composite
*/
public ResourceSelectionGroup(final Composite parent,
final Listener listener, final String[] fileExtensions,
final String message, final boolean showClosedProjects,
final boolean showNewContainerActions,
final int heightHint, final int widthHint) {
super(parent, SWT.NONE);
_listener = listener;
_showClosedProjects = showClosedProjects;
_showNewContainerActions = showNewContainerActions;
if (message != null) {
createContents(message, fileExtensions, heightHint, widthHint);
} else {
createContents(Messages.ContainerSelectionGroup_TITLE,
fileExtensions, heightHint, widthHint);
}
}
/**
* The container selection has changed in the tree view. Update the
* container name field value and notify all listeners.
*
* @param resource
* The container that changed
*/
public void containerSelectionChanged(final IResource resource) {
_selectedResource = resource;
// fire an event so the parent can update its controls
if (_listener != null) {
Event changeEvent = new Event();
changeEvent.type = SWT.Selection;
changeEvent.widget = this;
_listener.handleEvent(changeEvent);
}
}
public void itemDoubleClicked(final IResource resource) {
_selectedResource = resource;
// fire an event so the parent can update its controls
if (_listener != null) {
Event changeEvent = new Event();
changeEvent.type = SWT.MouseDoubleClick;
changeEvent.widget = this;
_listener.handleEvent(changeEvent);
}
}
/**
* Creates the contents of the composite.
*
* @param message
* The text to present to the user.
* @param fileExtensions
* The file extensions of files to include in the tree.
* @param heightHint
* The height of the tree widget.
* @param widthHint
* The width of the tree widget.
*/
public void createContents(final String message,
final String[] fileExtensions, final int heightHint,
final int widthHint) {
GridLayout layout = new GridLayout();
layout.marginWidth = 0;
setLayout(layout);
setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
Label label = new Label(this, SWT.WRAP);
label.setText(message);
// label.setFont(getFont());
createTreeViewer(fileExtensions, heightHint);
// Dialog.applyDialogFont(this);
}
/**
* Returns a new drill down viewer for this dialog.
*
* @param fileExtensions
* The file extensions of files to include in the tree.
* @param heightHint
* height hint for the drill down composite
*/
protected void createTreeViewer(final String[] fileExtensions,
final int heightHint) {
// Create drill down.
DrillDownComposite drillDown = new DrillDownComposite(this, SWT.BORDER);
GridData spec = new GridData(SWT.FILL, SWT.FILL, true, true);
spec.widthHint = SIZING_SELECTION_PANE_WIDTH;
spec.heightHint = heightHint;
drillDown.setLayoutData(spec);
// Create tree viewer inside drill down.
_treeViewer = new TreeViewer(drillDown, SWT.NONE);
drillDown.setChildTree(_treeViewer);
WorkspaceResourceContentProvider cp =
new WorkspaceResourceContentProvider(fileExtensions);
cp.showClosedProjects(_showClosedProjects);
_treeViewer.setContentProvider(cp);
_treeViewer.setLabelProvider(WorkbenchLabelProvider
.getDecoratingWorkbenchLabelProvider());
_treeViewer.setSorter(new ViewerSorter());
_treeViewer.setUseHashlookup(true);
_treeViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(
final SelectionChangedEvent event) {
IStructuredSelection selection = (IStructuredSelection) event
.getSelection();
containerSelectionChanged((IResource) selection
.getFirstElement()); // allow null
if (_newFolderAction!=null) {
_newFolderAction.setEnabled(selection!=null);
}
}
});
_treeViewer.addDoubleClickListener(new IDoubleClickListener() {
@Override
public void doubleClick(final DoubleClickEvent event) {
ISelection selection = event.getSelection();
if (selection instanceof IStructuredSelection) {
Object item = ((IStructuredSelection) selection)
.getFirstElement();
if (item == null) {
return;
}
if (_treeViewer.getExpandedState(item)) {
_treeViewer.collapseToLevel(item, 1);
} else if(_treeViewer.isExpandable(item)){
_treeViewer.expandToLevel(item, 1);
}else if(!(item instanceof IContainer))
itemDoubleClicked((IResource)item);
}
}
});
// This has to be done after the viewer has been laid out
_treeViewer.setInput(ResourcesPlugin.getWorkspace());
this.addNewContainerActions(drillDown.getToolBarManager());
this.addPopupMenu(_treeViewer);
this.setDefaultSelection(_treeViewer);
}
/**
* Sets the first Element of the TreeViewer as selected Item.
* @param viewer
* The TreeViewer, which selection should be set
*/
private void setDefaultSelection(final TreeViewer viewer) {
final TreeItem item = viewer.getTree().getItemCount() > 0 ? viewer.getTree().getItem(0) : null;
if (item!=null) {
viewer.getTree().setSelection(item);
}
}
/**
* Creates the New Folder and New Project actions and adds them to
* the given toolbar manager.
*
* @param manager
* The ToolBarManager, where the Actions are added
*/
private void addNewContainerActions(final ToolBarManager manager) {
if (_showNewContainerActions) {
_newFolderAction = new NewFolderAction(this.getShell());
_newFolderAction.setEnabled(false);
_newProjectAction = new NewProjectAction(this.getShell());
manager.add(new Separator());
manager.add(_newFolderAction);
manager.add(_newProjectAction);
manager.update(true);
}
}
/**
* Adds a PopupMenu to the given TreeViewer.
* @param viewer
* The TreeViewer, where the PopupMenu is added
*/
private void addPopupMenu(final TreeViewer viewer) {
MenuManager popupMenu = new MenuManager();
if (_showNewContainerActions) {
popupMenu.add(_newFolderAction);
popupMenu.add(_newProjectAction);
}
Menu menu = popupMenu.createContextMenu(viewer.getTree());
viewer.getTree().setMenu(menu);
}
/**
* Refreshes the Tree in an async-thread.
*/
private void refreshTree() {
_treeViewer.getTree().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
_treeViewer.refresh();
}
});
}
/**
* Returns the currently entered container name. Null if the field is empty.
* Note that the container may not exist yet if the user entered a new
* container name in the field.
*
* @return IPath
*/
public IPath getFullPath() {
if (_selectedResource == null) {
return null;
}
return _selectedResource.getFullPath();
}
/**
* Gives focus to one of the widgets in the group, as determined by the
* group.
*/
public void setInitialFocus() {
_treeViewer.getTree().setFocus();
}
/**
* Selects the given resource.
*
* @param resource the resource to select.
*/
public void setSelectedResource(final IResource resource) {
_selectedResource = resource;
List<IResource> itemsToExpand = new ArrayList<IResource>();
IContainer parent = resource.getParent();
while (parent != null) {
itemsToExpand.add(0, parent);
parent = parent.getParent();
}
_treeViewer.setExpandedElements(itemsToExpand.toArray());
_treeViewer.setSelection(new StructuredSelection(resource), true);
}
/**
* Selects the resource with the given path.
*
* @param path
* The path to a specific resource.
*/
public void setSelectedResource(final IPath path) {
IContainer workspace = ResourcesPlugin.getWorkspace().getRoot();
IResource res = workspace.findMember(path);
if (res != null) {
setSelectedResource(res);
}
}
}