/**
* Copyright (c) 2016 by Brainwy Software LTDA. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*
* Author: Mark Leone
* Created: Feb 11, 2016
*
* Loosely follows the JDT implementation of a best match hover. Code for obtaining
* and configuring contributed Hovers was copied from <code>BestMatchHover</code>,
* but this implementation combines Hover info in priority order, whereas the JDT
* implementation chooses the best fit hover.
*/
package org.python.pydev.editor.hover;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.python.pydev.editor.PyInformationPresenter;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.shared_core.string.FastStringBuffer;
public class PydevCombiningHover extends AbstractPyEditorTextHover {
public static final Object ID_DEFAULT_COMBINING_HOVER = "org.python.pydev.editor.hover.defaultCombiningHover";
// Note that the specification is static and is redone when the preferences are changed.
private static List<PyEditorTextHoverDescriptor> fTextHoverSpecifications;
private static List<AbstractPyEditorTextHover> fInstantiatedTextHovers;
private Map<AbstractPyEditorTextHover, PyEditorTextHoverDescriptor> hoverMap = new HashMap<>();
boolean preempt = false;
Integer currentPriority = null;
boolean contentTypeSupported = false;
protected ITextViewer viewer;
private static final String DIVIDER_CHAR = Character.toString((char) 0xfeff2015);
public PydevCombiningHover() {
installTextHovers();
this.addInformationPresenterControlListener(new ControlListener() {
@Override
public void controlMoved(ControlEvent e) {
}
@Override
public void controlResized(ControlEvent e) {
if (hoverControlPreferredWidth != null) {
informationControl.setSize(hoverControlPreferredWidth, informationControl.getBounds().height);
}
hoverControlWidth = informationControl.getBounds().width;
}
});
}
/**
* Installs all text hovers.
*/
public static void installTextHovers() {
// initialize lists - indicates that the initialization happened
fTextHoverSpecifications = new ArrayList<PyEditorTextHoverDescriptor>(5);
fInstantiatedTextHovers = new ArrayList<AbstractPyEditorTextHover>(5);
// populate list
PyEditorTextHoverDescriptor[] hoverDescs = PydevPlugin.getDefault().getPyEditorTextHoverDescriptors();
for (PyEditorTextHoverDescriptor desc : hoverDescs) {
// ensure that we don't add ourselves to the list
if (!ID_DEFAULT_COMBINING_HOVER.equals(desc.getId())) {
fTextHoverSpecifications.add(desc);
}
}
}
private void checkTextHovers() {
if (fTextHoverSpecifications == null) {
return;
}
hoverMap.clear();
List<PyEditorTextHoverDescriptor> specifications = fTextHoverSpecifications;
fTextHoverSpecifications = null;
for (PyEditorTextHoverDescriptor spec : specifications) {
if (spec == null) {
continue;
}
AbstractPyEditorTextHover hover = spec.createTextHover();
if (hover != null) {
fInstantiatedTextHovers.add(hover);
hoverMap.put(hover, spec);
}
}
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.text.ITextHover#getHoverInfo(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion)
*/
@Override
public String getHoverInfo(final ITextViewer textViewer, IRegion hoverRegion) {
this.viewer = textViewer;
final FastStringBuffer buf = new FastStringBuffer();
checkTextHovers();
if (fInstantiatedTextHovers == null) {
return null;
}
boolean firstHoverInfo = true;
//hovers are sorted by priority in descending order
for (final AbstractPyEditorTextHover hover : fInstantiatedTextHovers) {
if (hover == null) {
continue;
}
PyEditorTextHoverDescriptor descr = hoverMap.get(hover);
if (!descr.isEnabled()) {
continue;
}
if (hoverMap.get(hover) != null) {
if (currentPriority == null) {
currentPriority = descr.getPriority();
}
if (descr.getPriority().equals(currentPriority) || !preempt) {
@SuppressWarnings("deprecation")
final String hoverText = hover.getHoverInfo(textViewer, hoverRegion);
if (hoverText != null && hoverText.trim().length() > 0) {
if (!firstHoverInfo && PyHoverPreferencesPage.getUseHoverDelimiters()) {
buf.append(PyInformationPresenter.LINE_DELIM);
buf.appendN(DIVIDER_CHAR, 20);
buf.append(PyInformationPresenter.LINE_DELIM);
} else if (buf.length() > 0) {
buf.append(PyInformationPresenter.LINE_DELIM);
}
buf.append(hoverText);
firstHoverInfo = false;
viewer.getTextWidget().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
checkHoverControlWidth(hover);
}
});
}
}
currentPriority = descr.getPriority();
/* If preempt has already been set, don't unset it if a hover with the same priority
* does not have preempt set
*/
if (!preempt) {
preempt = descr.isPreempt();
}
}
}
currentPriority = null;
preempt = false;
return buf.toString();
}
/**
* Ensures that the width of the control for this Hover is equal to the
* largest width, if any, set for contributing Hovers.
* @param hover a contributing Hover
*/
private void checkHoverControlWidth(AbstractPyEditorTextHover hover) {
if (hover.getHoverControlPreferredWidth() != null) {
if (this.hoverControlWidth == null) {
this.hoverControlPreferredWidth = hover.getHoverControlPreferredWidth();
if (informationControl != null) {
informationControl.setSize(hoverControlPreferredWidth, informationControl.getBounds().height);
}
} else if (hover.getHoverControlPreferredWidth() > this.hoverControlWidth) {
this.hoverControlPreferredWidth = hover.getHoverControlPreferredWidth();
if (informationControl != null) {
informationControl.setSize(hoverControlPreferredWidth, informationControl.getBounds().height);
}
}
}
}
/*
* (non-Javadoc)
* @see org.python.pydev.editor.hover.AbstractPyEditorTextHover#isContentTypeSupported(java.lang.String)
*/
@Override
public boolean isContentTypeSupported(String contentType) {
return true;
}
}