1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 22:52:11 +02:00

Bug 476797 - Port Expand annotation hover from JDT

Ported the JDT expand annotation hover code into CDT & adapted.

Added preference to control in equivalent location to JDT. Default is
disabled for now.

Changed behaviour from JDT version - 
 * Breakpoint annotations & add breakpoint option displayed last
 * Tooltips displayed with no delay after expanded hover is displayed
 * Reverted fix for bug 165533 due to issue where single click on 1st
   item in expanded hover also triggers single click on top item in
   ruler if mouse within ruler area (Same as JDT)

Known issues -
 * Double click on ruler column sometimes triggers single click on 1st
   item in expanded hover if user is too slow (Same as JDT)

Change-Id: I87c2f8efd04ea5084b056241a04758a368e2ca55
Signed-off-by: William Riley <william.riley@renesas.com>
This commit is contained in:
William Riley 2016-03-08 13:39:56 -05:00 committed by Gerrit Code Review @ Eclipse.org
parent 739281bec7
commit c297330c9c
14 changed files with 1765 additions and 16 deletions

View file

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.cdt.ui; singleton:=true
Bundle-Version: 6.0.0.qualifier
Bundle-Version: 6.1.0.qualifier
Bundle-Activator: org.eclipse.cdt.ui.CUIPlugin
Bundle-Vendor: %providerName
Bundle-Localization: plugin

View file

@ -11,7 +11,7 @@
<relativePath>../../pom.xml</relativePath>
</parent>
<version>6.0.0-SNAPSHOT</version>
<version>6.1.0-SNAPSHOT</version>
<artifactId>org.eclipse.cdt.ui</artifactId>
<packaging>eclipse-plugin</packaging>
</project>

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2002, 2015 QNX Software Systems and others.
* Copyright (c) 2002, 2016 QNX Software Systems 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
@ -107,7 +107,7 @@ public class CDocumentProvider extends TextFileDocumentProvider {
/**
* Annotation representing an {@code IProblem}.
*/
static protected class ProblemAnnotation extends Annotation implements ICAnnotation {
public static class ProblemAnnotation extends Annotation implements ICAnnotation {
private static final String INDEXER_ANNOTATION_TYPE= "org.eclipse.cdt.ui.indexmarker"; //$NON-NLS-1$
private final ITranslationUnit fTranslationUnit;

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2005, 2015 IBM Corporation and others.
* Copyright (c) 2005, 2016 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
@ -97,6 +97,8 @@ import org.eclipse.jface.text.link.LinkedModeUI.IExitPolicy;
import org.eclipse.jface.text.link.LinkedPosition;
import org.eclipse.jface.text.link.LinkedPositionGroup;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationRulerColumn;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.IAnnotationModelExtension2;
@ -105,11 +107,14 @@ import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension2;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.IVerticalRulerColumn;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel;
import org.eclipse.jface.text.source.projection.ProjectionSupport;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
@ -136,6 +141,7 @@ import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionContext;
@ -232,6 +238,7 @@ import org.eclipse.cdt.internal.ui.text.DocumentCharacterIterator;
import org.eclipse.cdt.internal.ui.text.ICReconcilingListener;
import org.eclipse.cdt.internal.ui.text.Symbols;
import org.eclipse.cdt.internal.ui.text.TabsToSpacesConverter;
import org.eclipse.cdt.internal.ui.text.c.hover.CExpandHover;
import org.eclipse.cdt.internal.ui.text.c.hover.SourceViewerInformationControl;
import org.eclipse.cdt.internal.ui.text.contentassist.ContentAssistPreference;
import org.eclipse.cdt.internal.ui.util.CUIHelp;
@ -2347,6 +2354,10 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
action.setActionDefinitionId(ICEditorActionDefinitionIds.SELECT_LAST);
setAction(StructureSelectionAction.HISTORY, action);
// add annotation actions for roll-over expand hover
action= new CSelectMarkerRulerAction(bundle, "Editor.RulerAnnotationSelection.", this); //$NON-NLS-1$
setAction("AnnotationAction", action); //$NON-NLS-1$
// Assorted action groupings
fSelectionSearchGroup = createSelectionSearchGroup();
@ -3651,4 +3662,39 @@ public class CEditor extends TextEditor implements ICEditor, ISelectionChangedLi
public void removePostSaveListener(IPostSaveListener listener) {
fPostSaveListeners.remove(listener);
}
@Override
protected IVerticalRulerColumn createAnnotationRulerColumn(CompositeRuler ruler) {
if (!getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_ANNOTATION_ROLL_OVER)) {
return super.createAnnotationRulerColumn(ruler);
}
AnnotationRulerColumn column= new AnnotationRulerColumn(VERTICAL_RULER_WIDTH, getAnnotationAccess());
column.setHover(new CExpandHover(ruler, getAnnotationAccess(), new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
// for now: just invoke ruler double click action
triggerAction(ITextEditorActionConstants.RULER_DOUBLE_CLICK);
}
private void triggerAction(String actionID) {
IAction action= getAction(actionID);
if (action != null) {
if (action instanceof IUpdate)
((IUpdate) action).update();
// hack to propagate line change
if (action instanceof ISelectionListener) {
((ISelectionListener)action).selectionChanged(null, null);
}
if (action.isEnabled()) {
action.run();
}
}
}
}));
return column;
}
}

View file

@ -0,0 +1,130 @@
/*******************************************************************************
* Copyright (c) 2000, 2016 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
* William Riley (Renesas) - Adapted for CDT
*******************************************************************************/
package org.eclipse.cdt.internal.ui.editor;
import java.util.ResourceBundle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Event;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.VerticalRulerEvent;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.IUpdate;
import org.eclipse.ui.texteditor.SelectAnnotationRulerAction;
import org.eclipse.cdt.internal.ui.text.correction.CCorrectionProcessor;
import org.eclipse.cdt.internal.ui.text.correction.QuickAssistLightBulbUpdater.AssistAnnotation;
import org.eclipse.cdt.internal.ui.text.c.hover.CExpandHover;
/**
* A special select marker ruler action which activates quick fix if clicked on a quick fixable problem.
* <p>
* Originally copied from org.eclipse.jdt.internal.ui.javaeditor.JavaSelectMarkerRulerAction
*/
public class CSelectMarkerRulerAction extends SelectAnnotationRulerAction {
public CSelectMarkerRulerAction(ResourceBundle bundle, String prefix, ITextEditor editor) {
super(bundle, prefix, editor);
}
/*
* @see org.eclipse.ui.texteditor.IVerticalRulerListener#annotationDefaultSelected(org.eclipse.ui.texteditor.VerticalRulerEvent)
*/
@Override
public void annotationDefaultSelected(VerticalRulerEvent event) {
Annotation annotation= event.getSelectedAnnotation();
IAnnotationModel model= getAnnotationModel();
if (isOverrideIndicator(annotation)) {
((OverrideIndicatorManager.OverrideIndicator)annotation).open();
return;
}
if (isBreakpoint(annotation))
triggerAction(ITextEditorActionConstants.RULER_DOUBLE_CLICK, event.getEvent());
Position position= model.getPosition(annotation);
if (position == null)
return;
if (isQuickFixTarget(annotation)) {
ITextOperationTarget operation= getTextEditor().getAdapter(ITextOperationTarget.class);
final int opCode= ISourceViewer.QUICK_ASSIST;
if (operation != null && operation.canDoOperation(opCode)) {
getTextEditor().selectAndReveal(position.getOffset(), position.getLength());
operation.doOperation(opCode);
return;
}
}
// default:
super.annotationDefaultSelected(event);
}
/**
* Tells whether the given annotation is an override annotation.
*
* @param annotation the annotation
* @return <code>true</code> iff the annotation is an override annotation
*/
private boolean isOverrideIndicator(Annotation annotation) {
return annotation instanceof OverrideIndicatorManager.OverrideIndicator;
}
/**
* Checks whether the given annotation is a breakpoint annotation.
*
* @param annotation the annotation
* @return <code>true</code> if the annotation is a breakpoint annotation
*/
private boolean isBreakpoint(Annotation annotation) {
return annotation.getType().equals("org.eclipse.cdt.debug.core.breakpoint") || annotation.getType().equals(CExpandHover.NO_BREAKPOINT_ANNOTATION); //$NON-NLS-1$
}
private boolean isQuickFixTarget(Annotation a) {
return CCorrectionProcessor.hasCorrections(a) || a instanceof AssistAnnotation;
}
private void triggerAction(String actionID, Event event) {
IAction action= getTextEditor().getAction(actionID);
if (action != null) {
if (action instanceof IUpdate)
((IUpdate) action).update();
// hack to propagate line change
if (action instanceof ISelectionListener) {
((ISelectionListener)action).selectionChanged(null, null);
}
if (action.isEnabled()) {
if (event == null) {
action.run();
} else {
event.type= SWT.MouseDoubleClick;
event.count= 2;
action.runWithEvent(event);
}
}
}
}
}

View file

@ -14,6 +14,8 @@ package org.eclipse.cdt.internal.ui.preferences;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.Assert;
@ -44,6 +46,7 @@ import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
@ -155,6 +158,19 @@ public class CEditorHoverConfigurationBlock implements IPreferenceConfigurationB
private TableColumn fNameColumn;
private TableColumn fModifierColumn;
private Text fDescription;
private Map<Button, String> fCheckBoxes= new HashMap<>();
private SelectionListener fCheckBoxListener= new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
Button button= (Button) e.widget;
fStore.setValue(fCheckBoxes.get(button), button.getSelection());
}
@Override
public void widgetSelected(SelectionEvent e) {
Button button= (Button) e.widget;
fStore.setValue(fCheckBoxes.get(button), button.getSelection());
}
};
private PreferencePage fMainPreferencePage;
@ -173,7 +189,7 @@ public class CEditorHoverConfigurationBlock implements IPreferenceConfigurationB
ArrayList<OverlayKey> overlayKeys= new ArrayList<OverlayKey>();
//overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_ANNOTATION_ROLL_OVER));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, PreferenceConstants.EDITOR_ANNOTATION_ROLL_OVER));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, PreferenceConstants.EDITOR_TEXT_HOVER_MODIFIERS));
overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, PreferenceConstants.EDITOR_TEXT_HOVER_MODIFIER_MASKS));
@ -204,9 +220,8 @@ public class CEditorHoverConfigurationBlock implements IPreferenceConfigurationB
hoverComposite.setLayout(layout);
hoverComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
//String rollOverLabel= PreferencesMessages.getString("CEditorHoverConfigurationBlock.annotationRollover"); //$NON-NLS-1$
//addCheckBox(hoverComposite, rollOverLabel, PreferenceConstants.EDITOR_ANNOTATION_ROLL_OVER, 0); //$NON-NLS-1$
String rollOverLabel= PreferencesMessages.CEditorHoverConfigurationBlock_annotationRollover;
addCheckBox(hoverComposite, rollOverLabel, PreferenceConstants.EDITOR_ANNOTATION_ROLL_OVER, 0); //$NON-NLS-1$
//addFiller(hoverComposite);
Label label= new Label(hoverComposite, SWT.NONE);
@ -384,7 +399,14 @@ public class CEditorHoverConfigurationBlock implements IPreferenceConfigurationB
void initializeFields() {
fModifierEditor.setEnabled(false);
Iterator<Button> e= fCheckBoxes.keySet().iterator();
while (e.hasNext()) {
Button b= e.next();
String key= fCheckBoxes.get(b);
b.setSelection(fStore.getBoolean(key));
}
CEditorTextHoverDescriptor[] hoverDescs= getContributedHovers();
for (int i= 0; i < hoverDescs.length; i++)
fHoverTable.getItem(i).setChecked(hoverDescs[i].isEnabled());
@ -546,7 +568,21 @@ public class CEditorHoverConfigurationBlock implements IPreferenceConfigurationB
fMainPreferencePage.setValid(fStatus.isOK());
StatusUtil.applyToStatusLine(fMainPreferencePage, fStatus);
}
private Button addCheckBox(Composite parent, String label, String key, int indentation) {
Button checkBox= new Button(parent, SWT.CHECK);
checkBox.setText(label);
GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
gd.horizontalIndent= indentation;
gd.horizontalSpan= 2;
checkBox.setLayoutData(gd);
checkBox.addSelectionListener(fCheckBoxListener);
fCheckBoxes.put(checkBox, key);
return checkBox;
}
// private void addFiller(Composite composite) {
// PixelConverter pixelConverter= new PixelConverter(composite);

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* Copyright (c) 2000, 2016 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
@ -126,6 +126,7 @@ public final class PreferencesMessages extends NLS {
public static String CFileTypeDialog_patternLabel;
public static String CFileTypeDialog_typeLabel;
public static String CEditorPreferencePage_hover_title;
public static String CEditorHoverConfigurationBlock_annotationRollover;
public static String CEditorHoverConfigurationBlock_hoverPreferences;
public static String CEditorHoverConfigurationBlock_keyModifier;
public static String CEditorHoverConfigurationBlock_description;

View file

@ -1,5 +1,5 @@
###############################################################################
# Copyright (c) 2000, 2015 IBM Corporation and others.
# Copyright (c) 2000, 2016 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
@ -143,6 +143,7 @@ CFileTypeDialog_typeLabel=Type:
# Hover page
CEditorPreferencePage_hover_title= Ho&vers
CEditorHoverConfigurationBlock_annotationRollover=&Expand vertical ruler icons upon hovering (does not affect open editors)
CEditorHoverConfigurationBlock_hoverPreferences= Text &Hover key modifier preferences:
CEditorHoverConfigurationBlock_keyModifier= Pressed key &modifier while hovering:
CEditorHoverConfigurationBlock_description= Description:

View file

@ -0,0 +1,331 @@
/*******************************************************************************
* Copyright (c) 2000, 2016 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
* William Riley (Renesas) - Adapted for CDT
*******************************************************************************/
package org.eclipse.cdt.internal.ui.text.c.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.cdt.internal.ui.text.c.hover.AnnotationExpansionControl.AnnotationHoverInput;
/**
* This class was copied from org.eclipse.jdt.internal.ui.text.java.hover.AnnotationExpansionControl
*
* @since 6.1
*/
public class AnnotationExpandHover implements IAnnotationHover, IAnnotationHoverExtension {
private class InformationControlCreator implements IInformationControlCreator, IInformationControlCreatorExtension {
/*
* @see org.eclipse.jface.text.IInformationControlCreator#createInformationControl(org.eclipse.swt.widgets.Shell)
*/
@Override
public IInformationControl createInformationControl(Shell parent) {
return new AnnotationExpansionControl(parent, SWT.NONE, fAnnotationAccess);
}
/*
* @see org.eclipse.jface.text.IInformationControlCreatorExtension#canReuse(org.eclipse.jface.text.IInformationControl)
*/
@Override
public boolean canReuse(IInformationControl control) {
return control instanceof AnnotationExpansionControl;
}
/*
* @see org.eclipse.jface.text.IInformationControlCreatorExtension#canReplace(org.eclipse.jface.text.IInformationControlCreator)
*/
@Override
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)
*/
@Override
public void annotationSelected(VerticalRulerEvent event) {
fCompositeRuler.fireAnnotationSelected(event);
}
/*
* @see org.eclipse.jface.text.source.IVerticalRulerListener#annotationDefaultSelected(org.eclipse.jface.text.source.VerticalRulerEvent)
*/
@Override
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)
*/
@Override
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)
*/
@Override
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<Annotation> exact= new ArrayList<>();
HashMap<Position, Object> messagesAtPosition= new HashMap<>();
Iterator<Annotation> e= model.getAnnotationIterator();
while (e.hasNext()) {
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= 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<Annotation> exact, final IAnnotationModel model) {
class AnnotationComparator implements Comparator<Annotation> {
/*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(Annotation a1, Annotation a2) {
int a1Offset = getAnnotationOffsetForSort(model, a1);
int a2Offset = getAnnotationOffsetForSort(model, a2);
// annotation order:
// primary order: by position in line
// secondary: annotation importance
if (a1Offset == a2Offset)
return getOrder(a2) - getOrder(a1);
return a1Offset - a2Offset;
}
}
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<Position, Object> messagesAtPosition, Position position, String message) {
if (message == null)
return false;
if (messagesAtPosition.containsKey(position)) {
Object value= messagesAtPosition.get(position);
if (message.equals(value))
return true;
if (value instanceof List) {
@SuppressWarnings("unchecked")
List<String> messages= ((List<String>)value);
if (messages.contains(message))
return true;
messages.add(message);
} else {
ArrayList<Object> 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()
*/
@Override
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)
*/
@Override
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)
*/
@Override
public ILineRange getHoverLineRange(ISourceViewer viewer, int lineNumber) {
return new LineRange(lineNumber, 1);
}
/*
* @see org.eclipse.jface.text.source.IAnnotationHoverExtension#canHandleMouseCursor()
*/
@Override
public boolean canHandleMouseCursor() {
return true;
}
/**
* Get the offset of an annotation in the model.
* <p>
* Allows extenders to override ordering of annotations
* @param model Annotation model
* @param a Annotation to get offset of
* @return Offset. Larger values are displayed later in the hover
*/
protected int getAnnotationOffsetForSort(IAnnotationModel model, Annotation a) {
return model.getPosition(a).offset;
}
}

View file

@ -0,0 +1,955 @@
/*******************************************************************************
* Copyright (c) 2000, 2016 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
* William Riley (Renesas) - Adapted for CDT
*******************************************************************************/
package org.eclipse.cdt.internal.ui.text.c.hover;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.MenuListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.text.AbstractInformationControlManager;
import org.eclipse.jface.text.DefaultInformationControl;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IInformationControlExtension;
import org.eclipse.jface.text.IInformationControlExtension2;
import org.eclipse.jface.text.IInformationControlExtension5;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.IViewportListener;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationAccessExtension;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
import org.eclipse.jface.text.source.IVerticalRulerListener;
import org.eclipse.jface.text.source.VerticalRulerEvent;
/**
* A control that can display a number of annotations. The control can decide how it layouts the
* annotations to present them to the user.
* <p>
* This class was copied from org.eclipse.jdt.internal.ui.text.java.hover.AnnotationExpansionControl
* </p>
* <p>Each annotation can have its custom context menu and hover.</p>
*
* @since 6.1
*/
public class AnnotationExpansionControl implements IInformationControl, IInformationControlExtension, IInformationControlExtension2, IInformationControlExtension5 {
public interface ICallback {
void run(IInformationControlExtension2 control);
}
/**
* Input used by the control to display the annotations.
* TODO move to top-level class
* TODO encapsulate fields
*
* @since 6.1
*/
public static class AnnotationHoverInput {
public Annotation[] fAnnotations;
public ISourceViewer fViewer;
public IVerticalRulerInfo fRulerInfo;
public IVerticalRulerListener fAnnotationListener;
public IDoubleClickListener fDoubleClickListener;
public ICallback redoAction;
public IAnnotationModel model;
}
private final class Item {
Annotation fAnnotation;
Canvas canvas;
StyleRange[] oldStyles;
public void selected() {
Display disp= fShell.getDisplay();
canvas.setCursor(getHandCursor(disp));
// TODO: shade - for now: set grey background
canvas.setBackground(getSelectionColor(disp));
// highlight the viewer background at its position
oldStyles= setViewerBackground(fAnnotation);
// set the selection
fSelection= this;
if (fHoverManager == null) {
fHoverManager= createHoverManager(fComposite, true);
} else {
fHoverManager.showInformation();
}
if (fInput.fAnnotationListener != null) {
VerticalRulerEvent event= new VerticalRulerEvent(fAnnotation);
fInput.fAnnotationListener.annotationSelected(event);
}
}
public void defaultSelected(MouseEvent e) {
if (fInput.fAnnotationListener != null) {
Event swtEvent= new Event();
swtEvent.type= SWT.MouseDown;
swtEvent.display= e.display;
swtEvent.widget= e.widget;
swtEvent.time= e.time;
swtEvent.data= e.data;
swtEvent.x= e.x;
swtEvent.y= e.y;
swtEvent.button= e.button;
swtEvent.stateMask= e.stateMask;
swtEvent.count= e.count;
VerticalRulerEvent event= new VerticalRulerEvent(fAnnotation, swtEvent);
fInput.fAnnotationListener.annotationDefaultSelected(event);
}
dispose();
}
public void deselect() {
// hide the popup
// fHoverManager.disposeInformationControl();
// deselect
fSelection= null;
resetViewerBackground(oldStyles);
oldStyles= null;
Display disp= fShell.getDisplay();
canvas.setCursor(null);
// TODO: remove shading - for now: set standard background
canvas.setBackground(disp.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
}
}
/**
* Disposes of an item
*/
private final static class MyDisposeListener implements DisposeListener {
/*
* @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
*/
@Override
public void widgetDisposed(DisposeEvent e) {
Item item= (Item) ((Widget) e.getSource()).getData();
item.deselect();
item.canvas= null;
item.fAnnotation= null;
item.oldStyles= null;
((Widget) e.getSource()).setData(null);
}
}
/**
* Listener on context menu invocation on the items
*/
private final class MyMenuDetectListener implements Listener {
/*
* @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
*/
@Override
public void handleEvent(Event event) {
if (event.type == SWT.MenuDetect) {
// TODO: show per-item menu
// for now: show ruler context menu
if (fInput != null) {
Control ruler= fInput.fRulerInfo.getControl();
if (ruler != null && !ruler.isDisposed()) {
Menu menu= ruler.getMenu();
if (menu != null && !menu.isDisposed()) {
menu.setLocation(event.x, event.y);
menu.addMenuListener(new MenuListener() {
@Override
public void menuHidden(MenuEvent e) {
dispose();
}
@Override
public void menuShown(MenuEvent e) {
}
});
menu.setVisible(true);
}
}
}
}
}
}
/**
* Listener on mouse events on the items.
*/
private final class MyMouseListener extends MouseAdapter {
/*
* @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
*/
@Override
public void mouseDoubleClick(MouseEvent e) {
Item item= (Item) ((Widget) e.getSource()).getData();
if (e.button == 1 && item.fAnnotation == fInput.fAnnotations[0] && fInput.fDoubleClickListener != null) {
fInput.fDoubleClickListener.doubleClick(null);
// special code for JDT to renew the annotation set.
if (fInput.redoAction != null)
fInput.redoAction.run(AnnotationExpansionControl.this);
}
// dispose();
// TODO special action to invoke double-click action on the vertical ruler
// how about
// Canvas can= (Canvas) e.getSource();
// Annotation a= (Annotation) can.getData();
// if (a != null) {
// a.getDoubleClickAction().run();
// }
}
/*
* JDT uses mouseDown here rather than mouseUp to fix a bug
* (details see https://bugs.eclipse.org/bugs/show_bug.cgi?id=165533)
*
* However this causes an issue where the top annotation is fired if the user
* click the 1st item in the expansion control. Due to mouseUp going to the ruler
* after the expansion is close.
*
* Bug as described in JDT does not seems to affect CDT so reverting to mouseUp.
*
* @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
*/
@Override
public void mouseUp(MouseEvent e) {
Item item= (Item) ((Widget) e.getSource()).getData();
// TODO for now, to make double click work: disable single click on the first item
// disable later when the annotationlistener selectively handles input
if (item != null && e.button == 1) // && item.fAnnotation != fInput.fAnnotations[0])
item.defaultSelected(e);
}
}
/**
* Listener on mouse track events on the items.
*/
private final class MyMouseTrackListener implements MouseTrackListener {
/*
* @see org.eclipse.swt.events.MouseTrackListener#mouseEnter(org.eclipse.swt.events.MouseEvent)
*/
@Override
public void mouseEnter(MouseEvent e) {
Item item= (Item) ((Widget) e.getSource()).getData();
if (item != null)
item.selected();
}
/*
* @see org.eclipse.swt.events.MouseTrackListener#mouseExit(org.eclipse.swt.events.MouseEvent)
*/
@Override
public void mouseExit(MouseEvent e) {
Item item= (Item) ((Widget) e.getSource()).getData();
if (item != null)
item.deselect();
// if the event lies outside the entire popup, dispose
org.eclipse.swt.graphics.Region region= fShell.getRegion();
Canvas can= (Canvas) e.getSource();
Point p= can.toDisplay(e.x, e.y);
if (region == null) {
Rectangle bounds= fShell.getBounds();
// p= fShell.toControl(p);
if (!bounds.contains(p))
dispose();
} else {
p= fShell.toControl(p);
if (!region.contains(p))
dispose();
}
}
/*
* @see org.eclipse.swt.events.MouseTrackListener#mouseHover(org.eclipse.swt.events.MouseEvent)
*/
@Override
public void mouseHover(MouseEvent e) {
if (fHoverManager == null) {
fHoverManager= createHoverManager(fComposite, true);
}
}
}
private HoverManager createHoverManager(Composite target, boolean show) {
HoverManager hoverManager = new HoverManager();
hoverManager.takesFocusWhenVisible(false);
hoverManager.install(target);
if(show) {
hoverManager.showInformation();
}
return hoverManager;
}
/**
* @since 6.1
*/
public class LinearLayouter {
private static final int ANNOTATION_SIZE= 14;
private static final int BORDER_WIDTH= 2;
public Layout getLayout(int itemCount) {
// simple layout: a row of items
GridLayout layout= new GridLayout(itemCount, true);
layout.horizontalSpacing= 1;
layout.verticalSpacing= 0;
layout.marginHeight= 1;
layout.marginWidth= 1;
return layout;
}
public Object getLayoutData() {
GridData gridData= new GridData(ANNOTATION_SIZE + 2 * BORDER_WIDTH, ANNOTATION_SIZE + 2 * BORDER_WIDTH);
gridData.horizontalAlignment= GridData.CENTER;
gridData.verticalAlignment= GridData.CENTER;
return gridData;
}
public int getAnnotationSize() {
return ANNOTATION_SIZE;
}
public int getBorderWidth() {
return BORDER_WIDTH;
}
/**
* Gets the shell region for the given number of items.
*
* @param itemCount the item count
* @return the shell region
*/
public org.eclipse.swt.graphics.Region getShellRegion(int itemCount) {
// no special region - set to null for default shell size
return null;
}
}
/**
* Listener on paint events on the items. Paints the annotation image on the given <code>GC</code>.
*/
private final class MyPaintListener implements PaintListener {
/*
* @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
*/
@Override
public void paintControl(PaintEvent e) {
Canvas can= (Canvas) e.getSource();
Annotation a= ((Item) can.getData()).fAnnotation;
if (a != null) {
Rectangle rect= new Rectangle(fLayouter.getBorderWidth(), fLayouter.getBorderWidth(), fLayouter.getAnnotationSize(), fLayouter.getAnnotationSize());
if (fAnnotationAccessExtension != null)
fAnnotationAccessExtension.paint(a, e.gc, can, rect);
}
}
}
/**
* Our own private hover manager used to shop per-item pop-ups.
*/
private final class HoverManager extends AbstractInformationControlManager {
/**
*
*/
public HoverManager() {
super(new IInformationControlCreator() {
@Override
public IInformationControl createInformationControl(Shell parent) {
return new DefaultInformationControl(parent);
}
});
setMargins(5, 10);
setAnchor(ANCHOR_BOTTOM);
setFallbackAnchors(new Anchor[] {ANCHOR_BOTTOM, ANCHOR_LEFT, ANCHOR_RIGHT} );
}
/*
* @see org.eclipse.jface.text.AbstractInformationControlManager#computeInformation()
*/
@Override
protected void computeInformation() {
if (fSelection != null) {
Rectangle subjectArea= fSelection.canvas.getBounds();
Annotation annotation= fSelection.fAnnotation;
String msg;
if (annotation != null)
msg= annotation.getText();
else
msg= null;
setInformation(msg, subjectArea);
}
}
}
/** Model data. */
protected AnnotationHoverInput fInput;
/** The control's shell */
private Shell fShell;
/** The composite combining all the items. */
protected Composite fComposite;
/** The currently selected item, or <code>null</code> if none is selected. */
private Item fSelection;
/** The hover manager for the per-item hovers. */
private HoverManager fHoverManager;
/** The annotation access extension. */
private IAnnotationAccessExtension fAnnotationAccessExtension;
/* listener legion */
private final MyPaintListener fPaintListener;
private final MyMouseTrackListener fMouseTrackListener;
private final MyMouseListener fMouseListener;
private final MyMenuDetectListener fMenuDetectListener;
private final DisposeListener fDisposeListener;
private final IViewportListener fViewportListener;
private LinearLayouter fLayouter;
/**
* Creates a new control.
*
* @param parent parent shell
* @param shellStyle additional style flags
* @param access the annotation access
*/
public AnnotationExpansionControl(Shell parent, int shellStyle, IAnnotationAccess access) {
fPaintListener= new MyPaintListener();
fMouseTrackListener= new MyMouseTrackListener();
fMouseListener= new MyMouseListener();
fMenuDetectListener= new MyMenuDetectListener();
fDisposeListener= new MyDisposeListener();
fViewportListener= new IViewportListener() {
@Override
public void viewportChanged(int verticalOffset) {
dispose();
}
};
fLayouter= new LinearLayouter();
if (access instanceof IAnnotationAccessExtension)
fAnnotationAccessExtension= (IAnnotationAccessExtension) access;
fShell= new Shell(parent, shellStyle | SWT.NO_FOCUS | SWT.ON_TOP);
Display display= fShell.getDisplay();
fShell.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
fComposite= new Composite(fShell, SWT.NO_FOCUS | SWT.NO_REDRAW_RESIZE | SWT.NO_TRIM);
// fComposite= new Composite(fShell, SWT.NO_FOCUS | SWT.NO_REDRAW_RESIZE | SWT.NO_TRIM | SWT.V_SCROLL);
GridLayout layout= new GridLayout(1, true);
layout.marginHeight= 0;
layout.marginWidth= 0;
fShell.setLayout(layout);
GridData data= new GridData(GridData.FILL_BOTH);
data.heightHint= fLayouter.getAnnotationSize() + 2 * fLayouter.getBorderWidth() + 4;
fComposite.setLayoutData(data);
fComposite.addMouseTrackListener(new MouseTrackAdapter() {
@Override
public void mouseExit(MouseEvent e) {
if (fComposite == null)
return;
Control[] children= fComposite.getChildren();
Rectangle bounds= null;
for (int i= 0; i < children.length; i++) {
if (bounds == null)
bounds= children[i].getBounds();
else
bounds.add(children[i].getBounds());
if (bounds.contains(e.x, e.y))
return;
}
// if none of the children contains the event, we leave the popup
dispose();
}
});
// fComposite.getVerticalBar().addListener(SWT.Selection, new Listener() {
//
// public void handleEvent(Event event) {
// Rectangle bounds= fShell.getBounds();
// int x= bounds.x - fLayouter.getAnnotationSize() - fLayouter.getBorderWidth();
// int y= bounds.y;
// fShell.setBounds(x, y, bounds.width, bounds.height);
// }
//
// });
Cursor handCursor= getHandCursor(display);
fShell.setCursor(handCursor);
fComposite.setCursor(handCursor);
setInfoSystemColor();
}
private void setInfoSystemColor() {
Display display= fShell.getDisplay();
setForegroundColor(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
setBackgroundColor(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
}
/*
* @see org.eclipse.jface.text.IInformationControl#setInformation(java.lang.String)
*/
@Override
public void setInformation(String information) {
setInput(null);
}
/*
* @see org.eclipse.jface.text.IInformationControlExtension2#setInput(java.lang.Object)
*/
@Override
public void setInput(Object input) {
if (fInput != null && fInput.fViewer != null)
fInput.fViewer.removeViewportListener(fViewportListener);
if (input instanceof AnnotationHoverInput)
fInput= (AnnotationHoverInput) input;
else
fInput= null;
inputChanged(fInput, null);
}
/**
* Internal hook method called when the input is
* initially set or subsequently changed.
*
* @param newInput the new input
* @param newSelection the new selection
*/
protected void inputChanged(Object newInput, Object newSelection) {
refresh();
}
protected void refresh() {
adjustItemNumber();
if (fInput == null)
return;
if (fInput.fAnnotations == null)
return;
if (fInput.fViewer != null)
fInput.fViewer.addViewportListener(fViewportListener);
fShell.setRegion(fLayouter.getShellRegion(fInput.fAnnotations.length));
Layout layout= fLayouter.getLayout(fInput.fAnnotations.length);
fComposite.setLayout(layout);
Control[] children= fComposite.getChildren();
for (int i= 0; i < fInput.fAnnotations.length; i++) {
Canvas canvas= (Canvas) children[i];
Item item= new Item();
item.canvas= canvas;
item.fAnnotation= fInput.fAnnotations[i];
canvas.setData(item);
canvas.redraw();
}
}
protected void adjustItemNumber() {
if (fComposite == null)
return;
Control[] children= fComposite.getChildren();
int oldSize= children.length;
int newSize= fInput == null ? 0 : fInput.fAnnotations.length;
Display display= fShell.getDisplay();
// add missing items
for (int i= oldSize; i < newSize; i++) {
Canvas canvas= new Canvas(fComposite, SWT.NONE);
Object gridData= fLayouter.getLayoutData();
canvas.setLayoutData(gridData);
canvas.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
canvas.addPaintListener(fPaintListener);
canvas.addMouseTrackListener(fMouseTrackListener);
canvas.addMouseListener(fMouseListener);
canvas.addListener(SWT.MenuDetect, fMenuDetectListener);
canvas.addDisposeListener(fDisposeListener);
}
// dispose of exceeding resources
for (int i= oldSize; i > newSize; i--) {
Item item= (Item) children[i - 1].getData();
item.deselect();
children[i - 1].dispose();
}
}
/*
* @see IInformationControl#setVisible(boolean)
*/
@Override
public void setVisible(boolean visible) {
fShell.setVisible(visible);
if(visible) {
/*
* Force 1st item to be selected when made visible
*
* This causes the tooltip to be displayed without additional
* delay.
*/
Control[] children= fComposite.getChildren();
if(fHoverManager == null && children.length > 0) {
Object data = children[0].getData();
if(data instanceof Item) {
((Item)data).selected();
}
}
}
}
/*
* @see IInformationControl#dispose()
*/
@Override
public void dispose() {
if (fShell != null) {
if (!fShell.isDisposed())
fShell.dispose();
fShell= null;
fComposite= null;
if (fHoverManager != null)
fHoverManager.dispose();
fHoverManager= null;
fSelection= null;
}
}
/*
* @see org.eclipse.jface.text.IInformationControlExtension#hasContents()
*/
@Override
public boolean hasContents() {
return fInput.fAnnotations != null && fInput.fAnnotations.length > 0;
}
/*
* @see org.eclipse.jface.text.IInformationControl#setSizeConstraints(int, int)
*/
@Override
public void setSizeConstraints(int maxWidth, int maxHeight) {
//fMaxWidth= maxWidth;
//fMaxHeight= maxHeight;
}
/*
* @see org.eclipse.jface.text.IInformationControl#computeSizeHint()
*/
@Override
public Point computeSizeHint() {
return fShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
/*
* @see IInformationControl#setLocation(Point)
*/
@Override
public void setLocation(Point location) {
fShell.setLocation(location);
}
/*
* @see IInformationControl#setSize(int, int)
*/
@Override
public void setSize(int width, int height) {
fShell.setSize(width, height);
}
/*
* @see IInformationControl#addDisposeListener(DisposeListener)
*/
@Override
public void addDisposeListener(DisposeListener listener) {
fShell.addDisposeListener(listener);
}
/*
* @see IInformationControl#removeDisposeListener(DisposeListener)
*/
@Override
public void removeDisposeListener(DisposeListener listener) {
fShell.removeDisposeListener(listener);
}
/*
* @see IInformationControl#setForegroundColor(Color)
*/
@Override
public void setForegroundColor(Color foreground) {
fComposite.setForeground(foreground);
}
/*
* @see IInformationControl#setBackgroundColor(Color)
*/
@Override
public void setBackgroundColor(Color background) {
fComposite.setBackground(background);
}
/*
* @see IInformationControl#isFocusControl()
*/
@Override
public boolean isFocusControl() {
return fShell.getDisplay().getActiveShell() == fShell;
}
/*
* @see IInformationControl#setFocus()
*/
@Override
public void setFocus() {
fShell.forceFocus();
}
/*
* @see IInformationControl#addFocusListener(FocusListener)
*/
@Override
public void addFocusListener(FocusListener listener) {
fShell.addFocusListener(listener);
}
/*
* @see IInformationControl#removeFocusListener(FocusListener)
*/
@Override
public void removeFocusListener(FocusListener listener) {
fShell.removeFocusListener(listener);
}
private StyleRange[] setViewerBackground(Annotation annotation) {
StyledText text= fInput.fViewer.getTextWidget();
if (text == null || text.isDisposed())
return null;
Display disp= text.getDisplay();
Position pos= fInput.model.getPosition(annotation);
if (pos == null)
return null;
IRegion region= ((TextViewer)fInput.fViewer).modelRange2WidgetRange(new Region(pos.offset, pos.length));
if (region == null)
return null;
StyleRange[] ranges= text.getStyleRanges(region.getOffset(), region.getLength());
List<StyleRange> undoRanges= new ArrayList<>(ranges.length);
for (int i= 0; i < ranges.length; i++) {
undoRanges.add((StyleRange)ranges[i].clone());
}
int offset= region.getOffset();
StyleRange current= undoRanges.size() > 0 ? undoRanges.get(0) : null;
int curStart= current != null ? current.start : region.getOffset() + region.getLength();
int curEnd= current != null ? current.start + current.length : -1;
int index= 0;
// fill no-style regions
while (curEnd < region.getOffset() + region.getLength()) {
// add empty range
if (curStart > offset) {
StyleRange undoRange= new StyleRange(offset, curStart - offset, null, null);
undoRanges.add(index, undoRange);
index++;
}
// step
index++;
if (index < undoRanges.size()) {
offset= curEnd;
current= undoRanges.get(index);
curStart= current.start;
curEnd= current.start + current.length;
} else if (index == undoRanges.size()) {
// last one
offset= curEnd;
current= null;
curStart= region.getOffset() + region.getLength();
curEnd= -1;
} else
curEnd= region.getOffset() + region.getLength();
}
// create modified styles (with background)
List<StyleRange> shadedRanges= new ArrayList<>(undoRanges.size());
for (Iterator<StyleRange> it= undoRanges.iterator(); it.hasNext(); ) {
StyleRange range= (StyleRange) it.next().clone();
shadedRanges.add(range);
range.background= getHighlightColor(disp);
}
// set the ranges one by one
for (Iterator<StyleRange> iter= shadedRanges.iterator(); iter.hasNext(); ) {
text.setStyleRange(iter.next());
}
return undoRanges.toArray(undoRanges.toArray(new StyleRange[0]));
}
private void resetViewerBackground(StyleRange[] oldRanges) {
if (oldRanges == null)
return;
if (fInput == null)
return;
StyledText text= fInput.fViewer.getTextWidget();
if (text == null || text.isDisposed())
return;
// set the ranges one by one
for (int i= 0; i < oldRanges.length; i++) {
text.setStyleRange(oldRanges[i]);
}
}
private Color getHighlightColor(Display disp) {
return disp.getSystemColor(SWT.COLOR_GRAY);
}
private Color getSelectionColor(Display disp) {
return disp.getSystemColor(SWT.COLOR_GRAY);
}
private Cursor getHandCursor(Display disp) {
return disp.getSystemCursor(SWT.CURSOR_HAND);
}
/*
* @see org.eclipse.jface.text.IInformationControlExtension5#computeSizeConstraints(int, int)
* @since 3.4
*/
@Override
public Point computeSizeConstraints(int widthInChars, int heightInChars) {
return null;
}
/*
* @see org.eclipse.jface.text.IInformationControlExtension5#containsControl(org.eclipse.swt.widgets.Control)
* @since 3.4
*/
@Override
public boolean containsControl(Control control) {
do {
if (control == fShell)
return true;
if (control instanceof Shell)
return false;
control= control.getParent();
} while (control != null);
return false;
}
/*
* @see org.eclipse.jface.text.IInformationControlExtension5#getInformationPresenterControlCreator()
* @since 3.4
*/
@Override
public IInformationControlCreator getInformationPresenterControlCreator() {
return null;
}
/*
* @see org.eclipse.jface.text.IInformationControlExtension5#isVisible()
*/
@Override
public boolean isVisible() {
return fShell != null && !fShell.isDisposed() && fShell.isVisible();
}
}

View file

@ -0,0 +1,234 @@
/*******************************************************************************
* Copyright (c) 2000, 2016 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
* William Riley (Renesas) - Adapted for CDT
*******************************************************************************/
package org.eclipse.cdt.internal.ui.text.c.hover;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IInformationControlExtension2;
import org.eclipse.jface.text.Position;
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.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationPresentation;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ImageUtilities;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.AnnotationPreferenceLookup;
import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.CDTSharedImages;
import org.eclipse.cdt.internal.ui.editor.CDocumentProvider.ProblemAnnotation;
import org.eclipse.cdt.internal.ui.editor.ICAnnotation;
import org.eclipse.cdt.internal.ui.editor.CMarkerAnnotation;
import org.eclipse.cdt.internal.ui.text.c.hover.AnnotationExpansionControl.AnnotationHoverInput;
/**
* Originally copied from org.eclipse.jdt.internal.ui.text.java.hover.JavaExpandHover
* @since 6.1
*/
public class CExpandHover extends AnnotationExpandHover {
/** Id of CDT Breakpoint annotation type */
private static final String ANNOTATION_TYPE_BREAKPOINT = "org.eclipse.cdt.debug.core.breakpoint"; //$NON-NLS-1$
/** Id of the no breakpoint fake annotation */
public static final String NO_BREAKPOINT_ANNOTATION= "org.eclipse.cdt.internal.ui.NoBreakpointAnnotation"; //$NON-NLS-1$
private static class NoBreakpointAnnotation extends Annotation implements IAnnotationPresentation {
public NoBreakpointAnnotation() {
super(NO_BREAKPOINT_ANNOTATION, false, CHoverMessages.CExpandHover_tooltip_noBreakpoint);
}
/*
* @see org.eclipse.jface.text.source.IAnnotationPresentation#paint(org.eclipse.swt.graphics.GC, org.eclipse.swt.widgets.Canvas, org.eclipse.swt.graphics.Rectangle)
*/
@Override
public void paint(GC gc, Canvas canvas, Rectangle bounds) {
// draw affordance so the user know she can click here to get a breakpoint
Image fImage= CDTSharedImages.getImage(CDTSharedImages.IMG_OBJS_PUBLIC_FIELD);
ImageUtilities.drawImage(fImage, gc, canvas, bounds, SWT.CENTER);
}
/*
* @see org.eclipse.jface.text.source.IAnnotationPresentation#getLayer()
*/
@Override
public int getLayer() {
return IAnnotationPresentation.DEFAULT_LAYER;
}
}
private AnnotationPreferenceLookup fLookup= new AnnotationPreferenceLookup();
private IPreferenceStore fStore= CUIPlugin.getDefault().getCombinedPreferenceStore();
public CExpandHover(CompositeRuler ruler, IAnnotationAccess access, IDoubleClickListener doubleClickListener) {
super(ruler, access, doubleClickListener);
}
/*
* @see org.eclipse.ui.internal.texteditor.AnnotationExpandHover#getHoverInfoForLine(org.eclipse.jface.text.source.ISourceViewer, int)
*/
@Override
protected Object getHoverInfoForLine(final ISourceViewer viewer, final int line) {
//Use EDITOR_EVALUATE_TEMPORARY_PROBLEMS rather than EDITOR_CORRECTION_INDICATION as EDITOR_CORRECTION_INDICATION is not used in CDT
final boolean showTemporaryProblems= PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_EVALUATE_TEMPORARY_PROBLEMS);
IAnnotationModel model= viewer.getAnnotationModel();
IDocument document= viewer.getDocument();
if (model == null)
return null;
List<Annotation> exact= new ArrayList<>();
HashMap<Position, Object> messagesAtPosition= new HashMap<>();
Iterator<Annotation> e= model.getAnnotationIterator();
while (e.hasNext()) {
Annotation annotation= e.next();
if (fAnnotationAccess instanceof IAnnotationAccessExtension)
if (!((IAnnotationAccessExtension)fAnnotationAccess).isPaintable(annotation))
continue;
if (annotation instanceof ICAnnotation && !isIncluded((ICAnnotation)annotation, showTemporaryProblems))
continue;
AnnotationPreference pref= fLookup.getAnnotationPreference(annotation);
if (pref != null) {
String key= pref.getVerticalRulerPreferenceKey();
if (key != null && !fStore.getBoolean(key))
continue;
}
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);
}
}
sort(exact, model);
if (exact.size() > 0)
setLastRulerMouseLocation(viewer, line);
if (exact.size() > 0) {
if (!containsBreakpointAnnotation(exact)) {
//Add dummy annotation last if no breakpoint present
exact.add(new NoBreakpointAnnotation());
}
}
if (exact.size() <= 1)
return null;
AnnotationHoverInput input= new AnnotationHoverInput();
input.fAnnotations= exact.toArray(new Annotation[0]);
input.fViewer= viewer;
input.fRulerInfo= fCompositeRuler;
input.fAnnotationListener= fgListener;
input.fDoubleClickListener= fDblClickListener;
input.redoAction= new AnnotationExpansionControl.ICallback() {
@Override
public void run(IInformationControlExtension2 control) {
control.setInput(getHoverInfoForLine(viewer, line));
}
};
input.model= model;
return input;
}
private boolean isIncluded(ICAnnotation annotation, boolean showTemporaryProblems) {
// XXX: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=138601
if (annotation instanceof ProblemAnnotation && CMarkerAnnotation.TASK_ANNOTATION_TYPE.equals(annotation.getType()))
return false;
if (!annotation.isProblem())
return true;
if (annotation.isMarkedDeleted() && !annotation.hasOverlay())
return true;
if (annotation.hasOverlay() && !annotation.isMarkedDeleted())
return true;
if (annotation.hasOverlay())
return (!isIncluded(annotation.getOverlay(), showTemporaryProblems));
//JDT only shows annotations problems corrections, for CDT show all.
return showTemporaryProblems;
}
@Override
protected int getAnnotationOffsetForSort(IAnnotationModel model, Annotation a) {
if(this.isBreakpointAnnotation(a)) {
return Integer.MAX_VALUE; //Force breakpoints to end
} else {
return model.getPosition(a).offset;
}
}
/*
* @see org.eclipse.ui.internal.texteditor.AnnotationExpandHover#getOrder(org.eclipse.jface.text.source.Annotation)
*/
@Override
protected int getOrder(Annotation annotation) {
if (isBreakpointAnnotation(annotation))
return 0;//Force breakpoints to end. Usability improvement over JDT based on feedback
else
return super.getOrder(annotation);
}
private boolean isBreakpointAnnotation(Annotation a) {
return a.getType().equals(ANNOTATION_TYPE_BREAKPOINT);
}
private boolean containsBreakpointAnnotation(List<Annotation> annotations) {
for(Annotation a : annotations) {
if(isBreakpointAnnotation(a)) {
return true;
}
}
return false;
}
}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2002, 2010 QNX Software Systems and others.
* Copyright (c) 2002, 2016 QNX Software Systems 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
@ -20,6 +20,7 @@ public final class CHoverMessages extends NLS {
public static String AbstractAnnotationHover_message_singleQuickFix;
public static String AbstractAnnotationHover_message_multipleQuickFix;
public static String CExpandHover_tooltip_noBreakpoint;
public static String CMacroExpansionControl_exploreMacroExpansion;
public static String CMacroExpansionControl_statusText;

View file

@ -1,5 +1,5 @@
###############################################################################
# Copyright (c) 2000, 2009 IBM Corporation and others.
# Copyright (c) 2000, 2016 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
@ -14,6 +14,7 @@ AbstractAnnotationHover_action_configureAnnotationPreferences= Configure Annotat
AbstractAnnotationHover_message_singleQuickFix= 1 quick fix available:
AbstractAnnotationHover_message_multipleQuickFix= {0} quick fixes available:
CExpandHover_tooltip_noBreakpoint=Add a breakpoint
CMacroExpansionControl_exploreMacroExpansion=Press "{0}" for macro expansion steps
CMacroExpansionControl_statusText=Press {0} or {1} to step through macro expansion
CMacroExpansionControl_title_expansion=Expansion \#{0} of {1}

View file

@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2005, 2015 IBM Corporation and others.
* Copyright (c) 2005, 2016 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
@ -2113,6 +2113,17 @@ public class PreferenceConstants {
*/
public static final String INCLUDE_STYLE_MATCHING_PATTERN = "includeStyle.matchingPattern"; //$NON-NLS-1$
/**
* A named preference that controls whether annotation roll over is used or not.
* <p>
* Value is of type <code>Boolean</code>. If <code>true</code> the annotation ruler column
* uses a roll over to display multiple annotations
* </p>
*
* @since 6.1
*/
public static final String EDITOR_ANNOTATION_ROLL_OVER= "editor_annotation_roll_over"; //$NON-NLS-1$
/**
* Returns the CDT-UI preference store.
*
@ -2142,6 +2153,8 @@ public class PreferenceConstants {
store.setDefault(EDITOR_SOURCE_HOVER_BACKGROUND_COLOR_SYSTEM_DEFAULT, true);
store.setDefault(EDITOR_ANNOTATION_ROLL_OVER, false);
// Syntax highlighting
store.setDefault(EDITOR_MULTI_LINE_COMMENT_BOLD, false);
store.setDefault(EDITOR_MULTI_LINE_COMMENT_ITALIC, false);