/*******************************************************************************
* Copyright (c) 2014 Hussein Mhanna
*
* 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:
* Hussein Mhanna - initial API and implementation
******************************************************************************/
package org.eclipse.rmf.reqif10.search.ui.masterdetails;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EventObject;
import org.eclipse.emf.common.command.BasicCommandStack;
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.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;
import org.eclipse.emf.edit.ui.action.CopyAction;
import org.eclipse.emf.edit.ui.action.CreateChildAction;
import org.eclipse.emf.edit.ui.action.CutAction;
import org.eclipse.emf.edit.ui.action.DeleteAction;
import org.eclipse.emf.edit.ui.action.PasteAction;
import org.eclipse.emf.edit.ui.action.RedoAction;
import org.eclipse.emf.edit.ui.action.StaticSelectionCommandAction;
import org.eclipse.emf.edit.ui.action.UndoAction;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;
import org.eclipse.emf.edit.ui.provider.ExtendedImageRegistry;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionManager;
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.resource.ImageDescriptor;
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.rmf.reqif10.search.criteria.Criteria;
import org.eclipse.rmf.reqif10.search.criteria.CriteriaFactory;
import org.eclipse.rmf.reqif10.search.criteria.Criterias;
import org.eclipse.rmf.reqif10.search.criteria.Operator;
import org.eclipse.rmf.reqif10.search.criteria.impl.CriteriaImpl;
import org.eclipse.rmf.reqif10.search.criteria.provider.CriteriaItemProviderAdapterFactory;
import org.eclipse.rmf.reqif10.search.ui.ReqIFSearchUIPlugin;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.DetailsPart;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.MasterDetailsBlock;
import org.eclipse.ui.forms.SectionPart;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;
/**
* @author Hussein MHANNA
*
*/
public class CriteriasMasterDetailsBlock extends MasterDetailsBlock {
/**
* The horizontal orientation icon
*/
protected static final String HORIZONTAL_ORIENTATION_ICON_PATH = "full/obj16/Horizontal_Orientation.gif";
/**
* The vertical orientation icon
*/
protected static final String VERTICAL_ORIENTATION_ICON_PATH = "full/obj16/Vertical_Orientation.gif";
/**
* The horizontal orientation action label
*/
protected static final String HORIZONTAL_ORIENTATION_ACTION_LABEL = getString("_UI_HorizontalOrientation_label");
/**
* The vertical orientation action label
*/
protected static final String VERTICAL_ORIENTATION_ACTION_LABEL = getString("_UI_VerticalOrientation_label");
/**
* The managed form
*/
protected IManagedForm managedForm;
/**
* The tree viewer
*/
private TreeViewer treeViewer;
/**
* The temporary resource
*/
private Resource resource;
/**
* The adapter factory
*/
private ComposedAdapterFactory adapterFactory;
/**
* The editing domain
*/
private EditingDomain editingDomain;
/**
* The search criteria separator
*/
private String REQIF_SEARCH_CRITERIA = "REQIF_SEARCH_CRITERIA";
/**
* The criteria separator
*/
private String CRITERIA_SEPARATOR = "CRITERIA";
/**
* The criterias separator
*/
private String CRITERIAS_SEPARATOR = "CRITERIAS";
/**
* The criterias instance
*/
private Criterias criterias;
/**
* The constructor
*/
public CriteriasMasterDetailsBlock() {
}
/**
* {@inheritDoc}
*/
@Override
public void createContent(final IManagedForm managedForm) {
super.createContent(managedForm);
managedForm.getForm().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(final DisposeEvent e) {
managedForm.getForm().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
CriteriasMasterDetailsBlock.this.dispose();
}
});
}
});
}
/**
* Create the {@link AdapterFactory}
*
*/
private void createAdapterFactory() {
adapterFactory = new ComposedAdapterFactory();
adapterFactory
.addAdapterFactory(new ResourceItemProviderAdapterFactory());
adapterFactory
.addAdapterFactory(new CriteriaItemProviderAdapterFactory());
}
/**
* Create the {@link EditingDomain}
*/
private void createEditingDomain() {
CommandStack commandStack = new BasicCommandStack();
commandStack.addCommandStackListener(new CommandStackListener() {
@Override
public void commandStackChanged(EventObject event) {
handleCommandStackChanged(((CommandStack) event.getSource())
.getMostRecentCommand());
}
});
editingDomain = new AdapterFactoryEditingDomain(adapterFactory,
commandStack);
}
/**
* Called when the command stack is changed
*
* @param mostRecentCommand
* : The most recent command
*/
protected void handleCommandStackChanged(final Command mostRecentCommand) {
// Try to select the affected objects.
//
if (mostRecentCommand != null) {
setSelectionToViewer(mostRecentCommand.getAffectedObjects());
}
}
/**
* This sets the selection into whichever viewer is active.
*
* @param selections
* : The selection elements
*
*/
protected void setSelectionToViewer(final Collection<?> selections) {
if ((selections != null) && !selections.isEmpty()) {
final Object[] selectionsArray = selections.toArray();
final Runnable runnable = new Runnable() {
@Override
public void run() {
// Select the objects if the input of the viewer is not
// null, otherwise select them when the
// input will be set.
if (treeViewer != null) {
treeViewer.setSelection(new StructuredSelection(
selectionsArray), true);
}
}
};
if (false == managedForm.getForm().isDisposed()) {
managedForm.getForm().getDisplay().asyncExec(runnable);
}
}
}
private void createTreeViewer(Composite parent) {
Tree tree = getToolkit().createTree(parent, SWT.BORDER);
tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
treeViewer = new TreeViewer(tree);
treeViewer.setLabelProvider(new AdapterFactoryLabelProvider(
adapterFactory));
treeViewer.setContentProvider(new AdapterFactoryContentProvider(
adapterFactory));
}
/**
* {@inheritDoc}
*/
@Override
protected void createMasterPart(final IManagedForm managedForm,
final Composite parent) {
this.managedForm = managedForm;
createAdapterFactory();
createEditingDomain();
FormToolkit toolkit = getToolkit();
Section criteriasSection = toolkit.createSection(parent,
ExpandableComposite.EXPANDED | ExpandableComposite.TITLE_BAR);
criteriasSection.setText("Criterias");
final SectionPart sectionPart = new SectionPart(criteriasSection);
Composite composite = toolkit.createComposite(criteriasSection,
SWT.NONE);
criteriasSection.setClient(composite);
composite.setLayout(new GridLayout(1, false));
createTreeViewer(composite);
loadInput();
treeViewer.setInput(resource);
treeViewer.expandAll();
treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
managedForm.fireSelectionChanged(sectionPart,
treeViewer.getSelection());
}
});
new TreeViewerActionProvider(treeViewer);
}
private void loadInput() {
resource = editingDomain.getResourceSet().createResource(
URI.createURI("TEMP"));
criterias = CriteriaFactory.eINSTANCE.createCriterias();
resource.getContents().add(criterias);
String input = ReqIFSearchUIPlugin.getPlugin().getDialogSettings()
.get(REQIF_SEARCH_CRITERIA);
if (input != null && false == input.trim().isEmpty()) {
String[] criteriasString = null;
if (input.contains(CRITERIAS_SEPARATOR)) {
criteriasString = input.split(CRITERIAS_SEPARATOR);
} else {
criteriasString = new String[] { input };
}
for (String criteriaString : criteriasString) {
String[] criteriaDetails = criteriaString
.split(CRITERIA_SEPARATOR);
Criteria criteria = CriteriaFactory.eINSTANCE.createCriteria();
criteria.setFeatureName(criteriaDetails[0]);
criteria.setSerachedText(criteriaDetails[1]);
criteria.setOperator(Operator.get(criteriaDetails[2]));
criteria.setReplacementText(criteriaDetails[3]);
criteria.setSensitiveCase(Boolean.valueOf(criteriaDetails[4]));
criterias.getCriterias().add(criteria);
}
} else {
criterias.getCriterias().add(
CriteriaFactory.eINSTANCE.createCriteria());
}
}
private void saveInput() {
StringBuilder criteriaStringBuilder = new StringBuilder();
Criterias criterias = (Criterias) resource.getContents().get(0);
EList<Criteria> criteriasList = criterias.getCriterias();
for (int i = 0; i < criteriasList.size(); i++) {
Criteria criteria = criteriasList.get(i);
criteriaStringBuilder.append(criteria.getFeatureName())
.append(CRITERIA_SEPARATOR)
.append(criteria.getSerachedText())
.append(CRITERIA_SEPARATOR).append(criteria.getOperator())
.append(CRITERIA_SEPARATOR)
.append(criteria.getReplacementText())
.append(CRITERIA_SEPARATOR)
.append(criteria.isSensitiveCase());
if (i != criteriasList.size() - 1) {
criteriaStringBuilder.append(CRITERIAS_SEPARATOR);
}
}
ReqIFSearchUIPlugin.getPlugin().getDialogSettings()
.put(REQIF_SEARCH_CRITERIA, criteriaStringBuilder.toString());
}
/**
* {@inheritDoc}
*/
@Override
protected void registerPages(DetailsPart detailsPart) {
CriteriaDetailsPage criteriaDetailsPage = new CriteriaDetailsPage(
editingDomain);
detailsPart.registerPage(CriteriaImpl.class, criteriaDetailsPage);
}
/**
* {@inheritDoc}
*/
@Override
protected void createToolBarActions(final IManagedForm managedForm) {
final ScrolledForm form = managedForm.getForm();
form.getToolBarManager().add(
createHorizontalOrientationAction(managedForm));
form.getToolBarManager().add(
createVerticalOrientationAction(managedForm));
}
/**
* Create and return the horizontal orientation action
*
* @param managedForm
* : Tha {@link IManagedForm}
* @return The created action
*/
protected IAction createHorizontalOrientationAction(
final IManagedForm managedForm) {
final ImageDescriptor imageDescriptor = ExtendedImageRegistry.INSTANCE
.getImageDescriptor(ReqIFSearchUIPlugin.INSTANCE
.getImage(HORIZONTAL_ORIENTATION_ICON_PATH));
final Action horizontalOrientationAction = new Action(
HORIZONTAL_ORIENTATION_ACTION_LABEL, IAction.AS_RADIO_BUTTON) {
@Override
public void run() {
CriteriasMasterDetailsBlock.this.sashForm
.setOrientation(SWT.HORIZONTAL);
managedForm.getForm().reflow(true);
}
};
horizontalOrientationAction.setChecked(false);
horizontalOrientationAction
.setToolTipText(HORIZONTAL_ORIENTATION_ACTION_LABEL);
horizontalOrientationAction.setImageDescriptor(imageDescriptor);
return horizontalOrientationAction;
}
/**
* Create and return the vertical orientation action
*
* @param managedForm
* : The {@link IManagedForm}
* @return The created action
*/
protected IAction createVerticalOrientationAction(
final IManagedForm managedForm) {
final ImageDescriptor imageDescriptor = ExtendedImageRegistry.INSTANCE
.getImageDescriptor(ReqIFSearchUIPlugin.INSTANCE
.getImage(VERTICAL_ORIENTATION_ICON_PATH));
final Action verticalOrientationAction = new Action(
VERTICAL_ORIENTATION_ACTION_LABEL, IAction.AS_RADIO_BUTTON) {
@Override
public void run() {
CriteriasMasterDetailsBlock.this.sashForm
.setOrientation(SWT.VERTICAL);
managedForm.getForm().reflow(true);
}
};
verticalOrientationAction.setChecked(false);
verticalOrientationAction
.setToolTipText(VERTICAL_ORIENTATION_ACTION_LABEL);
verticalOrientationAction.setImageDescriptor(imageDescriptor);
return verticalOrientationAction;
}
/**
* Return the toolkit
*
* @return The form toolkit
*/
protected FormToolkit getToolkit() {
return managedForm.getToolkit();
}
/**
* Return the actions to be added in the tool bar
*
* @return The actions to be added in the tool bar
*/
protected Collection<IAction> getToolbarActions(
final IManagedForm managedForm) {
return Collections.emptyList();
}
/**
* This looks up a string in plugin.properties, making a substitution.
*
* @param key
* the key of the string.
* @return a string resource associated with the key.
*/
protected static String getString(final String key) {
return ReqIFSearchUIPlugin.INSTANCE.getString(key);
}
/**
* Called to dispose the widgets
*/
protected void dispose() {
saveInput();
try {
resource.delete(null);
} catch (IOException e) {
e.printStackTrace();
}
adapterFactory.dispose();
}
public Collection<Criteria> getCriterias() {
return criterias.getCriterias();
}
/**
* An extended {@link IMenuListener} that manage undo, redo, cut, copy and
* paste action.
*
* @author Hussein MHANNA
*
*/
protected class TreeViewerActionProvider implements IMenuListener {
/**
* This will contain one
* {@link org.eclipse.emf.edit.ui.action.CreateChildAction}
* corresponding to each descriptor generated for the current selection
* by the item provider.
*/
protected Collection<StaticSelectionCommandAction> createChildActions;
/**
* This is the action used to implement delete.
*/
private DeleteAction deleteAction;
/**
* This is the action used to implement undo.
*/
private UndoAction undoAction;
/**
* This is the action used to implement redo.
*/
private RedoAction redoAction;
/**
* This is the action used to implement cut.
*/
private CutAction cutAction;
/**
* This is the action used to implement copy.
*/
private CopyAction copyAction;
/**
* This is the action used to implement paste.
*/
private PasteAction pasteAction;
/**
* The tree viewer
*/
private final TreeViewer viewer;
/**
* The selection changed listener
*/
private final ISelectionChangedListener selectionChangedListener;
/**
* The constructor
*
* @param viewer
* : The viewer on wich the actions will be added
*/
public TreeViewerActionProvider(final TreeViewer viewer) {
this.viewer = viewer;
initializeActions();
selectionChangedListener = new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent event) {
TreeViewerActionProvider.this
.selectionChanged((IStructuredSelection) event
.getSelection());
}
};
this.viewer.addSelectionChangedListener(selectionChangedListener);
this.viewer.getTree().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(final DisposeEvent e) {
TreeViewerActionProvider.this.viewer
.removeSelectionChangedListener(selectionChangedListener);
}
});
final MenuManager manager = new MenuManager();
final Menu menu = manager.createContextMenu(viewer.getControl());
manager.addMenuListener(this);
manager.setRemoveAllWhenShown(true);
viewer.getControl().setMenu(menu);
}
/**
* Initialize the actions
*/
private void initializeActions() {
final ISharedImages sharedImages = PlatformUI.getWorkbench()
.getSharedImages();
deleteAction = createDeleteAction();
deleteAction.setImageDescriptor(sharedImages
.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE));
undoAction = createUndoAction();
undoAction.setImageDescriptor(sharedImages
.getImageDescriptor(ISharedImages.IMG_TOOL_UNDO));
redoAction = createRedoAction();
redoAction.setImageDescriptor(sharedImages
.getImageDescriptor(ISharedImages.IMG_TOOL_REDO));
cutAction = createCutAction();
cutAction.setImageDescriptor(sharedImages
.getImageDescriptor(ISharedImages.IMG_TOOL_CUT));
copyAction = createCopyAction();
copyAction.setImageDescriptor(sharedImages
.getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
pasteAction = createPasteAction();
pasteAction.setImageDescriptor(sharedImages
.getImageDescriptor(ISharedImages.IMG_TOOL_PASTE));
}
/**
* Create a delete action.
*
* @return the action used to implement delete.
*
* @see #deleteAction
*/
protected DeleteAction createDeleteAction() {
return new DeleteAction(editingDomain, true);
}
/**
* Create an undo action.
*
* @return the action used to implement undo.
*
* @see #undoAction
*/
protected UndoAction createUndoAction() {
return new UndoAction(editingDomain);
}
/**
* Create a redo action.
*
* @return the action used to implement redo.
*
* @see #redoAction
*/
protected RedoAction createRedoAction() {
return new RedoAction(editingDomain);
}
/**
* Create a cut action.
*
* @return the action used to implement cut.
*
* @see #cutAction
*/
protected CutAction createCutAction() {
return new CutAction(editingDomain);
}
/**
* Create a copy action.
*
* @return the action used to implement copy.
*
* @see #copyAction
*/
protected CopyAction createCopyAction() {
return new CopyAction(editingDomain);
}
/**
* Create a paste action.
*
* @return the action used to implement paste.
*
* @see #pasteAction
*/
protected PasteAction createPasteAction() {
return new PasteAction(editingDomain);
}
/**
* This implements {@link org.eclipse.jface.action.IMenuListener} to
* help fill the context menus with contributions from the Edit menu.
*
* @param menuManager
* The menu manager.
*/
@Override
public void menuAboutToShow(final IMenuManager menuManager) {
MenuManager submenuManager = null;
submenuManager = new MenuManager("New");
populateManager(submenuManager, createChildActions, null);
menuManager.add(submenuManager);
menuManager.add(new Separator());
menuManager.add(new ActionContributionItem(undoAction));
menuManager.add(new ActionContributionItem(redoAction));
menuManager.add(new Separator());
menuManager.add(new ActionContributionItem(cutAction));
menuManager.add(new ActionContributionItem(copyAction));
menuManager.add(new ActionContributionItem(pasteAction));
menuManager.add(new Separator());
menuManager.add(new ActionContributionItem(deleteAction));
}
/**
* Called when the selection is changed in the tree viewer.
*
* @param selection
* The selection of {@link SelectionChangedEvent}.
*/
private void selectionChanged(final IStructuredSelection selection) {
deleteAction.selectionChanged(selection);
undoAction.update();
redoAction.update();
cutAction.selectionChanged(selection);
copyAction.selectionChanged(selection);
pasteAction.selectionChanged(selection);
// Query the new selection for appropriate new child/descriptors
//
Collection<?> newChildDescriptors = null;
if (selection.size() == 1) {
final Object object = selection.getFirstElement();
newChildDescriptors = editingDomain.getNewChildDescriptors(
object, null);
}
// Generate actions for selection; populate and redraw the menus.
//
createChildActions = generateCreateChildActions(
newChildDescriptors, selection);
}
protected Collection<StaticSelectionCommandAction> generateCreateChildActions(
final Collection<?> descriptors, final ISelection selection) {
final Collection<StaticSelectionCommandAction> actions = new ArrayList<StaticSelectionCommandAction>();
if (descriptors != null) {
for (final Object descriptor : descriptors) {
actions.add(createCreateChildAction(selection, descriptor));
}
}
return actions;
}
protected CreateChildAction createCreateChildAction(
final ISelection selection, final Object descriptor) {
return new CreateChildAction(editingDomain, selection, descriptor);
}
}
/**
* This populates the specified <code>manager</code> with
* {@link org.eclipse.jface.action.ActionContributionItem}s based on the
* {@link org.eclipse.jface.action.IAction}s contained in the
* <code>actions</code> collection, by inserting them before the specified
* contribution item <code>contributionID</code>. If <code>ID</code> is
* <code>null</code>, they are simply added.
*/
protected void populateManager(final IContributionManager manager,
final Collection<? extends IAction> actions,
final String contributionID) {
if (actions != null) {
for (final IAction action : actions) {
if (contributionID != null) {
manager.insertBefore(contributionID, action);
} else {
manager.add(action);
}
}
}
}
}