blob: db2e4fa3822694602d63cc489d2df1bfe1ffcf07 [file] [log] [blame]
/*******************************************************************************
* 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);
}