blob: 842bd7961385c8c1f4cb4615f2035a30b0fb3b95 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2016 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.jface.internal.text.revisions;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.viewers.IPostSelectionProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.revisions.Revision;
/**
* A selection provider for annotate revisions. Selections of a revision can currently happen in
* following ways - note that this list may be changed in the future:
* <ul>
* <li>when the user clicks the revision ruler with the mouse</li>
* <li>when the caret is moved to a revision's line (only on post-selection)</li>
* </ul>
* <p>
* Calling {@link #setSelection(ISelection)} will set the current sticky revision on the ruler.
* </p>
*
* @since 3.2
*/
public final class RevisionSelectionProvider implements ISelectionProvider {
/**
* Post selection listener on the viewer that remembers the selection provider it is registered
* with.
*/
private final class PostSelectionListener implements ISelectionChangedListener {
private final IPostSelectionProvider fPostProvider;
public PostSelectionListener(IPostSelectionProvider postProvider) {
postProvider.addPostSelectionChangedListener(this);
fPostProvider= postProvider;
}
@Override
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection= event.getSelection();
if (selection instanceof ITextSelection) {
ITextSelection ts= (ITextSelection) selection;
int offset= ts.getOffset();
setSelectedRevision(fPainter.getRevision(offset));
}
}
public void dispose() {
fPostProvider.removePostSelectionChangedListener(this);
}
}
private final RevisionPainter fPainter;
private final ListenerList<ISelectionChangedListener> fListeners= new ListenerList<>();
/**
* The text viewer once we are installed, <code>null</code> if not installed.
*/
private ITextViewer fViewer;
/**
* The selection listener on the viewer, or <code>null</code>.
*/
private PostSelectionListener fSelectionListener;
/**
* The last selection, or <code>null</code>.
*/
private Revision fSelection;
/**
* Incoming selection changes are ignored while sending out events.
*
* @since 3.3
*/
private boolean fIgnoreEvents= false;
/**
* Creates a new selection provider.
*
* @param painter the painter that the created provider interacts with
*/
RevisionSelectionProvider(RevisionPainter painter) {
fPainter= painter;
}
@Override
public void addSelectionChangedListener(ISelectionChangedListener listener) {
fListeners.add(listener);
}
@Override
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
fListeners.remove(listener);
}
@Override
public ISelection getSelection() {
if (fSelection == null)
return StructuredSelection.EMPTY;
return new StructuredSelection(fSelection);
}
@Override
public void setSelection(ISelection selection) {
if (fIgnoreEvents)
return;
if (selection instanceof IStructuredSelection) {
Object first= ((IStructuredSelection) selection).getFirstElement();
if (first instanceof Revision)
fPainter.handleRevisionSelected((Revision) first);
else if (first instanceof String)
fPainter.handleRevisionSelected((String) first);
else if (selection.isEmpty())
fPainter.handleRevisionSelected((Revision) null);
}
}
/**
* Installs the selection provider on the viewer.
*
* @param viewer the viewer on which we listen to for post selection events
*/
void install(ITextViewer viewer) {
uninstall();
fViewer= viewer;
if (fViewer != null) {
ISelectionProvider provider= fViewer.getSelectionProvider();
if (provider instanceof IPostSelectionProvider) {
IPostSelectionProvider postProvider= (IPostSelectionProvider) provider;
fSelectionListener= new PostSelectionListener(postProvider);
}
}
}
/**
* Uninstalls the selection provider.
*/
void uninstall() {
fViewer= null;
if (fSelectionListener != null) {
fSelectionListener.dispose();
fSelectionListener= null;
}
}
/**
* Private protocol used by {@link RevisionPainter} to signal selection of a revision.
*
* @param revision the selected revision, or <code>null</code> for none
*/
void revisionSelected(Revision revision) {
setSelectedRevision(revision);
}
/**
* Updates the currently selected revision and sends out an event if it changed.
*
* @param revision the newly selected revision or <code>null</code> for none
*/
private void setSelectedRevision(Revision revision) {
if (revision != fSelection) {
fSelection= revision;
fireSelectionEvent();
}
}
private void fireSelectionEvent() {
fIgnoreEvents= true;
try {
ISelection selection= getSelection();
SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
for (ISelectionChangedListener listener : fListeners) {
listener.selectionChanged(event);
}
} finally {
fIgnoreEvents= false;
}
}
}