blob: f0b8689c4845e5ea06fc759625e362622762c6d0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012 Formal Mind GmbH and University of Dusseldorf.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Michael Jastram - initial API and implementation
******************************************************************************/
package org.eclipse.rmf.reqif10.pror.editor.actions;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CommandStack;
import org.eclipse.emf.common.command.CommandStackListener;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.emf.edit.command.DeleteCommand;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.ui.action.CommandActionHandler;
import org.eclipse.emf.edit.ui.action.EditingDomainActionBarContributor;
import org.eclipse.emf.edit.ui.dnd.EditingDomainViewerDropAdapter;
import org.eclipse.emf.edit.ui.dnd.LocalTransfer;
import org.eclipse.emf.edit.ui.dnd.ViewerDragAdapter;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.TrayDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.rmf.reqif10.Specification;
import org.eclipse.rmf.reqif10.pror.editor.IReqifEditor;
import org.eclipse.rmf.reqif10.pror.editor.presentation.ProrAdapterFactoryContentProvider;
import org.eclipse.rmf.reqif10.pror.editor.propertiesview.ProrPropertySheetPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.PlatformUI;
/**
* This modal dialog shows the tree hanging on a provided {@link EObject}. The
* idea is to use this to allow the user to configure things selectively (e.g.
* the columns of a {@link Specification}).
*
* @author jastram
*
*/
public class SubtreeDialog extends TrayDialog implements IMenuListener {
private static final int VALIDATE_ID = 99;
private final EObject input;
private final String title;
private final String helpContext;
private ISelectionProvider originalSelectionProvider;
private IAction[] actions = new IAction[] {};
private boolean presentAsDropdown;
private TreeViewer viewer;
private final List<ViewerFilter> filters = new ArrayList<ViewerFilter>();
private final AdapterFactory adapterFactory;
private final EditingDomain editingDomain;
private final IReqifEditor reqifEditor;
private CommandStackListener commandStackListener;
protected SubtreeDialog(IReqifEditor reqifEditor, EObject input, String title,
String helpContext) {
super(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
this.reqifEditor = reqifEditor;
this.editingDomain = reqifEditor.getEditingDomain();
this.adapterFactory = reqifEditor.getAdapterFactory();
this.input = input;
this.title = title;
this.helpContext = helpContext;
setHelpAvailable(true);
}
void addFilter(ViewerFilter filter) {
filters.add(filter);
}
/**
* We provide the default "Finish" button, instead of the default OK and
* Cancel.
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID,
IDialogConstants.FINISH_LABEL, true);
createButton(parent, VALIDATE_ID, "Validate", false);
}
@Override
protected void buttonPressed(int buttonId) {
if (buttonId == VALIDATE_ID) {
IStructuredSelection selection = (IStructuredSelection) viewer
.getSelection();
if (selection.getFirstElement() instanceof EObject) {
EObject element = (EObject) selection.getFirstElement();
// TODO this is not yet working...
Diagnostic diagnostic = Diagnostician.INSTANCE
.validate(element);
MessageDialog.openInformation(this.getShell(),
"Validation Report", diagnostic.getMessage());
}
} else {
super.buttonPressed(buttonId);
}
}
@Override
protected Point getInitialSize() {
return new Point(500, 600);
}
@Override
protected boolean isResizable() {
return true;
}
@Override
protected Control createDialogArea(Composite parent) {
this.getShell().setText(title);
Composite composite = (Composite) super.createDialogArea(parent);
composite.setLayout(new FormLayout());
ToolBar toolbar = presentAsDropdown ? buildDropdownToolbar(composite)
: buildRowToolbar(composite);
// Enable help
PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, helpContext);
final Sash sash = new Sash(composite, SWT.HORIZONTAL | SWT.BORDER);
FormData data = new FormData();
data.bottom = new FormAttachment(50, 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
sash.setLayoutData(data);
// Without this, the sash wouldn't hold its new position
sash.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
sash.setBounds(e.x, e.y, e.width, e.height);
((FormData) sash.getLayoutData()).bottom = new FormAttachment(
0, e.y);
sash.getParent().layout();
}
});
viewer = new TreeViewer(composite, SWT.BORDER);
viewer.setContentProvider(new ProrAdapterFactoryContentProvider(
getAdapterFactory()));
viewer.setLabelProvider(new AdapterFactoryLabelProvider(
getAdapterFactory()));
viewer.setInput(input);
viewer.setFilters(filters.toArray(new ViewerFilter[] {}));
createContextMenuFor(viewer);
viewer.getTree().addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.DEL) {
ISelection selection = viewer.getSelection();
TreeSelection treeSelection = (TreeSelection) selection;
Object firstElement = treeSelection.getFirstElement();
Command create = DeleteCommand.create(editingDomain,
firstElement);
editingDomain.getCommandStack().execute(create);
}
}
});
data = new FormData();
data.top = new FormAttachment(toolbar, 0);
data.bottom = new FormAttachment(sash, 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
viewer.getControl().setLayoutData(data);
// Now that we have the TreeViewer, let's attach the Toolbar
data = new FormData();
data.bottom = new FormAttachment(viewer.getControl(), 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
toolbar.setLayoutData(data);
final ProrPropertySheetPage propertySheet = new ProrPropertySheetPage(
(AdapterFactoryEditingDomain) editingDomain, adapterFactory);
propertySheet.createControl(composite);
data = new FormData();
data.top = new FormAttachment(sash, 0);
data.bottom = new FormAttachment(100, 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
propertySheet.getControl().setLayoutData(data);
// The and Property Sheet Editor must know about changes, so that the context
// menu contains the correct child creation actions.
originalSelectionProvider = getActionBarContributor().getActiveEditor()
.getSite().getSelectionProvider();
getActionBarContributor().getActiveEditor().getSite()
.setSelectionProvider(viewer);
viewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
((ISelectionChangedListener)getActionBarContributor()).selectionChanged(event);
propertySheet.selectionChanged(null, event.getSelection());
((ISelectionProvider) getActionBarContributor()
.getActiveEditor()).setSelection(event.getSelection());
}
});
commandStackListener = new CommandStackListener() {
public void commandStackChanged(final EventObject event) {
Display.getCurrent().asyncExec(new Runnable() {
public void run() {
Command mostRecentCommand = ((CommandStack) event
.getSource()).getMostRecentCommand();
if (mostRecentCommand != null) {
StructuredSelection selection = new StructuredSelection(
new ArrayList<Object>(mostRecentCommand
.getAffectedObjects()));
viewer.setSelection(selection);
}
}
});
}
};
editingDomain.getCommandStack().addCommandStackListener(
commandStackListener);
return composite;
}
/**
* It's unbelievable that there is no better way for doing this! I
* couldn't even use {@link MenuManager} to add the action, as that created
* an additional menu hierarchy that I couldn't get rid of!
*
* @param composite
* @return
*/
private ToolBar buildDropdownToolbar(Composite composite) {
final ToolBar toolbar = new ToolBar(composite, SWT.PUSH);
final Menu menu = new Menu(getShell(), SWT.POP_UP);
for (final IAction action : actions) {
MenuItem newItem = new MenuItem(menu, SWT.PUSH);
newItem.setText(action.getText());
newItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
action.run();
}
});
}
final ToolItem item = new ToolItem(toolbar, SWT.DROP_DOWN);
item.setText("Select Action...");
item.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
// The following code would allow to distinguish arrow and
// button, something we don't need right now.
// if (event.detail == SWT.ARROW) {
Rectangle rect = item.getBounds();
Point pt = new Point(rect.x, rect.y + rect.height);
pt = toolbar.toDisplay(pt);
menu.setLocation(pt.x, pt.y);
menu.setVisible(true);
}
// }
});
return toolbar;
}
private ToolBar buildRowToolbar(Composite composite) {
ToolBar toolbar = new ToolBar(composite, SWT.PUSH);
ToolBarManager toolbarManager = new ToolBarManager(toolbar);
for (IAction action : actions) {
toolbarManager.add(action);
}
toolbarManager.update(true);
return toolbar;
}
private AdapterFactory getAdapterFactory() {
return adapterFactory;
}
public void createContextMenuFor(StructuredViewer viewer) {
MenuManager contextMenu = new MenuManager("#PopUp");
contextMenu.add(new Separator("additions"));
contextMenu.setRemoveAllWhenShown(true);
contextMenu.addMenuListener(this);
Menu menu = contextMenu.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
int dndOperations = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK;
Transfer[] transfers = new Transfer[] { LocalTransfer.getInstance() };
viewer.addDragSupport(dndOperations, transfers, new ViewerDragAdapter(
viewer));
viewer.addDropSupport(dndOperations, transfers,
new EditingDomainViewerDropAdapter(editingDomain, viewer));
}
public void menuAboutToShow(IMenuManager menuManager) {
getActionBarContributor().menuAboutToShow(menuManager);
// Some actions may have the wrong selection, because the
// ActionBarContributor grabs it from the associated editor.
for (IContributionItem item : menuManager.getItems()) {
if (item instanceof ActionContributionItem) {
IAction action = ((ActionContributionItem) item).getAction();
if (action instanceof CommandActionHandler) {
CommandActionHandler handler = (CommandActionHandler) action;
handler.updateSelection((IStructuredSelection) viewer.getSelection());
}
}
}
}
private EditingDomainActionBarContributor getActionBarContributor() {
return reqifEditor.getActionBarContributor();
}
@Override
public boolean close() {
getActionBarContributor().getActiveEditor().getSite()
.setSelectionProvider(originalSelectionProvider);
if (commandStackListener != null) {
editingDomain.getCommandStack().removeCommandStackListener(commandStackListener);
}
return super.close();
}
/**
* If Actions are set before opening the Dialog, then these are presented in
* a row or in a dropdown.
* <p>
*
* When shown in a row, the text is omitted and only the icon shown. This
* makes sense for a small number of well-defined actions.
* <p>
*
* When shown in a dropdown, both icon and text are shown. This makes sense
* when the text is important and/or the number of items is unknown.
*
* @param actions
* @param presentAsDropdown
*/
public void setActions(IAction[] actions, boolean presentAsDropdown) {
this.actions = actions;
this.presentAsDropdown = presentAsDropdown;
}
}