/******************************************************************************* * Copyright (c) 2014 Obeo. * 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.emf.compare.ide.ui.internal.logical.view; import com.google.common.base.Throwables; import java.util.Collection; import java.util.Collections; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages; import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIPlugin; import org.eclipse.emf.compare.ide.ui.internal.preferences.EMFCompareUIPreferences; import org.eclipse.emf.compare.ide.ui.internal.progress.JobProgressInfoComposite; import org.eclipse.emf.compare.ide.ui.internal.progress.JobProgressMonitorWrapper; import org.eclipse.emf.compare.ide.ui.logical.SynchronizationModel; import org.eclipse.emf.compare.rcp.ui.internal.util.SWTUtil; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ISelection; 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.Display; import org.eclipse.ui.IActionBars; import org.eclipse.ui.ISelectionListener; import org.eclipse.ui.ISelectionService; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.navigator.CommonNavigator; import org.eclipse.ui.navigator.CommonViewer; /** * A simple view displaying the resources of a logical model. * * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> */ public class LogicalModelView extends CommonNavigator { /** Container widget of table and progress bar. */ private Composite container; /** The viewer displaying resources. */ private CommonViewer viewer; /** The content provider of the viewer. */ private LogicalModelViewContentProvider viewContentProvider; /** Display progress bar while computing logical model. */ private JobProgressInfoComposite progressInfoItem; /** Job executed when the selection changed. */ private SelectionChangedJob selectionChangedTask; /** Synchronize the viewer with editors and selection. */ private IAction synchronizeAction; /** Show files in flat list. */ private Action listPresentationAction; /** Show folder structure in full tree. */ private Action treePresentationAction; /** Selection listener. */ private ListenToSelection listenToSelection; /** The service that tracks selection changes. */ private ISelectionService selectionService; /** Keeps track of the last active workbench part. */ private IWorkbenchPart lastPart; /** Keeps track of the last selection. */ private ISelection lastSelection; /** Keeps track of the synchronization button state. */ private boolean synchroActive; /** Default presentation for the viewer. */ private Presentation presentation = Presentation.LIST; /** The preference store of the EMF Compare IDE UI plugin. */ private final IPreferenceStore store = EMFCompareIDEUIPlugin.getDefault().getPreferenceStore(); /** The listener of changes for the preference store of the EMF Compare IDE UI plugin. */ private IPropertyChangeListener preferenceStoreListener = new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { if (synchroActive && EMFCompareUIPreferences.RESOLUTION_SCOPE_PREFERENCE == event.getProperty() && !event.getOldValue().equals(event.getNewValue())) { selectionChangedTask.schedule(); } } }; /** * Presentation mode of the viewer. */ public enum Presentation { /** Show files in flat list. */ LIST, /** Show folder structure in full tree. */ TREE; } /* * (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ @Override public void createPartControl(Composite parent) { container = new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(); layout.numColumns = 1; container.setLayout(layout); super.createPartControl(container); } /* * (non-Javadoc) * @see * org.eclipse.ui.navigator.CommonNavigator#createCommonViewerObject(org.eclipse.swt.widgets.Composite) */ @Override protected CommonViewer createCommonViewerObject(Composite aParent) { selectionChangedTask = new SelectionChangedJob( EMFCompareIDEUIMessages.getString("LogicalModelView.computingLogicalModel")); //$NON-NLS-1$ selectionChangedTask.setPriority(Job.LONG); progressInfoItem = new JobProgressInfoComposite(selectionChangedTask, aParent, SWT.SMOOTH | SWT.HORIZONTAL, SWT.NONE); progressInfoItem.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); progressInfoItem.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); viewer = super.createCommonViewerObject(aParent); viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); viewContentProvider = new LogicalModelViewContentProvider(this); viewer.setContentProvider(viewContentProvider); viewer.setLabelProvider(new LogicalModelViewLabelProvider(this)); viewer.setInput(ResourcesPlugin.getWorkspace().getRoot()); updateLayout(false, false); IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); selectionService = activeWorkbenchWindow.getSelectionService(); makeActions(); fillToolbar(); return viewer; } /* * (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart#setFocus() */ @Override public void setFocus() { if (getCommonViewer().getControl().isVisible()) { getCommonViewer().getControl().setFocus(); } else { progressInfoItem.setFocus(); } } /* * (non-Javadoc) * @see org.eclipse.ui.part.WorkbenchPart#dispose() */ @Override public void dispose() { if (listenToSelection != null) { selectionService.removePostSelectionListener(listenToSelection); } if (!selectionChangedTask.cancel()) { try { selectionChangedTask.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); Throwables.propagate(e); } } super.dispose(); } /** * Get the presentation of the viewer. * * @return selected presentation */ Presentation getPresentation() { return presentation; } /** * Create actions. */ @SuppressWarnings("static-access") private void makeActions() { if (listenToSelection == null) { listenToSelection = new ListenToSelection(); } selectionService.addPostSelectionListener(listenToSelection); String synchronizationLabel = EMFCompareIDEUIMessages .getString("LogicalModelView.linkWithEditorAndSelection"); //$NON-NLS-1$ synchronizeAction = new Action(synchronizationLabel, IAction.AS_CHECK_BOX) { @Override public void run() { if (isChecked()) { synchroActive = true; store.addPropertyChangeListener(preferenceStoreListener); listenToSelection.selectionChanged(lastPart, lastSelection); } else { synchroActive = false; store.removePropertyChangeListener(preferenceStoreListener); } } }; synchronizeAction.setToolTipText(synchronizationLabel); synchronizeAction.setImageDescriptor( EMFCompareIDEUIPlugin.getDefault().getImageDescriptor("icons/full/eobj16/synced.gif")); //$NON-NLS-1$ listPresentationAction = new Action( EMFCompareIDEUIMessages.getString("LogicalModelView.listPresentation.title"), //$NON-NLS-1$ IAction.AS_RADIO_BUTTON) { @Override public void run() { if (!isChecked()) { return; } presentation = Presentation.LIST; treePresentationAction.setChecked(false); SWTUtil.safeSyncExec(new Runnable() { public void run() { getCommonViewer().refresh(); } }); } }; listPresentationAction.setImageDescriptor( EMFCompareIDEUIPlugin.getDefault().getImageDescriptor("icons/full/eobj16/flatLayout.gif")); //$NON-NLS-1$ treePresentationAction = new Action( EMFCompareIDEUIMessages.getString("LogicalModelView.treePresentation.title"), //$NON-NLS-1$ IAction.AS_RADIO_BUTTON) { @Override public void run() { if (!isChecked()) { return; } presentation = Presentation.TREE; listPresentationAction.setChecked(false); SWTUtil.safeSyncExec(new Runnable() { public void run() { getCommonViewer().refresh(); } }); } }; treePresentationAction.setImageDescriptor(EMFCompareIDEUIPlugin.getDefault() .getImageDescriptor("icons/full/eobj16/hierarchicalLayout.gif")); //$NON-NLS-1$ } /** * Contribute actions to the toolbar. */ private void fillToolbar() { IActionBars bars = getViewSite().getActionBars(); bars.getToolBarManager().add(synchronizeAction); IMenuManager dropdownMenu = bars.getMenuManager(); listPresentationAction.setChecked(true); dropdownMenu.add(listPresentationAction); dropdownMenu.add(treePresentationAction); } /** * Switch the display between the progress bar and the viewer. * * @param displayProgress * true to display the progress bar, false to display the viewer. * @param doLayout * true to layout the container of the progress bar and the viewer. */ private void updateLayout(boolean displayProgress, boolean doLayout) { ((GridData)progressInfoItem.getLayoutData()).exclude = !displayProgress; progressInfoItem.setVisible(displayProgress); ((GridData)viewer.getControl().getLayoutData()).exclude = displayProgress; viewer.getControl().setVisible(!displayProgress); if (doLayout) { container.layout(true, true); } } /** * Listener that reacts on selection changes. * * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> */ private final class ListenToSelection implements ISelectionListener { /** * Notifies this listener that the selection has changed. * * @param part * the workbench part containing the selection. * @param selection * the current selection. */ public void selectionChanged(IWorkbenchPart part, ISelection selection) { if (!(part instanceof LogicalModelView)) { lastPart = part; lastSelection = selection; } if (synchroActive && selection != null && !selection.isEmpty() && !selection.equals(selectionChangedTask.getSelection())) { selectionChangedTask.setPart(part); selectionChangedTask.setSelection(selection); selectionChangedTask.schedule(); } } } /** * Job executed on selection changes. * * @author <a href="mailto:axel.richard@obeo.fr">Axel Richard</a> */ private final class SelectionChangedJob extends Job { /** * The workbench part from which the selection occurs. */ private IWorkbenchPart part; /** * The selection. */ private ISelection selection; /** * Constructor. * * @param name * the job's name. */ private SelectionChangedJob(String name) { super(name); } /** * Set the workbench part. * * @param part * the workbench part. */ public void setPart(IWorkbenchPart part) { this.part = part; } /** * Get the last selection associated with this job. * * @return the last selection associated with this job. */ public ISelection getSelection() { return selection; } /** * Set a new selection to associate with this job. * * @param selection * the selection. */ public void setSelection(ISelection selection) { this.selection = selection; } /** * Compute the logical model from the IFile corresponding to the selection, and then display those * resources in the viewer. * * @param monitor * to monitor the whole process. * @return the status of the whole process. */ @Override public IStatus run(IProgressMonitor monitor) { IStatus status = Status.OK_STATUS; IProgressMonitor wrapper = new JobProgressMonitorWrapper(monitor, progressInfoItem); SubMonitor subMonitor = SubMonitor.convert(wrapper, 100); final ILogicalModelViewHandler handler = EMFCompareIDEUIPlugin.getDefault() .getLogicalModelViewHandlerRegistry().getBestHandlerFor(part, selection); if (handler != null) { // Display progress bar SWTUtil.safeSyncExec(new Runnable() { public void run() { if (!container.isDisposed()) { updateLayout(true, true); } } }); // Retrieve logical models final Collection<SynchronizationModel> logicalModels = handler.getSynchronizationModels(part, selection, subMonitor.newChild(50)); for (SynchronizationModel logicalModel : logicalModels) { Diagnostic diagnostic = logicalModel.getDiagnostic(); if (diagnostic != null && diagnostic.getSeverity() != Diagnostic.OK) { if (status != Status.CANCEL_STATUS) { SWTUtil.safeSyncExec(new Runnable() { public void run() { MessageDialog.openError(LogicalModelView.this.getSite().getShell(), EMFCompareIDEUIMessages .getString("LogicalModelView.errorDialog.title"), //$NON-NLS-1$ EMFCompareIDEUIMessages .getString("LogicalModelView.errorDialog.message")); //$NON-NLS-1$ } }); status = Status.CANCEL_STATUS; } } } // Retrieve resources from logical models final Collection<IResource> resources; if (status == Status.OK_STATUS) { resources = LogicalModelViewHandlerUtil.getLogicalModelResources(logicalModels, subMonitor.newChild(50)); } else { resources = Collections.emptySet(); } // Display resources in viewer if (!monitor.isCanceled()) { SWTUtil.safeSyncExec(new Runnable() { public void run() { updateLayout(false, true); viewContentProvider.setLeaves(resources); getCommonViewer().refresh(); } }); } else { status = Status.CANCEL_STATUS; } } return status; } } }