blob: 9aae73eb9236b0d2fde8aa68cdbfb216e4617160 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.text.java.hover;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IInformationControlCreatorExtension;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationAccessExtension;
import org.eclipse.jface.text.source.IAnnotationHover;
import org.eclipse.jface.text.source.IAnnotationHoverExtension;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ILineRange;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRulerListener;
import org.eclipse.jface.text.source.LineRange;
import org.eclipse.jface.text.source.VerticalRulerEvent;
import org.eclipse.jdt.internal.ui.text.java.hover.AnnotationExpansionControl.AnnotationHoverInput;
/**
* This class got moved here form Platform Text since it was not used there
* and caused discouraged access warnings. It will be moved down again once
* annotation roll-over support is provided by Platform Text.
*
* @since 3.2
*/
public class AnnotationExpandHover implements IAnnotationHover, IAnnotationHoverExtension {
private class InformationControlCreator implements IInformationControlCreator, IInformationControlCreatorExtension {
/*
* @see org.eclipse.jface.text.IInformationControlCreator#createInformationControl(org.eclipse.swt.widgets.Shell)
*/
public IInformationControl createInformationControl(Shell parent) {
return new AnnotationExpansionControl(parent, SWT.NONE, fAnnotationAccess);
}
/*
* @see org.eclipse.jface.text.IInformationControlCreatorExtension#canReuse(org.eclipse.jface.text.IInformationControl)
*/
public boolean canReuse(IInformationControl control) {
return control instanceof AnnotationExpansionControl;
}
/*
* @see org.eclipse.jface.text.IInformationControlCreatorExtension#canReplace(org.eclipse.jface.text.IInformationControlCreator)
*/
public boolean canReplace(IInformationControlCreator creator) {
return creator == this;
}
}
private class VerticalRulerListener implements IVerticalRulerListener {
/*
* @see org.eclipse.jface.text.source.IVerticalRulerListener#annotationSelected(org.eclipse.jface.text.source.VerticalRulerEvent)
*/
public void annotationSelected(VerticalRulerEvent event) {
fCompositeRuler.fireAnnotationSelected(event);
}
/*
* @see org.eclipse.jface.text.source.IVerticalRulerListener#annotationDefaultSelected(org.eclipse.jface.text.source.VerticalRulerEvent)
*/
public void annotationDefaultSelected(VerticalRulerEvent event) {
fCompositeRuler.fireAnnotationDefaultSelected(event);
}
/*
* @see org.eclipse.jface.text.source.IVerticalRulerListener#annotationContextMenuAboutToShow(org.eclipse.jface.text.source.VerticalRulerEvent, org.eclipse.swt.widgets.Menu)
*/
public void annotationContextMenuAboutToShow(VerticalRulerEvent event, Menu menu) {
fCompositeRuler.fireAnnotationContextMenuAboutToShow(event, menu);
}
}
private final IInformationControlCreator fgCreator= new InformationControlCreator();
protected final IVerticalRulerListener fgListener= new VerticalRulerListener();
protected CompositeRuler fCompositeRuler;
protected IDoubleClickListener fDblClickListener;
protected IAnnotationAccess fAnnotationAccess;
/**
* Creates a new hover instance.
*
* @param ruler
* @param access
* @param doubleClickListener
*/
public AnnotationExpandHover(CompositeRuler ruler, IAnnotationAccess access, IDoubleClickListener doubleClickListener) {
fCompositeRuler= ruler;
fAnnotationAccess= access;
fDblClickListener= doubleClickListener;
}
/*
* @see org.eclipse.jface.text.source.IAnnotationHover#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer, int)
*/
public String getHoverInfo(ISourceViewer sourceViewer, int line) {
// we don't have any sensible return value as text
return null;
}
protected Object getHoverInfoForLine(ISourceViewer viewer, int line) {
IAnnotationModel model= viewer.getAnnotationModel();
IDocument document= viewer.getDocument();
if (model == null)
return null;
List exact= new ArrayList();
HashMap messagesAtPosition= new HashMap();
Iterator e= model.getAnnotationIterator();
while (e.hasNext()) {
Annotation annotation= (Annotation) e.next();
Position position= model.getPosition(annotation);
if (position == null)
continue;
if (compareRulerLine(position, document, line) == 1) {
if (isDuplicateMessage(messagesAtPosition, position, annotation.getText()))
continue;
exact.add(annotation);
}
}
if (exact.size() < 1)
return null;
sort(exact, model);
if (exact.size() > 0)
setLastRulerMouseLocation(viewer, line);
AnnotationHoverInput input= new AnnotationHoverInput();
input.fAnnotations= (Annotation[]) exact.toArray(new Annotation[0]);
input.fViewer= viewer;
input.fRulerInfo= fCompositeRuler;
input.fAnnotationListener= fgListener;
input.fDoubleClickListener= fDblClickListener;
input.model= model;
return input;
}
protected void sort(List exact, final IAnnotationModel model) {
class AnnotationComparator implements Comparator {
/*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(Object o1, Object o2) {
Annotation a1= (Annotation) o1;
Annotation a2= (Annotation) o2;
Position p1= model.getPosition(a1);
Position p2= model.getPosition(a2);
// annotation order:
// primary order: by position in line
// secondary: annotation importance
if (p1.offset == p2.offset)
return getOrder(a2) - getOrder(a1);
return p1.offset - p2.offset;
}
}
Collections.sort(exact, new AnnotationComparator());
}
protected int getOrder(Annotation annotation) {
if (fAnnotationAccess instanceof IAnnotationAccessExtension) {
IAnnotationAccessExtension extension= (IAnnotationAccessExtension) fAnnotationAccess;
return extension.getLayer(annotation);
}
return IAnnotationAccessExtension.DEFAULT_LAYER;
}
protected boolean isDuplicateMessage(Map messagesAtPosition, Position position, String message) {
if (message == null)
return false;
if (messagesAtPosition.containsKey(position)) {
Object value= messagesAtPosition.get(position);
if (message == null || message.equals(value))
return true;
if (value instanceof List) {
List messages= (List)value;
if (messages.contains(message))
return true;
messages.add(message);
} else {
ArrayList messages= new ArrayList();
messages.add(value);
messages.add(message);
messagesAtPosition.put(position, messages);
}
} else
messagesAtPosition.put(position, message);
return false;
}
protected void setLastRulerMouseLocation(ISourceViewer viewer, int line) {
// set last mouse activity in order to get the correct context menu
if (fCompositeRuler != null) {
StyledText st= viewer.getTextWidget();
if (st != null && !st.isDisposed()) {
if (viewer instanceof ITextViewerExtension5) {
int widgetLine= ((ITextViewerExtension5)viewer).modelLine2WidgetLine(line);
Point loc= st.getLocationAtOffset(st.getOffsetAtLine(widgetLine));
fCompositeRuler.setLocationOfLastMouseButtonActivity(0, loc.y);
} else if (viewer instanceof TextViewer) {
// TODO remove once TextViewer implements the extension
int widgetLine= ((TextViewer)viewer).modelLine2WidgetLine(line);
Point loc= st.getLocationAtOffset(st.getOffsetAtLine(widgetLine));
fCompositeRuler.setLocationOfLastMouseButtonActivity(0, loc.y);
}
}
}
}
/**
* Returns the distance to the ruler line.
*
* @param position the position
* @param document the document
* @param line the line number
* @return the distance to the ruler line
*/
protected int compareRulerLine(Position position, IDocument document, int line) {
if (position.getOffset() > -1 && position.getLength() > -1) {
try {
int firstLine= document.getLineOfOffset(position.getOffset());
if (line == firstLine)
return 1;
if (firstLine <= line && line <= document.getLineOfOffset(position.getOffset() + position.getLength()))
return 2;
} catch (BadLocationException x) {
}
}
return 0;
}
/*
* @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverControlCreator()
*/
public IInformationControlCreator getHoverControlCreator() {
return fgCreator;
}
/*
* @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverInfo(org.eclipse.jface.text.source.ISourceViewer, org.eclipse.jface.text.source.ILineRange, int)
*/
public Object getHoverInfo(ISourceViewer sourceViewer, ILineRange lineRange, int visibleLines) {
return getHoverInfoForLine(sourceViewer, lineRange.getStartLine());
}
/*
* @see org.eclipse.jface.text.source.IAnnotationHoverExtension#getHoverLineRange(org.eclipse.jface.text.source.ISourceViewer, int)
*/
public ILineRange getHoverLineRange(ISourceViewer viewer, int lineNumber) {
return new LineRange(lineNumber, 1);
}
/*
* @see org.eclipse.jface.text.source.IAnnotationHoverExtension#canHandleMouseCursor()
*/
public boolean canHandleMouseCursor() {
return true;
}
}