/*******************************************************************************
* Copyright (c) 2012 xored software, Inc. 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:
* xored software, Inc. - initial API and implementation (Yuri Strot)
******************************************************************************/
package com.xored.glance.internal.ui.viewers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xored.glance.ui.controls.text.styled.TextSelector;
import com.xored.glance.ui.sources.BaseTextSource;
import com.xored.glance.ui.sources.ColorManager;
import com.xored.glance.ui.sources.ITextBlock;
import com.xored.glance.ui.sources.ITextSourceListener;
import com.xored.glance.ui.sources.Match;
import com.xored.glance.ui.sources.SourceSelection;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.graphics.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @author Yuri Strot
*/
public class SourceViewerControl extends BaseTextSource implements ISelectionChangedListener {
public static String ANNOTATION_TYPE = ColorManager.ANNOTATION_ID;
public static String SELECTED_ANNOTATION_TYPE = ColorManager.ANNOTATION_SELECTED_ID;
private static final Map<Annotation, Position> NO_ANNOTAIONS = Maps.newHashMap();
private TextSelector selector;
private final ListenerList listeners;
private boolean inited;
private boolean disposed;
private final TextViewerBlock[] blocks;
private final SourceViewer viewer;
public SourceViewerControl(final SourceViewer viewer) {
this.viewer = viewer;
listeners = new ListenerList();
blocks = new TextViewerBlock[] {new TextViewerBlock(viewer)};
}
@Override
public void addTextSourceListener(final ITextSourceListener listener) {
listeners.add(listener);
}
@Override
public void dispose() {
if (!disposed) {
selector.dispose();
viewer.removeSelectionChangedListener(this);
replaceAnnotations(getAnnotations(), NO_ANNOTAIONS);
getBlock().dispose();
disposed = true;
}
inited = false;
}
public TextViewerBlock getBlock() {
return blocks[0];
}
@Override
public ITextBlock[] getBlocks() {
return blocks;
}
@Override
public SourceSelection getSelection() {
final Point selection = viewer.getSelectedRange();
return new SourceSelection(getBlock(), selection.x, selection.y);
}
@Override
public void init() {
if (inited) {
return;
}
inited = true;
if (selector != null) {
selector.dispose();
select(null);
}
selector = new ViewerSelector(viewer);
viewer.addSelectionChangedListener(this);
}
@Override
public boolean isDisposed() {
return disposed;
}
@Override
public void removeTextSourceListener(final ITextSourceListener listener) {
listeners.remove(listener);
}
@Override
public void select(final Match match) {
final Annotation[] remove = getAnnotations(true);
final Map<Annotation, Position> add = match != null ? createAnnotations(
new Match[] {match},
true) : new HashMap<Annotation, Position>();
replaceAnnotations(remove, add);
selector.setMatch(match);
}
@Override
public void selectionChanged(final SelectionChangedEvent event) {
final ISelection selection = event.getSelection();
if (selection instanceof TextSelection) {
final TextSelection tSelection = (TextSelection) selection;
final SourceSelection sSelection = new SourceSelection(
getBlock(),
tSelection.getOffset(),
tSelection.getLength());
final Object[] objects = listeners.getListeners();
for (final Object object : objects) {
final ITextSourceListener listener = (ITextSourceListener) object;
listener.selectionChanged(sSelection);
}
}
}
@Override
public void show(final Match[] matches) {
replaceMatches(matches);
}
private Map<Annotation, Position> createAnnotations(final Match[] matches, final boolean selected) {
final Map<Annotation, Position> map = new HashMap<Annotation, Position>();
for (final Match match : matches) {
final Annotation annotation = new Annotation(selected ? SELECTED_ANNOTATION_TYPE
: ANNOTATION_TYPE, false, null);
final Position position = new Position(match.getOffset(), match.getLength());
map.put(annotation, position);
}
return map;
}
/**
* @return all selected and unselected annotations.
*/
private Annotation[] getAnnotations() {
List<Annotation> allAnnotations = Lists.newArrayList();
Collections.addAll(allAnnotations, getAnnotations(true));
Collections.addAll(allAnnotations, getAnnotations(false));
return allAnnotations.toArray(new Annotation[allAnnotations.size()]);
}
private Annotation[] getAnnotations(final boolean selected) {
final String type = selected ? SELECTED_ANNOTATION_TYPE : ANNOTATION_TYPE;
final IAnnotationModel model = viewer.getAnnotationModel();
final List<Annotation> annotations = new ArrayList<Annotation>();
if (model != null) {
final Iterator<?> it = model.getAnnotationIterator();
while (it.hasNext()) {
final Annotation annotation = (Annotation) it.next();
if (type.equals(annotation.getType())) {
annotations.add(annotation);
}
}
}
return annotations.toArray(new Annotation[annotations.size()]);
}
private void replaceAnnotations(Annotation[] remove, Map<Annotation, Position> add) {
final IAnnotationModel model = viewer.getAnnotationModel();
if (model instanceof IAnnotationModelExtension) {
final IAnnotationModelExtension eModel = (IAnnotationModelExtension) model;
eModel.replaceAnnotations(remove, add);
} else {
for (final Annotation annotation : remove) {
model.removeAnnotation(annotation);
}
for (final Annotation annotation : add.keySet()) {
model.addAnnotation(annotation, add.get(annotation));
}
}
}
private void replaceMatches(final Match[] matches) {
final Annotation[] remove = getAnnotations(false);
final Map<Annotation, Position> add = createAnnotations(matches, false);
replaceAnnotations(remove, add);
}
}