| /******************************************************************************* |
| * Copyright (c) 2008, 2017 THALES GLOBAL SERVICES and others. |
| * 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: |
| * Obeo - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.sirius.table.ui.tools.internal.editor; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.emf.common.notify.AdapterFactory; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.edit.provider.ComposedImage; |
| import org.eclipse.emf.edit.provider.IDisposable; |
| import org.eclipse.jface.dialogs.IMessageProvider; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.dialogs.ProgressMonitorDialog; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.TreePath; |
| import org.eclipse.jface.viewers.TreeSelection; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.jface.window.Window; |
| import org.eclipse.sirius.business.api.dialect.command.RefreshRepresentationsCommand; |
| import org.eclipse.sirius.common.tools.DslCommonPlugin; |
| import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; |
| import org.eclipse.sirius.table.metamodel.table.DTable; |
| import org.eclipse.sirius.table.metamodel.table.provider.TableUIPlugin; |
| import org.eclipse.sirius.table.tools.api.command.ITableCommandFactory; |
| import org.eclipse.sirius.table.tools.api.command.ITableCommandFactoryProvider; |
| import org.eclipse.sirius.table.tools.api.command.TableCommandFactoryService; |
| import org.eclipse.sirius.table.ui.tools.api.editor.DTableEditor; |
| import org.eclipse.sirius.table.ui.tools.internal.commands.EMFCommandFactoryUI; |
| import org.eclipse.sirius.table.ui.tools.internal.editor.provider.DTableEditorUtil; |
| import org.eclipse.sirius.tools.api.interpreter.InterpreterRegistry; |
| import org.eclipse.sirius.tools.api.profiler.SiriusTasksKey; |
| import org.eclipse.sirius.ui.business.api.descriptor.ComposedImageDescriptor; |
| import org.eclipse.sirius.ui.business.api.dialect.DialectUIManager; |
| import org.eclipse.sirius.ui.business.api.dialect.marker.TraceabilityMarkerNavigationProvider; |
| import org.eclipse.sirius.ui.business.api.session.SessionEditorInput; |
| import org.eclipse.sirius.ui.tools.internal.editor.AbstractDTableViewerManager; |
| import org.eclipse.sirius.ui.tools.internal.editor.AbstractDTreeEditor; |
| import org.eclipse.sirius.viewpoint.DRepresentation; |
| import org.eclipse.sirius.viewpoint.provider.SiriusEditPlugin; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.graphics.Image; |
| 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.Label; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.TreeItem; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorMatchingStrategy; |
| import org.eclipse.ui.IEditorReference; |
| import org.eclipse.ui.IEditorSite; |
| import org.eclipse.ui.IFileEditorInput; |
| import org.eclipse.ui.IPartListener; |
| import org.eclipse.ui.PartInitException; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.actions.WorkspaceModifyOperation; |
| import org.eclipse.ui.contexts.IContextService; |
| import org.eclipse.ui.dialogs.SaveAsDialog; |
| import org.eclipse.ui.part.FileEditorInput; |
| |
| /** |
| * Provides generic support for DTable editors. <BR> |
| * Clients may extend this class. |
| * |
| * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> |
| * |
| */ |
| public abstract class AbstractDTableEditor extends AbstractDTreeEditor implements DTableEditor { |
| |
| /** The contributor ID */ |
| private static final String CONTRIBUTOR_ID = "org.eclipse.sirius.table.ui.EditorID"; //$NON-NLS-1$ |
| |
| /** the context ID. */ |
| private static final String CONTEXT_ID = CONTRIBUTOR_ID + ".tableContext"; //$NON-NLS-1$ |
| |
| /** This DTable model */ |
| private DTable tableModel; |
| |
| private IPartListener refreshAtOpeningActivator; |
| |
| @Override |
| public void doSave(final IProgressMonitor progressMonitor) { |
| if (isDeleted(getEditorInput())) { |
| if (isSaveAsAllowed()) { |
| /* |
| * 1GEUSSR: ITPUI:ALL - User should never loose changes made in the editors. Changed Behavior to make |
| * sure that if called inside a regular save (because of deletion of input element) there is a way to |
| * report back to the caller. |
| */ |
| performSaveAs(progressMonitor); |
| } else { |
| final Shell shell = getSite().getShell(); |
| final String title = Messages.dTableEditor_ErrorSaveDeletedTitle; |
| final String msg = Messages.dTableEditor_ErrorSaveDeletedMessage; |
| MessageDialog.openError(shell, title, msg); |
| } |
| |
| } else { |
| // Update the readonly state |
| // updateState(getEditorInput()); |
| // Valide the state |
| // validateState(getEditorInput()); |
| performSave(false, progressMonitor); |
| } |
| // Indicates that the editor is saved |
| } |
| |
| /** |
| * Perform the saveAs action. |
| * |
| * @param progressMonitor |
| * The progress monitor |
| */ |
| private void performSaveAs(final IProgressMonitor progressMonitor) { |
| final Shell shell = getSite().getShell(); |
| final IEditorInput input = getEditorInput(); |
| final SaveAsDialog dialog = new SaveAsDialog(shell); |
| final IFile original = input instanceof IFileEditorInput ? ((IFileEditorInput) input).getFile() : null; |
| if (original != null) { |
| dialog.setOriginalFile(original); |
| } |
| dialog.create(); |
| if (isDeleted(input) && original != null) { |
| final String message = MessageFormat.format(Messages.dTableEditor_SavingDeletedFile, original.getName()); |
| dialog.setErrorMessage(null); |
| dialog.setMessage(message, IMessageProvider.WARNING); |
| } |
| if (dialog.open() == Window.CANCEL) { |
| if (progressMonitor != null) { |
| progressMonitor.setCanceled(true); |
| } |
| } else { |
| final IPath filePath = dialog.getResult(); |
| if (filePath == null) { |
| if (progressMonitor != null) { |
| progressMonitor.setCanceled(true); |
| } |
| return; |
| } |
| final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); |
| final IFile file = workspaceRoot.getFile(filePath); |
| final IEditorInput newInput = new FileEditorInput(file); |
| // Check if the editor is already open |
| final IEditorMatchingStrategy matchingStrategy = getEditorDescriptor().getEditorMatchingStrategy(); |
| final IEditorReference[] editorRefs = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getEditorReferences(); |
| for (IEditorReference editorRef : editorRefs) { |
| if (matchingStrategy.matches(editorRef, newInput)) { |
| MessageDialog.openWarning(shell, Messages.dTableEditor_SaveAsErrorTitle, Messages.dTableEditor_SaveAsErrorMessage); |
| return; |
| } |
| } |
| final boolean success = false; |
| if (progressMonitor != null) { |
| progressMonitor.setCanceled(!success); |
| } |
| } |
| } |
| |
| @Override |
| public void doSaveAs() { |
| if (isSaveAsAllowed()) { |
| try { |
| new ProgressMonitorDialog(PlatformUI.getWorkbench().getDisplay().getActiveShell()).run(false, false, new WorkspaceModifyOperation() { |
| |
| @Override |
| protected void execute(IProgressMonitor monitor) throws CoreException, InvocationTargetException, InterruptedException { |
| performSaveAs(monitor); |
| } |
| }); |
| |
| } catch (final InterruptedException e) { |
| IStatus status = new Status(IStatus.ERROR, TableUIPlugin.ID, e.getLocalizedMessage(), e); |
| TableUIPlugin.getPlugin().getLog().log(status); |
| } catch (final InvocationTargetException e) { |
| IStatus status = new Status(IStatus.ERROR, TableUIPlugin.ID, e.getLocalizedMessage(), e.getTargetException()); |
| TableUIPlugin.getPlugin().getLog().log(status); |
| } |
| } |
| } |
| |
| @Override |
| public void init(final IEditorSite site, final IEditorInput input) throws PartInitException { |
| super.init(site, input); |
| |
| if (getTableModel() != null) { |
| // Launch the refresh if needed |
| if (DialectUIManager.INSTANCE.isRefreshActivatedOnRepresentationOpening()) { |
| launchRefresh(); |
| } |
| |
| // In case of shared table representation, we notify the use of |
| // table |
| // representation locking |
| initPermissionAuthority(getTableModel()); |
| } |
| } |
| |
| @Override |
| protected void configureCommandFactoryProviders() { |
| /* get IEMFCommandFactories */ |
| emfCommandFactory = TableCommandFactoryService.getInstance().getNewProvider().getCommandFactory(getEditingDomain()); |
| |
| /* We add a callback for UI stuffs */ |
| emfCommandFactory.setUserInterfaceCallBack(new EMFCommandFactoryUI()); |
| } |
| |
| @Override |
| public void createPartControl(final Composite parent) { |
| // Display the status message to inform user about reason why the |
| // session opening failed |
| if (session == null && getEditorInput() instanceof SessionEditorInput) { |
| SessionEditorInput sessionEditorInput = (SessionEditorInput) getEditorInput(); |
| IStatus status = sessionEditorInput.getStatus(); |
| if (status.getSeverity() >= IStatus.ERROR) { |
| Composite composite = new Composite(parent, SWT.NO_FOCUS); |
| control = composite; |
| GridLayout gridLayout = new GridLayout(); |
| composite.setLayout(gridLayout); |
| StyledText widget = new StyledText(composite, SWT.NO_FOCUS | SWT.READ_ONLY | SWT.MULTI | SWT.WRAP); |
| widget.setLineAlignment(0, widget.getLineCount(), SWT.CENTER); |
| widget.setAlignment(SWT.CENTER); |
| String message = MessageFormat.format(org.eclipse.sirius.table.metamodel.table.provider.Messages.AbstractDTableEditor_editorToBeClosedAndReopenedSinceContentIsNotAccessible, |
| status.getMessage()); |
| widget.setText(message); |
| GridData layoutData = new GridData(); |
| layoutData.grabExcessHorizontalSpace = true; |
| layoutData.grabExcessVerticalSpace = true; |
| layoutData.horizontalAlignment = SWT.CENTER; |
| layoutData.verticalAlignment = SWT.CENTER; |
| widget.setLayoutData(layoutData); |
| return; |
| } |
| } |
| super.createPartControl(parent); |
| |
| DslCommonPlugin.PROFILER.startWork(SiriusTasksKey.CREATE_TABLE_KEY); |
| if (getTableModel() == null) { |
| /* eclipse was closed with an editor opened and not saved */ |
| final Label errorLabel = new Label(parent, SWT.CENTER); |
| errorLabel.setText(org.eclipse.sirius.table.metamodel.table.provider.Messages.AbstractDTableEditor_tableNotSaved); |
| return; |
| } |
| treeViewerManager = new DTableViewerManager(parent, getTableModel(), getEditingDomain(), accessor, (ITableCommandFactory) emfCommandFactory, this); |
| DslCommonPlugin.PROFILER.stopWork(SiriusTasksKey.CREATE_TABLE_KEY); |
| getSite().setSelectionProvider(treeViewerManager.getTreeViewer()); |
| /* initialize interpreter. */ |
| if (session != null) { |
| InterpreterRegistry.prepareImportsFromSession(session.getInterpreter(), session); |
| } |
| |
| refreshAtOpeningActivator = new RefreshAtOpeningActivator(session, this); |
| getSite().getPage().addPartListener(refreshAtOpeningActivator); |
| |
| // Activate the context for this site. |
| IContextService contextService = getSite().getService(IContextService.class); |
| contextService.activateContext(CONTEXT_ID); |
| } |
| |
| /** |
| * Overridden to update the UI part when the {@link DTable} model is changed outside of a EMF Command (which notify |
| * DTableContentAdapter) in case of collab model. |
| * |
| * {@inheritDoc} |
| */ |
| @Override |
| public void setInput(IEditorInput input) { |
| super.setInput(input); |
| |
| // Make sure that the viewer columns are up-to-date |
| if (treeViewerManager != null) { |
| enablePropertiesUpdate(false); |
| DTableEditorUtil.updateViewerColumns(this.treeViewerManager, this.getTableModel()); |
| enablePropertiesUpdate(true); |
| } |
| } |
| |
| @Override |
| public Image getFrozenRepresentationImage() { |
| if (frozenRepresentationImage == null || frozenRepresentationImage.isDisposed()) { |
| Image refreshImage = TableUIPlugin.Implementation.getImage(TableUIPlugin.Implementation.getBundledImageDescriptor("icons/" + DTableViewerManager.REFRESH_IMG + ".gif")); //$NON-NLS-1$ //$NON-NLS-2$ |
| List<Object> images = new ArrayList<Object>(2); |
| images.add(refreshImage); |
| Image lockByOtherOverlayIamge = SiriusEditPlugin.getPlugin().getImage(SiriusEditPlugin.Implementation.getBundledImageDescriptor("icons/full/decorator/permission_denied_overlay.gif")); //$NON-NLS-1$ |
| images.add(lockByOtherOverlayIamge); |
| ImageDescriptor composedImageDescriptor = new ComposedImageDescriptor(new ComposedImage(images)); |
| frozenRepresentationImage = SiriusEditPlugin.getPlugin().getImage(composedImageDescriptor); |
| } |
| return frozenRepresentationImage; |
| } |
| |
| @Override |
| public Control getControl() { |
| if (control == null) { |
| AbstractDTableViewerManager tableViewer = getTableViewer(); |
| if (tableViewer != null) { |
| TreeViewer treeViewer = tableViewer.getTreeViewer(); |
| control = treeViewer.getTree(); |
| } |
| } |
| return control; |
| } |
| |
| @Override |
| protected void launchRefresh() { |
| getEditingDomain().getCommandStack().execute(new RefreshRepresentationsCommand(getEditingDomain(), new NullProgressMonitor(), getRepresentation())); |
| } |
| |
| @Override |
| public String getContributorId() { |
| return CONTRIBUTOR_ID; |
| } |
| |
| @Override |
| protected void setRepresentation(URI uri, boolean loadOnDemand) { |
| setTableModel(getDTable(uri, loadOnDemand)); |
| } |
| |
| /** |
| * Get the DTable corresponding to this URI |
| * |
| * @param uri |
| * the URI to resolve. |
| * @param loadOnDemand |
| * whether to create and load the resource, if it doesn't already exists. |
| * @return the DTable resource resolved by the URI, or <code>null</code> if there isn't one and it's not being |
| * demand loaded. |
| */ |
| private DTable getDTable(final URI uri, final boolean loadOnDemand) { |
| DTable result = null; |
| final Resource resource = getEditingDomain().getResourceSet().getResource(uri.trimFragment(), loadOnDemand); |
| if (resource != null && resource.isLoaded()) { |
| if (uri.fragment() != null) { |
| final EObject rootElement = resource.getEObject(uri.fragment()); |
| if (rootElement instanceof DTable) { |
| result = (DTable) rootElement; |
| } |
| } |
| } |
| return result; |
| } |
| |
| @Override |
| public DRepresentation getRepresentation() { |
| return tableModel; |
| } |
| |
| /** |
| * Get the table model. |
| * |
| * @return the table model |
| */ |
| public DTable getTableModel() { |
| return tableModel; |
| } |
| |
| /** |
| * Set the tableModel. |
| * |
| * @param tableModel |
| * the tableModel to set |
| */ |
| protected void setTableModel(final DTable tableModel) { |
| this.tableModel = tableModel; |
| } |
| |
| @Override |
| public void validateRepresentation() { |
| // TODO implement validation for Table Editor. |
| } |
| |
| @Override |
| protected void setAccessor(ModelAccessor accessor) { |
| super.setAccessor(accessor); |
| ((ITableCommandFactory) emfCommandFactory).setModelAccessor(this.accessor); |
| } |
| |
| @Override |
| public void gotoMarker(IMarker marker) { |
| if (TraceabilityMarkerNavigationProvider.isTraceabilityMarker(marker)) { |
| new TraceabilityMarkerNavigationProvider(this).gotoMarker(marker); |
| } else { |
| doGoToMarker(marker); |
| } |
| } |
| |
| /** |
| * Sets the cursor and selection state for an editor to reveal the position of the given marker. |
| * |
| * @param marker |
| * the marker to go to |
| */ |
| // TODO for now on, this implementation only allow to handle Traceability |
| // markers |
| protected void doGoToMarker(IMarker marker) { |
| final String tableURI = marker.getAttribute(TraceabilityMarkerNavigationProvider.REPRESENTATION_URI, null); |
| final String elementId = marker.getAttribute(TraceabilityMarkerNavigationProvider.REPRESENTATION_ELEMENT_ID, null); |
| |
| if (tableURI == null || elementId == null) { |
| return; |
| } |
| |
| final URI markerTableURI = URI.createURI(tableURI); |
| final DTable markerTable = (DTable) this.getTableModel().eResource().getEObject(markerTableURI.fragment()); |
| |
| if (markerTable != null) { |
| EObject searchedElement = markerTable.eResource().getEObject(elementId); |
| TreeItem contains = contains(this.getTableViewer().getTreeViewer().getTree().getItems(), searchedElement); |
| if (contains != null) { |
| ISelection selection = new TreeSelection(getTreePathFromItem(contains)); |
| // FIXME this always throws a Runtime Exception because Tree is |
| // busy. However, tree is correctly expanded |
| this.getTableViewer().getTreeViewer().setSelection(selection); |
| } |
| } |
| } |
| |
| /** |
| * Returns the tree path for the given item. |
| * |
| * @param item |
| * the item from which obtain the path |
| * @return {@link TreePath} |
| * |
| * @since 0.9.0 |
| */ |
| protected TreePath getTreePathFromItem(TreeItem item) { |
| LinkedList<Object> segments = new LinkedList<Object>(); |
| TreeItem myItem = item; |
| while (myItem != null) { |
| Object segment = item.getData(); |
| Assert.isNotNull(segment); |
| segments.addFirst(segment); |
| myItem = myItem.getParentItem(); |
| } |
| return new TreePath(segments.toArray()); |
| } |
| |
| @Override |
| public Object getAdapter(Class type) { |
| Object result = super.getAdapter(type); |
| if (result == null) { |
| if (type == ITableCommandFactoryProvider.class) { |
| result = emfCommandFactory; |
| } |
| } |
| return result; |
| } |
| |
| @Override |
| public AdapterFactory getAdapterFactory() { |
| if (adapterFactory == null) { |
| // Create an adapter factory that yields item providers. |
| adapterFactory = TableUIPlugin.getPlugin().createAdapterFactory(); |
| } |
| return adapterFactory; |
| } |
| |
| @Override |
| public void dispose() { |
| if (getAdapterFactory() instanceof IDisposable) { |
| ((IDisposable) getAdapterFactory()).dispose(); |
| } |
| super.dispose(); |
| if (refreshAtOpeningActivator != null) { |
| getSite().getPage().removePartListener(refreshAtOpeningActivator); |
| refreshAtOpeningActivator = null; |
| } |
| } |
| |
| @Override |
| protected void updateEditorAfterAirdResourceReload() { |
| // We update all components that keeps Aird proxified element after an |
| // Aird resource reload. |
| treeViewerManager.updateDRepresentation(getTableModel()); |
| getSite().getPage().removePartListener(refreshAtOpeningActivator); |
| refreshAtOpeningActivator = new RefreshAtOpeningActivator(session, this); |
| getSite().getPage().addPartListener(refreshAtOpeningActivator); |
| treeViewerManager.getTreeViewer().refresh(); |
| } |
| |
| } |