| /******************************************************************************* |
| * Copyright (c) 2000, 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.compare; |
| |
| import org.eclipse.compare.contentmergeviewer.IFlushable; |
| import org.eclipse.compare.internal.CompareMessages; |
| import org.eclipse.compare.internal.IFlushable2; |
| import org.eclipse.compare.internal.NullViewer; |
| import org.eclipse.compare.internal.Utilities; |
| import org.eclipse.compare.structuremergeviewer.ICompareInput; |
| import org.eclipse.core.runtime.Adapters; |
| import org.eclipse.jface.viewers.ISelection; |
| import org.eclipse.jface.viewers.StructuredViewer; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| |
| import com.ibm.icu.text.MessageFormat; |
| |
| /** |
| * A custom <code>CompareViewerPane</code> that supports dynamic viewer switching. |
| * |
| * <p> |
| * Clients must implement the viewer switching strategy by implementing |
| * the <code>getViewer(Viewer, Object)</code> method. |
| * <p> |
| * If a property with the name <code>CompareUI.COMPARE_VIEWER_TITLE</code> is set |
| * on the top level SWT control of a viewer, it is used as a title in the <code>CompareViewerPane</code>'s |
| * title bar. |
| * |
| * @since 2.0 |
| */ |
| public abstract class CompareViewerSwitchingPane extends CompareViewerPane { |
| private Viewer fViewer; |
| private boolean fControlVisibility; |
| private String fTitle; |
| private String fTitleArgument; |
| |
| /** |
| * Creates a <code>CompareViewerSwitchingPane</code> as a child of the given parent and with the |
| * specified SWT style bits. |
| * |
| * @param parent a widget which will be the parent of the new instance (cannot be null) |
| * @param style the style of widget to construct |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> |
| * </ul> |
| * @exception org.eclipse.swt.SWTException <ul> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> |
| * </ul> |
| */ |
| public CompareViewerSwitchingPane(Composite parent, int style) { |
| this(parent, style, false); |
| } |
| |
| /** |
| * Creates a <code>CompareViewerSwitchingPane</code> as a child of the given parent and with the |
| * specified SWT style bits. |
| * |
| * @param parent a widget which will be the parent of the new instance (cannot be null) |
| * @param style the style of widget to construct |
| * @param visibility the initial visibility of the CompareViewerSwitchingPane |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> |
| * </ul> |
| * @exception org.eclipse.swt.SWTException <ul> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> |
| * </ul> |
| */ |
| public CompareViewerSwitchingPane(Composite parent, int style, boolean visibility) { |
| super(parent, style); |
| |
| fControlVisibility= visibility; |
| |
| setViewer(new NullViewer(this)); |
| |
| addDisposeListener( |
| e -> { |
| if (fViewer != null) |
| fViewer.removeSelectionChangedListener(CompareViewerSwitchingPane.this); |
| if (fViewer instanceof StructuredViewer) { |
| StructuredViewer sv= (StructuredViewer) fViewer; |
| sv.removeDoubleClickListener(CompareViewerSwitchingPane.this); |
| sv.removeOpenListener(CompareViewerSwitchingPane.this); |
| } |
| fViewer= null; |
| } |
| ); |
| } |
| |
| /** |
| * Returns the current viewer. |
| * |
| * @return the current viewer |
| */ |
| public Viewer getViewer() { |
| return fViewer; |
| } |
| |
| private void setViewer(Viewer newViewer) { |
| if (newViewer == fViewer) |
| return; |
| |
| boolean oldEmpty= isEmpty(); |
| |
| if (fViewer != null) { |
| fViewer.removeSelectionChangedListener(this); |
| |
| if (fViewer instanceof StructuredViewer) { |
| StructuredViewer sv= (StructuredViewer) fViewer; |
| sv.removeDoubleClickListener(this); |
| sv.removeOpenListener(this); |
| } |
| |
| Control content= getContent(); |
| setContent(null); |
| |
| fViewer.setInput(null); |
| |
| if (content != null && !content.isDisposed()) |
| content.dispose(); |
| } else { |
| oldEmpty= false; |
| } |
| |
| setContent(null); |
| |
| fViewer= newViewer; |
| |
| if (fViewer != null) { |
| // we have to remember and restore the old visibility of the CustomPane |
| // since setContent changes the visibility |
| boolean old= getVisible(); |
| setContent(fViewer.getControl()); |
| setVisible(old); // restore old visibility |
| |
| boolean newEmpty= isEmpty(); |
| |
| fViewer.addSelectionChangedListener(this); |
| |
| if (fViewer instanceof StructuredViewer) { |
| StructuredViewer sv= (StructuredViewer) fViewer; |
| sv.addDoubleClickListener(this); |
| sv.addOpenListener(this); |
| } |
| |
| if (oldEmpty != newEmpty) { // re-layout my container |
| Composite parent= getParent(); |
| if (parent instanceof Splitter) |
| ((Splitter)parent).setVisible(this, fControlVisibility ? !newEmpty : true); |
| } |
| |
| layout(true); |
| } |
| } |
| |
| /** |
| * Returns the optional title argument that has been set with |
| * <code>setTitelArgument</code> or <code>null</code> if no optional title |
| * argument has been set. |
| * |
| * @return the optional title argument or <code>null</code> |
| * @noreference This method is for internal use only. Clients should not |
| * call this method. |
| * @nooverride This method is not intended to be re-implemented or extended |
| * by clients. |
| */ |
| public String getTitleArgument() { |
| return fTitleArgument; |
| } |
| |
| /** |
| * Returns <code>true</code> if no viewer is installed or if the current viewer |
| * is a <code>NullViewer</code>. |
| * |
| * @return <code>true</code> if no viewer is installed or if the current viewer is a <code>NullViewer</code> |
| */ |
| public boolean isEmpty() { |
| return fViewer == null || fViewer instanceof NullViewer; |
| } |
| |
| @Override |
| public ISelection getSelection() { |
| if (fViewer != null) |
| return fViewer.getSelection(); |
| return super.getSelection(); |
| } |
| |
| @Override |
| public void setSelection(ISelection s) { |
| if (fViewer != null) |
| fViewer.setSelection(s); |
| } |
| |
| private boolean hasFocus2() { |
| // do we have focus? |
| Display display= getDisplay(); |
| if (display != null) |
| for (Control focus= display.getFocusControl(); focus != null; focus= focus.getParent()) |
| if (focus == this) |
| return true; |
| return false; |
| } |
| |
| /** |
| * @param input the input |
| * @return true, if the input is considered as changed |
| * @noreference This method is not intended to be referenced by clients. |
| * @nooverride This method is not intended to be re-implemented or extended |
| * by clients. |
| */ |
| protected boolean inputChanged(Object input) { |
| return getInput() != input; |
| } |
| |
| /** |
| * Sets the input object of this pane. |
| * For this input object a suitable viewer is determined by calling the abstract |
| * method <code>getViewer(Viewer, Object)</code>. |
| * If the returned viewer differs from the current one, the old viewer |
| * is disposed and the new one installed. Then the input object is fed |
| * into the newly installed viewer by calling its <code>setInput(Object)</code> method. |
| * If new and old viewer don't differ no new viewer is installed but just |
| * <code>setInput(Object)</code> is called. |
| * If the input is <code>null</code> the pane is cleared, |
| * that is the current viewer is disposed. |
| * |
| * @param input the new input object or <code>null</code> |
| */ |
| @Override |
| public void setInput(Object input) { |
| if (!inputChanged(input)) |
| return; |
| |
| boolean hadFocus = hasFocus2(); |
| |
| super.setInput(input); |
| |
| // viewer switching |
| Viewer newViewer= null; |
| if (input != null) |
| newViewer= getViewer(fViewer, input); |
| |
| if (newViewer == null) { |
| if (fViewer instanceof NullViewer) |
| return; |
| newViewer= new NullViewer(this); |
| } |
| |
| setViewer(newViewer); |
| |
| // set input |
| fViewer.setInput(input); |
| |
| if (getViewer() == null || !Utilities.okToUse(getViewer().getControl())) |
| return; |
| |
| Image image= null; |
| if (!(fViewer instanceof NullViewer) && input instanceof ICompareInput) |
| image= ((ICompareInput)input).getImage(); |
| setImage(image); |
| |
| String title= null; |
| if (fViewer != null) { |
| Control c= fViewer.getControl(); |
| if (c != null) { |
| Object data= c.getData(CompareUI.COMPARE_VIEWER_TITLE); |
| if (data instanceof String) |
| title= (String) data; |
| if (hadFocus) |
| c.setFocus(); |
| } |
| } |
| |
| fTitle= title; |
| updateTitle(); |
| } |
| |
| /** |
| * Sets an additional and optional argument for the pane's title. |
| * |
| * @param argument |
| * an optional argument for the pane's title |
| * @noreference This method is for internal use only. Clients should not |
| * call this method. |
| * @nooverride This method is not intended to be re-implemented or extended |
| * by clients. |
| */ |
| public void setTitleArgument(String argument) { |
| fTitleArgument= argument; |
| updateTitle(); |
| } |
| |
| private void updateTitle() { |
| if (fTitle != null) { |
| if (fTitleArgument != null) { |
| String format= CompareMessages.CompareViewerSwitchingPane_Titleformat; |
| String t= MessageFormat.format(format, fTitle, fTitleArgument); |
| setText(t); |
| } else |
| setText(fTitle); |
| } else { |
| setText(""); //$NON-NLS-1$ |
| } |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> T getAdapter(Class<T> adapter) { |
| if (adapter == INavigatable.class) { |
| if (isEmpty()) |
| return null; |
| Viewer viewer= getViewer(); |
| if (viewer == null) |
| return null; |
| Control control= viewer.getControl(); |
| if (control == null) |
| return null; |
| Object data= control.getData(INavigatable.NAVIGATOR_PROPERTY); |
| if (data instanceof INavigatable) |
| return (T) data; |
| } |
| if (adapter == IFlushable.class) { |
| Viewer v= getViewer(); |
| if (v != null) { |
| IFlushable flushable = Adapters.adapt(v, IFlushable.class); |
| if (flushable != null) |
| return (T) flushable; |
| } |
| } |
| if (adapter == IFlushable2.class) { |
| Viewer v= getViewer(); |
| if (v != null) { |
| IFlushable2 flushable = Adapters.adapt(v, IFlushable2.class); |
| if (flushable != null) |
| return (T) flushable; |
| } |
| } |
| return super.getAdapter(adapter); |
| } |
| |
| @Override |
| public boolean setFocus() { |
| Viewer v= getViewer(); |
| if (v != null) { |
| Control c= v.getControl(); |
| if (c != null) { |
| if (c.setFocus()) |
| return true; |
| } |
| } |
| return super.setFocus(); |
| } |
| |
| /** |
| * Returns a viewer which is able to display the given input. |
| * If no viewer can be found, <code>null</code> is returned. |
| * The additional argument oldViewer represents the viewer currently installed |
| * in the pane (or <code>null</code> if no viewer is installed). |
| * It can be returned from this method if the current viewer can deal with the |
| * input (and no new viewer must be created). |
| * |
| * @param oldViewer the currently installed viewer or <code>null</code> |
| * @param input the input object for which a viewer must be determined or <code>null</code> |
| * @return a viewer for the given input, or <code>null</code> if no viewer can be determined |
| */ |
| abstract protected Viewer getViewer(Viewer oldViewer, Object input); |
| } |