| /******************************************************************************* |
| * Copyright (c) 2006, 2017 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.team.ui; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| |
| import org.eclipse.compare.CompareConfiguration; |
| import org.eclipse.compare.CompareEditorInput; |
| import org.eclipse.compare.CompareUI; |
| import org.eclipse.compare.CompareViewerPane; |
| import org.eclipse.compare.CompareViewerSwitchingPane; |
| import org.eclipse.compare.IContentChangeListener; |
| import org.eclipse.compare.IContentChangeNotifier; |
| import org.eclipse.compare.IPropertyChangeNotifier; |
| import org.eclipse.compare.ITypedElement; |
| import org.eclipse.compare.Splitter; |
| import org.eclipse.compare.contentmergeviewer.IFlushable; |
| import org.eclipse.compare.internal.CompareEditor; |
| import org.eclipse.compare.internal.CompareEditorInputNavigator; |
| import org.eclipse.compare.structuremergeviewer.ICompareInput; |
| import org.eclipse.core.runtime.Adapters; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jface.action.ToolBarManager; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.Viewer; |
| 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.Shell; |
| import org.eclipse.team.internal.ui.TeamUIMessages; |
| import org.eclipse.team.internal.ui.Utils; |
| import org.eclipse.team.internal.ui.synchronize.LocalResourceTypedElement; |
| import org.eclipse.team.internal.ui.synchronize.SynchronizePageConfiguration; |
| import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.progress.IProgressService; |
| |
| /** |
| * Abstract class for hosting a page based structure input view for the purposes |
| * of feeding compare viewers. |
| * <p> |
| * |
| * @since 3.2 |
| * @noextend This class is not intended to be subclassed by clients outside of the Team framework. |
| * @deprecated Clients should use a subclass of {@link CompareEditorInput} |
| * and {@link CompareUI#openCompareDialog(org.eclipse.compare.CompareEditorInput)} |
| */ |
| @Deprecated |
| public abstract class PageSaveablePart extends SaveablePartAdapter implements IContentChangeListener{ |
| |
| private CompareConfiguration cc; |
| Shell shell; |
| |
| // Tracking of dirty state |
| private boolean fDirty= false; |
| private ArrayList<Object> fDirtyViewers= new ArrayList<>(); |
| private IPropertyChangeListener fDirtyStateListener; |
| |
| // SWT controls |
| private CompareViewerSwitchingPane fContentPane; |
| private CompareViewerPane fEditionPane; |
| private CompareViewerSwitchingPane fStructuredComparePane; |
| private Control control; |
| |
| // Configuration options |
| private boolean showContentPanes = true; |
| |
| /** |
| * Create a saveable part. |
| * @param shell the shell for the part |
| * @param compareConfiguration the compare configuration |
| */ |
| protected PageSaveablePart(Shell shell, CompareConfiguration compareConfiguration){ |
| this.shell = shell; |
| this.cc = compareConfiguration; |
| |
| fDirtyStateListener= e -> { |
| String propertyName= e.getProperty(); |
| if (CompareEditorInput.DIRTY_STATE.equals(propertyName)) { |
| boolean changed= false; |
| Object newValue= e.getNewValue(); |
| if (newValue instanceof Boolean) |
| changed= ((Boolean)newValue).booleanValue(); |
| setDirty(e.getSource(), changed); |
| } |
| }; |
| } |
| |
| @Override |
| public boolean isDirty() { |
| return fDirty || fDirtyViewers.size() > 0; |
| } |
| |
| @Override |
| public void createPartControl(Composite parent) { |
| Composite composite = new Composite(parent, SWT.NULL); |
| GridLayout layout = new GridLayout(); |
| layout.marginHeight = 0; |
| layout.marginWidth = 0; |
| layout.verticalSpacing = 0; |
| GridData data = new GridData(GridData.FILL_BOTH); |
| data.grabExcessHorizontalSpace = true; |
| composite.setLayout(layout); |
| composite.setLayoutData(data); |
| |
| shell = parent.getShell(); |
| |
| Splitter vsplitter = new Splitter(composite, SWT.VERTICAL); |
| vsplitter.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL)); |
| // we need two panes: the left for the elements, the right one for the structured diff |
| Splitter hsplitter = new Splitter(vsplitter, SWT.HORIZONTAL); |
| fEditionPane = new CompareViewerPane(hsplitter, SWT.BORDER | SWT.FLAT); |
| fStructuredComparePane = new CompareViewerSwitchingPane(hsplitter, SWT.BORDER | SWT.FLAT, true) { |
| @Override |
| protected Viewer getViewer(Viewer oldViewer, Object input) { |
| if (input instanceof ICompareInput) |
| return findStructureViewer(this, oldViewer, (ICompareInput)input); |
| return null; |
| } |
| }; |
| fStructuredComparePane.addSelectionChangedListener(e -> feedInput2(e.getSelection())); |
| fEditionPane.setText(TeamUIMessages.ParticipantPageSaveablePart_0); |
| fContentPane = new CompareViewerSwitchingPane(vsplitter, SWT.BORDER | SWT.FLAT) { |
| @Override |
| protected Viewer getViewer(Viewer oldViewer, Object input) { |
| if (!(input instanceof ICompareInput)) |
| return null; |
| Viewer newViewer= findContentViewer(this, oldViewer, (ICompareInput)input); |
| boolean isNewViewer= newViewer != oldViewer; |
| if (isNewViewer && newViewer instanceof IPropertyChangeNotifier) { |
| final IPropertyChangeNotifier dsp= (IPropertyChangeNotifier) newViewer; |
| dsp.addPropertyChangeListener(fDirtyStateListener); |
| Control c= newViewer.getControl(); |
| c.addDisposeListener( |
| e -> dsp.removePropertyChangeListener(fDirtyStateListener) |
| ); |
| hookContentChangeListener((ICompareInput)input); |
| } |
| return newViewer; |
| } |
| }; |
| vsplitter.setWeights(new int[]{30, 70}); |
| |
| control = composite; |
| |
| ToolBarManager toolBarManager = CompareViewerPane.getToolBarManager(fEditionPane); |
| Control c = createPage(fEditionPane, toolBarManager); |
| fEditionPane.setContent(c); |
| |
| if(! showContentPanes) { |
| hsplitter.setMaximizedControl(fEditionPane); |
| } |
| |
| getSelectionProvider().addSelectionChangedListener(event -> { |
| ICompareInput input = getCompareInput(event.getSelection()); |
| if (input != null) |
| prepareCompareInput(input); |
| setInput(input); |
| }); |
| } |
| |
| /** |
| * Return the selection provider for the page. This method is |
| * called after the page is created in order to register a |
| * selection listener on the page. |
| * @return the selection provider for the page |
| */ |
| protected abstract ISelectionProvider getSelectionProvider(); |
| |
| /** |
| * Create the page for this part and return the top level control |
| * for the page. |
| * @param parent the parent composite |
| * @param toolBarManager the toolbar manager for the page |
| * @return the top-level control for the page |
| */ |
| protected abstract Control createPage(Composite parent, ToolBarManager toolBarManager); |
| |
| /** |
| * Set the title of the page's page to the given text. The title |
| * will appear in the header of the pane containing the page. |
| * @param title the page's title |
| */ |
| protected void setPageDescription(String title) { |
| fEditionPane.setText(title); |
| } |
| |
| /** |
| * Set the saveable part's dirty state to the given state. |
| * @param dirty the dirty state |
| */ |
| protected void setDirty(boolean dirty) { |
| boolean confirmSave= true; |
| Object o= cc.getProperty(CompareEditor.CONFIRM_SAVE_PROPERTY); |
| if (o instanceof Boolean) |
| confirmSave= ((Boolean)o).booleanValue(); |
| |
| if (!confirmSave) { |
| fDirty= dirty; |
| if (!fDirty) |
| fDirtyViewers.clear(); |
| } |
| } |
| |
| private void setDirty(Object source, boolean dirty) { |
| Assert.isNotNull(source); |
| if (dirty) |
| fDirtyViewers.add(source); |
| else |
| fDirtyViewers.remove(source); |
| } |
| |
| /** |
| * Feeds input from the page into the content and structured viewers. |
| * @param input the input |
| */ |
| private void setInput(Object input) { |
| CompareViewerPane pane = fContentPane; |
| if (pane != null && !pane.isDisposed()) |
| fContentPane.setInput(input); |
| if (fStructuredComparePane != null && !fStructuredComparePane.isDisposed()) |
| fStructuredComparePane.setInput(input); |
| } |
| |
| /* |
| * Feeds selection from structure viewer to content viewer. |
| */ |
| private void feedInput2(ISelection sel) { |
| ICompareInput input = getCompareInput(sel); |
| prepareCompareInput(input); |
| if (input != null) |
| fContentPane.setInput(input); |
| } |
| |
| /** |
| * Convenience method that calls {@link #prepareInput(ICompareInput, CompareConfiguration, IProgressMonitor)} |
| * with a progress monitor. |
| * @param input the compare input to be prepared |
| */ |
| protected void prepareCompareInput(final ICompareInput input) { |
| if (input == null) |
| return; |
| // Don't allow the use of shared documents with PageSaveableParts |
| Object left = input.getLeft(); |
| if (left instanceof LocalResourceTypedElement) { |
| LocalResourceTypedElement lrte = (LocalResourceTypedElement) left; |
| lrte.enableSharedDocument(false); |
| } |
| IProgressService manager = PlatformUI.getWorkbench().getProgressService(); |
| try { |
| // TODO: we need a better progress story here (i.e. support for cancellation) bug 127075 |
| manager.busyCursorWhile(monitor -> { |
| prepareInput(input, getCompareConfiguration(), monitor); |
| hookContentChangeListener(input); |
| }); |
| } catch (InvocationTargetException e) { |
| Utils.handle(e); |
| } catch (InterruptedException e) { |
| // Ignore |
| } |
| } |
| |
| /** |
| * Prepare the compare input for display in a content viewer. This method is |
| * called from {@link #prepareCompareInput(ICompareInput)} and may be called |
| * from a non-UI thread. This method should not be called by others. |
| * @param input the input |
| * @param configuration the compare configuration |
| * @param monitor a progress monitor |
| * @throws InvocationTargetException |
| */ |
| protected abstract void prepareInput(ICompareInput input, CompareConfiguration configuration, IProgressMonitor monitor) throws InvocationTargetException; |
| |
| private void hookContentChangeListener(ICompareInput node) { |
| // TODO: there is no unhook which may lead to a leak |
| ITypedElement left = node.getLeft(); |
| if(left instanceof IContentChangeNotifier) { |
| ((IContentChangeNotifier)left).addContentChangeListener(this); |
| } |
| ITypedElement right = node.getRight(); |
| if(right instanceof IContentChangeNotifier) { |
| ((IContentChangeNotifier)right).addContentChangeListener(this); |
| } |
| } |
| |
| /** |
| * Return the parent shell of this part. |
| * @return the parent shell of this part |
| */ |
| protected Shell getShell() { |
| return shell; |
| } |
| |
| /** |
| * This method is internal to the framework and should not be called by clients |
| * outside of the framework. |
| */ |
| protected void setNavigator(ISynchronizePageConfiguration configuration) { |
| configuration.setProperty(SynchronizePageConfiguration.P_NAVIGATOR, new CompareEditorInputNavigator( |
| new Object[] { |
| configuration.getProperty(SynchronizePageConfiguration.P_ADVISOR), |
| fStructuredComparePane, |
| fContentPane |
| } |
| )); |
| } |
| |
| /* |
| * Find a viewer that can provide a structure view for the given compare input. |
| * Return <code>null</code> if a suitable viewer could not be found. |
| * @param parent the parent composite for the viewer |
| * @param oldViewer the viewer that is currently a child of the parent |
| * @param input the compare input to be viewed |
| * @return a viewer capable of displaying a structure view of the input or |
| * <code>null</code> if such a viewer is not available. |
| */ |
| private Viewer findStructureViewer(Composite parent, Viewer oldViewer, ICompareInput input) { |
| return CompareUI.findStructureViewer(oldViewer, input, parent, cc); |
| } |
| |
| /* |
| * Find a viewer that can provide a content compare view for the given compare input. |
| * Return <code>null</code> if a suitable viewer could not be found. |
| * @param parent the parent composite for the viewer |
| * @param oldViewer the viewer that is currently a child of the parent |
| * @param input the compare input to be viewed |
| * @return a viewer capable of displaying a content compare view of the input or |
| * <code>null</code> if such a viewer is not available. |
| */ |
| private Viewer findContentViewer(Composite parent, Viewer oldViewer, ICompareInput input) { |
| return CompareUI.findContentViewer(oldViewer, input, parent, cc); |
| } |
| |
| /** |
| * Return a compare input that represents the selection. |
| * This input is used to feed the structure and content |
| * viewers. By default, a compare input is returned if the selection is |
| * of size 1 and the selected element implements <code>ICompareInput</code>. |
| * Subclasses may override. |
| * @param selection the selection |
| * @return a compare input representing the selection |
| */ |
| protected ICompareInput getCompareInput(ISelection selection) { |
| if (selection != null && selection instanceof IStructuredSelection) { |
| IStructuredSelection ss= (IStructuredSelection) selection; |
| if (ss.size() == 1) { |
| Object o = ss.getFirstElement(); |
| if(o instanceof ICompareInput) { |
| return (ICompareInput)o; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Set whether the file contents panes should be shown. If they are not, |
| * only the page will be shown. |
| * |
| * @param showContentPanes whether to show contents pane |
| */ |
| public void setShowContentPanes(boolean showContentPanes) { |
| this.showContentPanes = showContentPanes; |
| } |
| |
| /** |
| * Returns the primary control for this part. |
| * |
| * @return the primary control for this part. |
| */ |
| public Control getControl() { |
| return control; |
| } |
| |
| /** |
| * Return the compare configuration. |
| * @return the compare configuration |
| */ |
| private CompareConfiguration getCompareConfiguration() { |
| return cc; |
| } |
| |
| /** |
| * This method flushes the content in any viewers. Subclasses should |
| * override if they need to perform additional processing when a save is |
| * performed. |
| * |
| * @param monitor |
| * a progress monitor |
| */ |
| @Override |
| public void doSave(IProgressMonitor monitor) { |
| flushViewers(monitor); |
| } |
| |
| private void flushViewers(IProgressMonitor monitor) { |
| Iterator iter = fDirtyViewers.iterator(); |
| |
| for (int i=0; i<fDirtyViewers.size(); i++){ |
| Object element = iter.next(); |
| IFlushable flushable = Adapters.adapt(element, IFlushable.class); |
| if (flushable != null) |
| flushable.flush(monitor); |
| } |
| } |
| |
| } |