/*******************************************************************************
* Copyright (c) 2010, 2011 Mia-Software.
* 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:
* Nicolas Bros (Mia-Software) - Bug 331203 - table model editor - initial API and implementation
* Nicolas Guyomar (Mia-Software) - Bug 331442 - To be able to add and remove lines (model elements) from the table
* Nicolas Bros (Mia-Software) - Bug 332226 - To be able to create or delete model element from the table
* Nicolas Guyomar (Mia-Software) - Bug 332226 - To be able to create or delete model element from the table
* Nicolas Bros (Mia-Software) - Bug 331900 - customizable NatTable
* Nicolas Guyomar (Mia-Software) - Bug 332924 - To be able to save the table
* Nicolas Guyomar (Mia-Software) - Bug 332998 - To be able to add a column and fill it with the result of a query
* Gregoire Dupe (Mia-Software) - Bug 332998 - To be able to add a column and fill it with the result of a query
* Nicolas Guyomar (Mia-Software) - Bug 333015 - To be able to hide columns
* Nicolas Guyomar (Mia-Software) - Bug 333029 - To be able to save the size of the lines and the columns
* Nicolas Guyomar (Mia-Software) - Bug 335154 - Sort Column By Type : Cannot modify resource set without a write transaction
* Nicolas Guyomar (Mia-Software) - Bug 335020 - Nattable widget should use the Eclipse framework
*******************************************************************************/
package org.eclipse.papyrus.infra.table.common.internal;
import java.util.EventObject;
import java.util.HashMap;
import org.eclipse.core.databinding.conversion.IConverter;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.common.command.CommandStackListener;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.databinding.EObjectObservableValue;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.domain.IEditingDomainProvider;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.facet.widgets.nattable.INatTableWidget;
import org.eclipse.emf.facet.widgets.nattable.INatTableWidgetProvider;
import org.eclipse.emf.facet.widgets.nattable.IWorkbenchPartProvider;
import org.eclipse.emf.facet.widgets.nattable.instance.tableinstance.TableInstance;
import org.eclipse.emf.facet.widgets.nattable.instance.tableinstance.TableinstancePackage;
import org.eclipse.emf.facet.widgets.nattable.instance.tableinstance2.TableInstance2;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.papyrus.infra.emf.databinding.EMFObservableValue;
import org.eclipse.papyrus.infra.table.common.messages.Messages;
import org.eclipse.papyrus.infra.table.instance.papyrustableinstance.PapyrusTableInstance;
import org.eclipse.papyrus.infra.table.instance.papyrustableinstance.PapyrustableinstancePackage;
import org.eclipse.papyrus.infra.widgets.editors.BooleanRadio;
import org.eclipse.papyrus.infra.widgets.editors.StringEditor;
import org.eclipse.papyrus.infra.widgets.editors.StringLabel;
import org.eclipse.papyrus.uml.tools.providers.UMLLabelProvider;
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.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.uml2.uml.util.UMLUtil;
/**
* Comes from org.eclipse.emf.facet.widget.nattable.workbench.
*
* This class should not be used by others plugins
* TODO Should be deleted or merge with an other classe
*/
public class NatTableEditor extends EditorPart implements ISelectionProvider, IEditingDomainProvider, IWorkbenchPartProvider {
public static final String ID = "org.eclipse.emf.facet.widgets.nattable.workbench.editor.NatTableEditor"; //$NON-NLS-1$
private TableEditorInput tableEditorInput;
protected INatTableWidget natTableWidget;
private EditingDomain editingDomain;
private MenuManager menuMgr;
private StringLabel contextLabel;
private final CommandStackListener commandListener = new CommandStackListener() {
public void commandStackChanged(final EventObject event) {
Display.getCurrent().asyncExec(new Runnable() {
public void run() {
firePropertyChange(PROP_DIRTY);
}
});
}
};
/**
* we listen the context to refresh it in the table if the context change
*/
private final Adapter contextListener = new AdapterImpl() {
/**
*
* @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(org.eclipse.emf.common.notify.Notification)
*
* @param notification
*/
@Override
public void notifyChanged(final Notification notification) {
NatTableEditor.this.contextLabel.refreshValue();
super.notifyChanged(notification);
}
};
@SuppressWarnings("rawtypes")
// We cannot change the method signature because of the override
@Override
public Object getAdapter(final Class adapter) {
if(adapter == INatTableWidgetProvider.class) {
return new INatTableWidgetProvider() {
public INatTableWidget getNatTableWidget() {
return NatTableEditor.this.natTableWidget;
}
};
}
return null;
}
@Override
public void init(final IEditorSite site, final IEditorInput input) throws PartInitException {
if(input instanceof TableEditorInput) {
this.tableEditorInput = (TableEditorInput)input;
this.editingDomain = this.tableEditorInput.getEditingDomain();
initializeEditingDomain();
setSite(site);
setInput(this.tableEditorInput);
setPartName(this.tableEditorInput.getName());
} else if(input instanceof FileEditorInput) {
initializeEditingDomain();
FileEditorInput fileEditorInput = (FileEditorInput)input;
URI uri = URI.createPlatformResourceURI(fileEditorInput.getFile().getFullPath().toString(), false);
if(uri != null) {
Resource resource = null;
if(getEditingDomain() != null) {
resource = getEditingDomain().loadResource(uri.toString());
} else {
ResourceSet rSet = new ResourceSetImpl();
resource = rSet.createResource(uri);
}
PapyrusTableInstance tableInstance = null;
for(EObject eObject : resource.getContents()) {
if(eObject instanceof PapyrusTableInstance) {
tableInstance = (PapyrusTableInstance)eObject;
// One instance of tableInstance per .table file
break;
}
}
this.tableEditorInput = new TableEditorInput(tableInstance, getEditingDomain());
setSite(site);
setInput(this.tableEditorInput);
setPartName(fileEditorInput.getName());
}
} else {
throw new PartInitException("Input should be of type TableEditorInput or a .table file"); //$NON-NLS-1$
}
}
/**
*
* @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*
* @param parent
*/
@Override
public void createPartControl(final Composite parent) {
this.menuMgr = new MenuManager("#PopUp", NatTableEditor.ID); //$NON-NLS-1$
this.menuMgr.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
this.menuMgr.setRemoveAllWhenShown(true);
final Composite editorComposite = createCompositeCompositeWthTableBorder(parent);
// createFirstLine(editorComposite);
// createDescription(editorComposite);
// the composite owning the table
final Composite tableComposite = new Composite(editorComposite, SWT.NONE);
GridLayout tableCompositeGridLayout = new GridLayout(1, true);
tableComposite.setLayout(tableCompositeGridLayout);
final GridData compositeTableGridLayout = new GridData();
compositeTableGridLayout.grabExcessHorizontalSpace = true;
compositeTableGridLayout.grabExcessVerticalSpace = true;
compositeTableGridLayout.horizontalAlignment = SWT.FILL;
compositeTableGridLayout.verticalAlignment = SWT.FILL;
tableComposite.setLayoutData(compositeTableGridLayout);
this.natTableWidget = createNattableWidget(tableComposite, this, this.tableEditorInput.getPapyrusTableInstance().getTable(), this.menuMgr);
getSite().setSelectionProvider(this);
getSite().registerContextMenu(this.menuMgr, this.natTableWidget);
}
private INatTableWidget createNattableWidget(final Composite tableComposite, final NatTableEditor natTableEditor, final TableInstance2 table, final MenuManager menuMgr2) {
// the nattable widget itself
//old instanciation
// this.natTableWidget = INatTableWidgetFactory.INSTANCE.createNatTableWidget(tableComposite, this, this.tableEditorInput.getPapyrusTableInstance().getTable(), this.menuMgr);
this.natTableWidget = new PapyrusNatTableWidget(tableComposite, natTableEditor, table, this.menuMgr);
final GridData tableGridData = new GridData();
tableGridData.grabExcessHorizontalSpace = true;
tableGridData.grabExcessVerticalSpace = true;
tableGridData.horizontalAlignment = SWT.FILL;
tableGridData.verticalAlignment = SWT.FILL;
this.natTableWidget.getComposite().setLayoutData(tableGridData);
return this.natTableWidget;
}
private Composite createCompositeCompositeWthTableBorder(final Composite parent) {
Composite editorComposite = new Composite(parent, SWT.BORDER);
final GridLayout editorGridLayout = new GridLayout(1, true);
editorGridLayout.marginHeight = 0;
editorGridLayout.marginWidth = 0;
GridData data = new GridData();
data.grabExcessHorizontalSpace = true;
data.horizontalAlignment = SWT.FILL;
editorComposite.setLayoutData(data);
editorComposite.setLayout(editorGridLayout);
return editorComposite;
}
/**
*
* @param parent
* the parent composite
*/
protected void createFirstLine(final Composite parent) {
final TableInstance table = this.tableEditorInput.getPapyrusTableInstance().getTable();
Composite firstLineComposite = new Composite(parent, SWT.NONE);
final GridLayout smallGridLayout = new GridLayout(2, true);
smallGridLayout.marginHeight = 0;
smallGridLayout.marginWidth = 0;
smallGridLayout.marginLeft = 0;
smallGridLayout.marginRight = 0;
firstLineComposite.setLayout(smallGridLayout);
GridData lineData = new GridData();
lineData.horizontalSpan = 1;
lineData.horizontalAlignment = SWT.FILL;
firstLineComposite.setLayoutData(lineData);
//we display the context of the table
this.contextLabel = new StringLabel(firstLineComposite, SWT.LEFT);
this.contextLabel.setLabel(Messages.NatTableEditor_TableContextLabel);
this.contextLabel.setToolTipText(Messages.NatTableEditor_TableContextTollTip);
//we observe the feature context of the table (and not the name of the context, because the context is not a NamedElement, but an EObject
final IObservableValue contextObservable = new EObjectObservableValue(table, TableinstancePackage.eINSTANCE.getTableInstance_Context());
table.getContext().eAdapters().add(this.contextListener);
/*
* we should set the converted before the observable!
*/
this.contextLabel.setConverters(null, new ContextLabelConverter());
this.contextLabel.setLabelProvider(new UMLLabelProvider());
this.contextLabel.setModelObservable(contextObservable);
//set the layout for contextLabel
GridData contextGridData = new GridData();
contextGridData.grabExcessHorizontalSpace = true;
contextGridData.horizontalAlignment = SWT.FILL;
contextGridData.horizontalSpan = 1;
this.contextLabel.setLayoutData(contextGridData);
BooleanRadio checkbox = new BooleanRadio(firstLineComposite, SWT.NONE, "IsSynchronized :");
checkbox.setToolTipText("Indicates if the table is synchronized with queries");
final IObservableValue isSynchronizedObservable = new EMFObservableValue(this.tableEditorInput.getPapyrusTableInstance(), PapyrustableinstancePackage.eINSTANCE.getPapyrusTableInstance_IsSynchronized(), getEditingDomain());
checkbox.setModelObservable(isSynchronizedObservable);
GridData checkboxGridData = new GridData();
checkboxGridData.grabExcessHorizontalSpace = true;
checkboxGridData.horizontalAlignment = SWT.FILL;
checkbox.setLayoutData(checkboxGridData);
}
protected void createDescription(final Composite parent) {
final TableInstance table = this.tableEditorInput.getPapyrusTableInstance().getTable();
EClass tableEClass = table.eClass();
//we display the description of the table
final StringEditor descriptionEditor = new StringEditor(parent, SWT.MULTI);
descriptionEditor.setLabel(Messages.NatTableEditor_TaleDescriptionLabel);
descriptionEditor.setToolTipText(Messages.NatTableEditor_TableDescriptionToolTip);
EStructuralFeature myFeature = tableEClass.getEStructuralFeature(TableinstancePackage.TABLE_INSTANCE__DESCRIPTION);
EMFObservableValue observable = new EMFObservableValue(table, myFeature, getEditingDomain());
descriptionEditor.setModelObservable(observable);
//set the layout for the description editor
GridData descriptionGridData = new GridData();
descriptionGridData.grabExcessHorizontalSpace = true;
descriptionGridData.horizontalAlignment = SWT.FILL;
descriptionEditor.setLayoutData(descriptionGridData);
}
@Override
public void dispose() {
if(this.natTableWidget.getTableInstance().getContext() != null) {//can be null when we are destroying the context (and the table!)
this.natTableWidget.getTableInstance().getContext().eAdapters().remove(this.contextListener);
}
super.dispose();
}
@Override
public void setFocus() {
this.natTableWidget.getComposite().setFocus();
}
@Override
public boolean isDirty() {
return ((BasicCommandStack)this.editingDomain.getCommandStack()).isSaveNeeded();
}
@Override
public boolean isSaveAsAllowed() {
return true;
}
@Override
public void doSave(final IProgressMonitor monitor) {
this.natTableWidget.save();
firePropertyChange(PROP_DIRTY);
}
@Override
public void doSaveAs() {
this.natTableWidget.saveAs();
firePropertyChange(PROP_DIRTY);
}
public void addSelectionChangedListener(final ISelectionChangedListener listener) {
this.natTableWidget.addSelectionChangedListener(listener);
}
public ISelection getSelection() {
/*
* if nattable has the focus : we retun the nattable selection
* if not, we return the papyrus table instance
*/
Control[] children = ((Composite)this.natTableWidget).getChildren();
if(children.length != 0) {
boolean focus = children[0].isFocusControl();
if(focus) {
return this.natTableWidget.getSelection();
}
}
return new StructuredSelection(this.tableEditorInput.getPapyrusTableInstance());
}
public EditingDomain getEditingDomain() {
return this.editingDomain;
}
public void removeSelectionChangedListener(final ISelectionChangedListener listener) {
this.natTableWidget.removeSelectionChangedListener(listener);
}
public void setSelection(final ISelection selection) {
this.natTableWidget.setSelection(selection);
}
/**
* This sets up the editing domain for the model editor
*/
protected void initializeEditingDomain() {
if(this.editingDomain == null) {
ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
BasicCommandStack commandStack = new BasicCommandStack();
this.editingDomain = new AdapterFactoryEditingDomain(adapterFactory, commandStack, new HashMap<Resource, Boolean>());
}
this.editingDomain.getCommandStack().removeCommandStackListener(this.commandListener);
this.editingDomain.getCommandStack().addCommandStackListener(this.commandListener);
}
public IWorkbenchPart getPart() {
return this;
}
/**
* This ocnverter is used by the LabelEditor (used to display the context)
*
*
*
*/
private class ContextLabelConverter implements IConverter {
/**
*
* @see org.eclipse.core.databinding.conversion.IConverter#getToType()
*
* @return
*/
public Object getToType() {
return String.class;
}
/**
*
* @see org.eclipse.core.databinding.conversion.IConverter#getFromType()
*
* @return
*/
public Object getFromType() {
return Object.class;
}
/**
*
* @see org.eclipse.core.databinding.conversion.IConverter#convert(java.lang.Object)
*
* @param fromObject
* @return
*/
public Object convert(final Object fromObject) {
if(fromObject instanceof EObject) {
return UMLUtil.getQualifiedText((EObject)fromObject);
}
return ""; //$NON-NLS-1$
}
}
}