/******************************************************************************* * 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; } }