diff options
author | Markus Keller | 2008-05-13 16:50:23 +0000 |
---|---|---|
committer | Markus Keller | 2008-05-13 16:50:23 +0000 |
commit | 1a0d4a0970b4db3044663fa2594a5d5a74d95ce9 (patch) | |
tree | 76e8b6e9133329fc03029a0e7116a3ce04cb5b3b | |
parent | ed20ea32b5943811ded97e3d2f5c167284d747fc (diff) | |
download | eclipse.platform.text-1a0d4a0970b4db3044663fa2594a5d5a74d95ce9.tar.gz eclipse.platform.text-1a0d4a0970b4db3044663fa2594a5d5a74d95ce9.tar.xz eclipse.platform.text-1a0d4a0970b4db3044663fa2594a5d5a74d95ce9.zip |
112921: [content assist] Enhance additional info popup
13 files changed, 755 insertions, 396 deletions
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/AccessorUtil.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/AccessorUtil.java deleted file mode 100644 index dc43eea5780..00000000000 --- a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/AccessorUtil.java +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008 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.jface.internal.text; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - - -/** - * Helper class for calling non-accessible methods. - * - * @since 3.4 - */ -public class AccessorUtil { - - /** - * Invokes a zero-parameter method via reflection, making the method accessible first. - * - * @param targetObject the target object - * @param declaringClass the declaring class of the method to be called - * @param methodName the method name - * @return the result - * @throws RuntimeException if the invocation fails - */ - public static Object invoke(Object targetObject, Class declaringClass, String methodName) { - return invoke(targetObject, declaringClass, methodName, (Class[]) null, null); - } - - /** - * Invokes a single-parameter method via reflection, making the method accessible first. - * - * @param targetObject the target object - * @param declaringClass the declaring class of the method to be called - * @param methodName the method name - * @param parameterType0 the method's parameter type - * @param arg0 the argument used for the method call - * @return the result - * @throws RuntimeException if the invocation fails - */ - public static Object invoke(Object targetObject, Class declaringClass, String methodName, Class parameterType0, Object arg0) { - return invoke(targetObject, declaringClass, methodName, new Class[] { parameterType0 }, new Object[] { arg0 }); - } - - /** - * Invokes a method via reflection, making the method accessible first. - * - * @param targetObject the target object - * @param declaringClass the declaring class of the method to be called - * @param methodName the method name - * @param parameterTypes the method's parameter types, or <code>null</code> if none - * @param args the arguments used for the method call, or <code>null</code> if none - * @return the result - * @throws RuntimeException if the invocation fails - */ - public static Object invoke(Object targetObject, Class declaringClass, String methodName, Class[] parameterTypes, Object[] args) { - try { - Method method= declaringClass.getDeclaredMethod(methodName, parameterTypes); - method.setAccessible(true); - return method.invoke(targetObject, args); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - /** - * Reads the value of a field via reflection, making the field accessible first. - * - * @param targetObject the target object - * @param declaringClass the declaring class of the field to be read - * @param fieldName the field name - * @return the value of the field - * @throws RuntimeException if the invocation fails - */ - public static Object getValue(Object targetObject, Class declaringClass, String fieldName) { - try { - Field field= declaringClass.getDeclaredField(fieldName); - field.setAccessible(true); - return field.get(targetObject); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } -} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/DelayedInputChangeListener.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/DelayedInputChangeListener.java new file mode 100644 index 00000000000..496e5ce3ec2 --- /dev/null +++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/DelayedInputChangeListener.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2008 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.jface.internal.text; + +import org.eclipse.jface.text.IDelayedInputChangeProvider; +import org.eclipse.jface.text.IInputChangedListener; + + +/** + * A delayed input change listener that forwards delayed input changes to an information control replacer. + * + * @since 3.4 + */ +public final class DelayedInputChangeListener implements IInputChangedListener { + + private final IDelayedInputChangeProvider fChangeProvider; + private final InformationControlReplacer fInformationControlReplacer; + + /** + * Creates a new listener. + * + * @param changeProvider the information control with delayed input changes + * @param informationControlReplacer the information control replacer, whose information control should get the new input + */ + public DelayedInputChangeListener(IDelayedInputChangeProvider changeProvider, InformationControlReplacer informationControlReplacer) { + fChangeProvider= changeProvider; + fInformationControlReplacer= informationControlReplacer; + } + + /* + * @see org.eclipse.jface.text.IDelayedInputChangeListener#inputChanged(java.lang.Object) + */ + public void inputChanged(Object newInput) { + fChangeProvider.setDelayedInputChangeListener(null); + fInformationControlReplacer.setDelayedInput(newInput); + } +} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/IInformationControlReplacer.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/IInformationControlReplacer.java deleted file mode 100644 index c69756ae328..00000000000 --- a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/IInformationControlReplacer.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2007, 2008 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.jface.internal.text; - -import org.eclipse.swt.graphics.Rectangle; - -import org.eclipse.jface.text.AbstractInformationControlManager; -import org.eclipse.jface.text.IInformationControl; -import org.eclipse.jface.text.IInformationControlCreator; - - -/** - * An information control replacer can replace an - * {@link AbstractInformationControlManager}'s control. - * - * @see AbstractInformationControlManager#setInformationControlReplacer(IInformationControlReplacer) - * @since 3.4 - */ -public interface IInformationControlReplacer { - - /** - * Replace the information control. - * - * @param informationPresenterControlCreator the information presenter control creator - * @param contentBounds the bounds of the content area of the information control - * @param information the information to show - * @param subjectArea the subject area - * @param takeFocus <code>true</code> iff the replacing information control should take focus - */ - public void replaceInformationControl(IInformationControlCreator informationPresenterControlCreator, Rectangle contentBounds, Object information, Rectangle subjectArea, boolean takeFocus); - - /** - * Tells whether the replacer is currently replacing another information control. - * - * @return <code>true</code> while code from {@link #replaceInformationControl(IInformationControlCreator, Rectangle, Object, Rectangle, boolean)} is run - */ - public boolean isReplacing(); - - /** - * @return the current information control, or <code>null</code> if none available - */ - public IInformationControl getCurrentInformationControl2(); - - /** - * Disposes this information control replacer. - * <p> - * Can be called more than once. Calling - * {@link AbstractInformationControlManager#setInformationControlReplacer(IInformationControlReplacer)} - * will dispose its old replacer if set. - * </p> - * - * @see AbstractInformationControlManager - */ - public void dispose(); - - /** - * The number of pixels to blow up the keep-up zone. - * - * @return the margin in pixels - */ - public int getKeepUpMargin(); - - /** - * @param input the delayed input, or <code>null</code> to request cancellation - */ - public void setDelayedInput(Object input); -} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/InformationControlReplacer.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/InformationControlReplacer.java new file mode 100644 index 00000000000..eaa6e991b59 --- /dev/null +++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/InformationControlReplacer.java @@ -0,0 +1,206 @@ +/******************************************************************************* + * Copyright (c) 2008 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.jface.internal.text; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.jface.text.AbstractInformationControlManager; +import org.eclipse.jface.text.AbstractReusableInformationControlCreator; +import org.eclipse.jface.text.DefaultInformationControl; +import org.eclipse.jface.text.IInformationControl; +import org.eclipse.jface.text.IInformationControlCreator; +import org.eclipse.jface.text.IInformationControlExtension2; +import org.eclipse.jface.text.IInformationControlExtension3; +import org.eclipse.jface.util.Geometry; + + +/** + * An information control replacer can replace an + * {@link AbstractInformationControlManager}'s control. + * + * @see AbstractInformationControlManager#setInformationControlReplacer(InformationControlReplacer) + * @since 3.4 + */ +public class InformationControlReplacer extends AbstractInformationControlManager { + + /** + * Minimal width in pixels. + */ + private static final int MIN_WIDTH= 80; + /** + * Minimal height in pixels. + */ + private static final int MIN_HEIGHT= 50; + + /** + * Default control creator. + */ + protected static class DefaultInformationControlCreator extends AbstractReusableInformationControlCreator { + public IInformationControl doCreateInformationControl(Shell shell) { + return new DefaultInformationControl(shell, true); + } + } + + private boolean fIsReplacing; + private Object fReplacableInformation; + private boolean fDelayedInformationSet; + private Rectangle fReplaceableArea; + private Rectangle fContentBounds; + + + /** + * Creates a new information control replacer. + * + * @param creator the default information control creator + */ + public InformationControlReplacer(IInformationControlCreator creator) { + super(creator); + takesFocusWhenVisible(false); + } + + /** + * Replace the information control. + * + * @param informationPresenterControlCreator the information presenter control creator + * @param contentBounds the bounds of the content area of the information control + * @param information the information to show + * @param subjectArea the subject area + * @param takeFocus <code>true</code> iff the replacing information control should take focus + */ + public void replaceInformationControl(IInformationControlCreator informationPresenterControlCreator, Rectangle contentBounds, Object information, final Rectangle subjectArea, boolean takeFocus) { + + try { + fIsReplacing= true; + if (! fDelayedInformationSet) + fReplacableInformation= information; + else + takeFocus= true; // delayed input has been set, so the original info control must have been focused + fContentBounds= contentBounds; + fReplaceableArea= subjectArea; + + setCustomInformationControlCreator(informationPresenterControlCreator); + + takesFocusWhenVisible(takeFocus); + + showInformation(); + } finally { + fIsReplacing= false; + fReplacableInformation= null; + fDelayedInformationSet= false; + fReplaceableArea= null; + setCustomInformationControlCreator(null); + } + } + + /* + * @see org.eclipse.jface.text.AbstractInformationControlManager#computeInformation() + */ + protected void computeInformation() { + if (fIsReplacing && fReplacableInformation != null) { + setInformation(fReplacableInformation, fReplaceableArea); + return; + } + + if (DEBUG) + System.out.println("InformationControlReplacer: no active replaceable"); //$NON-NLS-1$ + } + + /** + * Opens the information control with the given information and the specified + * subject area. It also activates the information control closer. + * + * @param subjectArea the information area + * @param information the information + */ + public void showInformationControl(Rectangle subjectArea, Object information) { + IInformationControl informationControl= getInformationControl(); + + Rectangle controlBounds= fContentBounds; + if (informationControl instanceof IInformationControlExtension3) { + IInformationControlExtension3 iControl3= (IInformationControlExtension3) informationControl; + Rectangle trim= iControl3.computeTrim(); + controlBounds= Geometry.add(controlBounds, trim); + + /* + * Ensure minimal size. Interacting with a tiny information control + * (resizing, selecting text) would be a pain. + */ + controlBounds.width= Math.max(controlBounds.width, MIN_WIDTH); + controlBounds.height= Math.max(controlBounds.height, MIN_HEIGHT); + + getInternalAccessor().cropToClosestMonitor(controlBounds); + } + + Point location= Geometry.getLocation(controlBounds); + Point size= Geometry.getSize(controlBounds); + + // Caveat: some IInformationControls fail unless setSizeConstraints(..) is called with concrete values + informationControl.setSizeConstraints(size.x, size.y); + + if (informationControl instanceof IInformationControlExtension2) + ((IInformationControlExtension2) informationControl).setInput(information); + else + informationControl.setInformation(information.toString()); + + informationControl.setLocation(location); + informationControl.setSize(size.x, size.y); + + showInformationControl(subjectArea); + } + + /* + * @see org.eclipse.jface.text.AbstractInformationControlManager#hideInformationControl() + */ + public void hideInformationControl() { + super.hideInformationControl(); + } + + /** + * @param input the delayed input, or <code>null</code> to request cancellation + */ + public void setDelayedInput(Object input) { + fReplacableInformation= input; + if (! isReplacing()) { + fDelayedInformationSet= true; + } else if (getCurrentInformationControl2() instanceof IInformationControlExtension2) { + ((IInformationControlExtension2) getCurrentInformationControl2()).setInput(input); + } else if (getCurrentInformationControl2() != null) { + getCurrentInformationControl2().setInformation(input.toString()); + } + } + + /** + * Tells whether the replacer is currently replacing another information control. + * + * @return <code>true</code> while code from {@link #replaceInformationControl(IInformationControlCreator, Rectangle, Object, Rectangle, boolean)} is run + */ + public boolean isReplacing() { + return fIsReplacing; + } + + /** + * @return the current information control, or <code>null</code> if none available + */ + public IInformationControl getCurrentInformationControl2() { + return getInternalAccessor().getCurrentInformationControl(); + } + + /** + * The number of pixels to blow up the keep-up zone. + * + * @return the margin in pixels + */ + public int getKeepUpMargin() { + return 15; + } +} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/InternalAccessor.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/InternalAccessor.java new file mode 100644 index 00000000000..6e56d90e813 --- /dev/null +++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/InternalAccessor.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2008 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.jface.internal.text; + +import org.eclipse.swt.graphics.Rectangle; + +import org.eclipse.jface.text.AbstractInformationControlManager; +import org.eclipse.jface.text.IInformationControl; +import org.eclipse.jface.text.IInformationControlExtension3; +import org.eclipse.jface.text.ITextViewerExtension8; +import org.eclipse.jface.text.ITextViewerExtension8.EnrichMode; + + +/** + * An internal class that gives access to internal methods of {@link + * AbstractInformationControlManager} and subclasses. + * + * @since 3.4 + */ +public abstract class InternalAccessor { + + /** + * Returns the current information control, or <code>null</code> if none. + * + * @return the current information control, or <code>null</code> if none + */ + public abstract IInformationControl getCurrentInformationControl(); + + /** + * Sets the information control replacer for this manager and disposes the + * old one if set. + * + * @param replacer the information control replacer for this manager, or + * <code>null</code> if no information control replacing should + * take place + */ + public abstract void setInformationControlReplacer(InformationControlReplacer replacer); + + /** + * Returns the current information control replacer or <code>null</code> if none has been installed. + * + * @return the current information control replacer or <code>null</code> if none has been installed + */ + public abstract InformationControlReplacer getInformationControlReplacer(); + + /** + * Tests whether the given information control is replaceable. + * + * @param iControl information control or <code>null</code> if none + * @return <code>true</code> if information control is replaceable, <code>false</code> otherwise + */ + public abstract boolean canReplace(IInformationControl iControl); + + /** + * Tells whether this manager's information control is currently being replaced. + * + * @return <code>true</code> if a replace is in progress + */ + public abstract boolean isReplaceInProgress(); + + /** + * Crops the given bounds such that they lie completely on the closest monitor. + * + * @param bounds shell bounds to crop + */ + public abstract void cropToClosestMonitor(Rectangle bounds); + + /** + * Sets the hover enrich mode. Only applicable when an information + * control replacer has been set with + * {@link #setInformationControlReplacer(InformationControlReplacer)} . + * + * @param mode the enrich mode + * @see ITextViewerExtension8#setHoverEnrichMode(org.eclipse.jface.text.ITextViewerExtension8.EnrichMode) + */ + public abstract void setHoverEnrichMode(EnrichMode mode); + + /** + * Indicates whether the mouse cursor is allowed to leave the subject area without closing the hover. + * + * @return whether the mouse cursor is allowed to leave the subject area without closing the hover + */ + public abstract boolean getAllowMouseExit(); + + /** + * Replaces this manager's information control as defined by + * the information control replacer. + * <strong>Must only be called when the information control is instanceof {@link IInformationControlExtension3}!</strong> + * + * @param takeFocus <code>true</code> iff the replacing information control should take focus + */ + public abstract void replaceInformationControl(boolean takeFocus); + +} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/StickyHoverManager.java b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/StickyHoverManager.java index 6a4f1ff49b6..c183999f603 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/StickyHoverManager.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/StickyHoverManager.java @@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.jface.internal.text; - import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.ControlListener; @@ -26,14 +25,8 @@ import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.jface.text.AbstractInformationControlManager; -import org.eclipse.jface.text.AbstractReusableInformationControlCreator; -import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.IInformationControl; -import org.eclipse.jface.text.IInformationControlCreator; -import org.eclipse.jface.text.IInformationControlExtension2; import org.eclipse.jface.text.IInformationControlExtension3; import org.eclipse.jface.text.IInformationControlExtension5; import org.eclipse.jface.text.IViewportListener; @@ -60,7 +53,7 @@ import org.eclipse.jface.util.Geometry; * * @since 3.4 */ -public class StickyHoverManager extends AbstractInformationControlManager implements IWidgetTokenKeeper, IWidgetTokenKeeperExtension, IInformationControlReplacer { +public class StickyHoverManager extends InformationControlReplacer implements IWidgetTokenKeeper, IWidgetTokenKeeperExtension { /** * Priority of the info controls managed by this sticky hover manager. @@ -71,24 +64,6 @@ public class StickyHoverManager extends AbstractInformationControlManager implem */ private static final int WIDGET_PRIORITY= -5; - /** - * Minimal width in pixels. - */ - private static final int MIN_WIDTH= 80; - /** - * Minimal height in pixels. - */ - private static final int MIN_HEIGHT= 50; - - /** - * Default control creator. - */ - private static class DefaultInformationControlCreator extends AbstractReusableInformationControlCreator { - public IInformationControl doCreateInformationControl(Shell shell) { - return new DefaultInformationControl(shell, true); - } - } - /** * Internal information control closer. Listens to several events issued by its subject control @@ -119,7 +94,7 @@ public class StickyHoverManager extends AbstractInformationControlManager implem * @see IInformationControlCloser#setInformationControl(IInformationControl) */ public void setInformationControl(IInformationControl control) { - // NOTE: we use fInformationControl from the outer class + // NOTE: we use getCurrentInformationControl2() from the outer class } /* @@ -294,12 +269,7 @@ public class StickyHoverManager extends AbstractInformationControlManager implem } - private TextViewer fTextViewer; - private boolean fIsReplacing; - private Object fReplacableInformation; - private boolean fDelayedInformationSet; - private Rectangle fReplaceableArea; - private Rectangle fContentBounds; + private final TextViewer fTextViewer; /** @@ -312,25 +282,11 @@ public class StickyHoverManager extends AbstractInformationControlManager implem fTextViewer= textViewer; setCloser(new Closer()); - takesFocusWhenVisible(false); install(fTextViewer.getTextWidget()); } /* - * @see AbstractInformationControlManager#computeInformation() - */ - protected void computeInformation() { - if (fIsReplacing && fReplacableInformation != null) { - setInformation(fReplacableInformation, fReplaceableArea); - return; - } - - if (DEBUG) - System.out.println("StickyHover: no active replaceable"); //$NON-NLS-1$ - } - - /* * @see AbstractInformationControlManager#showInformationControl(Rectangle) */ protected void showInformationControl(Rectangle subjectArea) { @@ -344,7 +300,7 @@ public class StickyHoverManager extends AbstractInformationControlManager implem /* * @see AbstractInformationControlManager#hideInformationControl() */ - protected void hideInformationControl() { + public void hideInformationControl() { try { super.hideInformationControl(); } finally { @@ -417,107 +373,4 @@ public class StickyHoverManager extends AbstractInformationControlManager implem return iControl.isFocusControl(); } - /* - * @see org.eclipse.jface.text.IInformationControlReplacer#replaceInformationControl(IInformationControlCreator, org.eclipse.swt.graphics.Rectangle, java.lang.Object, org.eclipse.swt.graphics.Rectangle, boolean) - */ - public void replaceInformationControl(IInformationControlCreator informationPresenterControlCreator, Rectangle contentBounds, Object information, final Rectangle subjectArea, boolean takeFocus) { - - try { - fIsReplacing= true; - if (! fDelayedInformationSet) - fReplacableInformation= information; - else - takeFocus= true; // delayed input has been set, so the original info control must have been focused - fContentBounds= contentBounds; - fReplaceableArea= subjectArea; - - setCustomInformationControlCreator(informationPresenterControlCreator); - - takesFocusWhenVisible(takeFocus); - - showInformation(); - } finally { - fIsReplacing= false; - fReplacableInformation= null; - fDelayedInformationSet= false; - fReplaceableArea= null; - setCustomInformationControlCreator(null); - } - } - - /* - * @see org.eclipse.jface.text.AbstractInformationControlManager#internalShowInformationControl(org.eclipse.swt.graphics.Rectangle, java.lang.Object) - */ - public void internalShowInformationControl2(Rectangle subjectArea, Object information) { - IInformationControl informationControl= getInformationControl(); - - Rectangle controlBounds= fContentBounds; - if (informationControl instanceof IInformationControlExtension3) { - IInformationControlExtension3 iControl3= (IInformationControlExtension3) informationControl; - Rectangle trim= iControl3.computeTrim(); - controlBounds= Geometry.add(controlBounds, trim); - - /* - * Ensure minimal size. Interacting with a tiny information control - * (resizing, selecting text) would be a pain. - */ - controlBounds.width= Math.max(controlBounds.width, MIN_WIDTH); - controlBounds.height= Math.max(controlBounds.height, MIN_HEIGHT); - - // reflective version of cropToClosestMonitor(controlBounds); - AccessorUtil.invoke(this, AbstractInformationControlManager.class, "cropToClosestMonitor", Rectangle.class, controlBounds); //$NON-NLS-1$ - } - - Point location= Geometry.getLocation(controlBounds); - Point size= Geometry.getSize(controlBounds); - - // Caveat: some IInformationControls fail unless setSizeConstraints(..) is called with concrete values - informationControl.setSizeConstraints(size.x, size.y); - - if (informationControl instanceof IInformationControlExtension2) - ((IInformationControlExtension2) informationControl).setInput(information); - else - informationControl.setInformation(information.toString()); - - informationControl.setLocation(location); - informationControl.setSize(size.x, size.y); - - showInformationControl(subjectArea); - } - - /* - * @see org.eclipse.jface.text.IInformationControlReplacer#setDelayedInput(java.lang.Object) - */ - public void setDelayedInput(Object input) { - fReplacableInformation= input; - if (! isReplacing()) { - fDelayedInformationSet= true; - } else if (getCurrentInformationControl2() instanceof IInformationControlExtension2) { - ((IInformationControlExtension2) getCurrentInformationControl2()).setInput(input); - } else if (getCurrentInformationControl2() != null) { - getCurrentInformationControl2().setInformation(input.toString()); - } - } - - /* - * @see org.eclipse.jface.text.IInformationControlReplacer#getKeepUpMargin() - */ - public int getKeepUpMargin() { - return 15; - } - - /* - * @see org.eclipse.jface.text.IInformationControlReplacer#isReplacing() - */ - public boolean isReplacing() { - return fIsReplacing; - } - - /* - * @see org.eclipse.jface.text.IInformationControlReplacer#getCurrentInformationControl() - */ - public IInformationControl getCurrentInformationControl2() { - // reflective version of super.getCurrentInformationControl() - return (IInformationControl) AccessorUtil.invoke(this, AbstractInformationControlManager.class, "getCurrentInformationControl"); //$NON-NLS-1$ - } } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractHoverInformationControlManager.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractHoverInformationControlManager.java index 0958b590be9..ca66688219a 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractHoverInformationControlManager.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractHoverInformationControlManager.java @@ -38,8 +38,9 @@ import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Scrollable; -import org.eclipse.jface.internal.text.AccessorUtil; -import org.eclipse.jface.internal.text.IInformationControlReplacer; +import org.eclipse.jface.internal.text.DelayedInputChangeListener; +import org.eclipse.jface.internal.text.InformationControlReplacer; +import org.eclipse.jface.internal.text.InternalAccessor; import org.eclipse.jface.text.ITextViewerExtension8.EnrichMode; import org.eclipse.jface.text.source.AnnotationBarHoverManager; import org.eclipse.jface.util.Geometry; @@ -100,7 +101,7 @@ abstract public class AbstractHoverInformationControlManager extends AbstractInf * @see IInformationControlCloser#setHoverControl(IHoverControl) */ public void setInformationControl(IInformationControl control) { - // NOTE: we use fInformationControl from the outer class + // NOTE: we use getCurrentInformationControl() from the outer class } /* @@ -372,7 +373,7 @@ abstract public class AbstractHoverInformationControlManager extends AbstractInf IInformationControl iControl= getCurrentInformationControl(); if (!hasInformationControlReplacer() || !canMoveIntoInformationControl(iControl)) { if (AbstractHoverInformationControlManager.this instanceof AnnotationBarHoverManager) { - if (Boolean.TRUE.equals(AccessorUtil.getValue(AbstractHoverInformationControlManager.this, AnnotationBarHoverManager.class, "fAllowMouseExit"))) //$NON-NLS-1$ + if (getInternalAccessor().getAllowMouseExit()) return; } hideInformationControl(); @@ -405,33 +406,6 @@ abstract public class AbstractHoverInformationControlManager extends AbstractInf } /** - * The delayed input change listener implementation. - * @since 3.4 - */ - private static final class DelayedInputChangeListener implements IInputChangedListener { - - private final IDelayedInputChangeProvider fChangeProvider; - private final IInformationControlReplacer fInformationControlReplacer; - - /** - * @param changeProvider the information control with delayed input changes - * @param informationControlReplacer the information control replacer, whose information control should get the new input - */ - private DelayedInputChangeListener(IDelayedInputChangeProvider changeProvider, IInformationControlReplacer informationControlReplacer) { - fChangeProvider= changeProvider; - fInformationControlReplacer= informationControlReplacer; - } - - /* - * @see org.eclipse.jface.text.IDelayedInputChangeListener#inputChanged(java.lang.Object) - */ - public void inputChanged(Object newInput) { - fChangeProvider.setDelayedInputChangeListener(null); - fInformationControlReplacer.setDelayedInput(newInput); - } - } - - /** * To be installed on the manager's subject control. Serves two different purposes: * <ul> * <li> start function: initiates the computation of the information to be presented. This happens on @@ -766,6 +740,22 @@ abstract public class AbstractHoverInformationControlManager extends AbstractInf } } return false; + + } else if (subjectArea.x + subjectArea.width < iControlBounds.x) { + // special case for hover events (e.g. in annotation ruler): subjectArea totally left of iControl + // +-----------+--------------------+ + // +-----------+ | | + // |subjectArea|also keepUp| InformationControl | + // +-----------+ | | + // +-----------+--------------------+ + if (subjectArea.x + subjectArea.width <= x && x <= iControlBounds.x) { + // is horizontally between subject area and iControl + if (iControlBounds.y <= y && y <= iControlBounds.y + iControlBounds.height) { + // is to the left of iControl (in a horizontal projection) + return true; + } + } + return false; } } @@ -800,14 +790,13 @@ abstract public class AbstractHoverInformationControlManager extends AbstractInf /** * Sets the hover enrich mode. Only applicable when an information * control replacer has been set with - * {@link #setInformationControlReplacer(IInformationControlReplacer)} . + * {@link #setInformationControlReplacer(InformationControlReplacer)} . * * @param mode the enrich mode * @since 3.4 * @see ITextViewerExtension8#setHoverEnrichMode(org.eclipse.jface.text.ITextViewerExtension8.EnrichMode) */ void setHoverEnrichMode(EnrichMode mode) { - // Do not rename! Called reflectively from SourceViewer. fEnrichMode= mode; } @@ -971,5 +960,23 @@ abstract public class AbstractHoverInformationControlManager extends AbstractInf protected int getHoverEventStateMask() { return fHoverEventStateMask; } + + /** + * Returns an adapter that gives access to internal methods. + * <p> + * <strong>Note:</strong> This method is not intended to be referenced or overridden by clients.</p> + * + * @return the replaceable information control accessor + * @since 3.4 + * @noreference This method is not intended to be referenced by clients. + * @nooverride This method is not intended to be re-implemented or extended by clients. + */ + public InternalAccessor getInternalAccessor() { + return new MyInternalAccessor() { + public void setHoverEnrichMode(EnrichMode mode) { + AbstractHoverInformationControlManager.this.setHoverEnrichMode(mode); + } + }; + } } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControlManager.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControlManager.java index 39f37d02f0b..10138289e91 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControlManager.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractInformationControlManager.java @@ -27,8 +27,9 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Monitor; import org.eclipse.jface.dialogs.IDialogSettings; -import org.eclipse.jface.internal.text.IInformationControlReplacer; -import org.eclipse.jface.internal.text.StickyHoverManager; +import org.eclipse.jface.internal.text.InformationControlReplacer; +import org.eclipse.jface.internal.text.InternalAccessor; +import org.eclipse.jface.text.ITextViewerExtension8.EnrichMode; import org.eclipse.jface.util.Geometry; @@ -51,6 +52,49 @@ import org.eclipse.jface.util.Geometry; abstract public class AbstractInformationControlManager { /** + * An internal class that gives access to internal methods. + * + * @since 3.4 + */ + class MyInternalAccessor extends InternalAccessor { + public IInformationControl getCurrentInformationControl() { + return AbstractInformationControlManager.this.getCurrentInformationControl(); + } + + public void setInformationControlReplacer(InformationControlReplacer replacer) { + AbstractInformationControlManager.this.setInformationControlReplacer(replacer); + } + + public InformationControlReplacer getInformationControlReplacer() { + return AbstractInformationControlManager.this.getInformationControlReplacer(); + } + + public boolean canReplace(IInformationControl control) { + return AbstractInformationControlManager.this.canReplace(control); + } + + public boolean isReplaceInProgress() { + return AbstractInformationControlManager.this.isReplaceInProgress(); + } + + public void replaceInformationControl(boolean takeFocus) { + AbstractInformationControlManager.this.replaceInformationControl(takeFocus); + } + + public void cropToClosestMonitor(Rectangle bounds) { + AbstractInformationControlManager.this.cropToClosestMonitor(bounds); + } + + public void setHoverEnrichMode(EnrichMode mode) { + throw new UnsupportedOperationException("only implemented in AbstractHoverInformationControlManager"); //$NON-NLS-1$ + } + + public boolean getAllowMouseExit() { + throw new UnsupportedOperationException("only implemented in AnnotationBarHoverManager"); //$NON-NLS-1$ + } + } + + /** * Interface of an information control closer. An information control closer * monitors its information control and its subject control and closes the * information control if necessary. @@ -226,7 +270,7 @@ abstract public class AbstractInformationControlManager { * * @since 3.4 */ - private IInformationControlReplacer fInformationControlReplacer; + private InformationControlReplacer fInformationControlReplacer; /** Indicates the enable state of this manager */ private boolean fEnabled= false; @@ -391,8 +435,7 @@ abstract public class AbstractInformationControlManager { * take place * @since 3.4 */ - void setInformationControlReplacer(IInformationControlReplacer replacer) { - // Do not rename! Called reflectively from StickyHoverManager. + void setInformationControlReplacer(InformationControlReplacer replacer) { if (fInformationControlReplacer != null) fInformationControlReplacer.dispose(); fInformationControlReplacer= replacer; @@ -404,7 +447,7 @@ abstract public class AbstractInformationControlManager { * @return the current information control replacer or <code>null</code> if none has been installed * @since 3.4 */ - IInformationControlReplacer getInformationControlReplacer() { + InformationControlReplacer getInformationControlReplacer() { return fInformationControlReplacer; } @@ -438,7 +481,6 @@ abstract public class AbstractInformationControlManager { * @since 3.4 */ IInformationControl getCurrentInformationControl() { - // Do not rename! Called reflectively from StickyHoverManager. return fInformationControl; } @@ -1104,8 +1146,8 @@ abstract public class AbstractInformationControlManager { * @param information the information */ private void internalShowInformationControl(Rectangle subjectArea, Object information) { - if (this instanceof StickyHoverManager) { - ((StickyHoverManager) this).internalShowInformationControl2(subjectArea, information); + if (this instanceof InformationControlReplacer) { + ((InformationControlReplacer) this).showInformationControl(subjectArea, information); return; } @@ -1173,7 +1215,6 @@ abstract public class AbstractInformationControlManager { * @since 3.4 */ void cropToClosestMonitor(Rectangle bounds) { - // Do not rename! Called reflectively from StickyHoverManager. Rectangle monitorBounds= getClosestMonitor(fSubjectControl.getDisplay(), bounds).getClientArea(); bounds.intersect(monitorBounds); } @@ -1249,6 +1290,11 @@ abstract public class AbstractInformationControlManager { setEnabled(false); disposeInformationControl(); + if (fInformationControlReplacer != null) { + fInformationControlReplacer.dispose(); + fInformationControlReplacer= null; + } + if (fSubjectControl != null && !fSubjectControl.isDisposed() && fSubjectControlDisposeListener != null) fSubjectControl.removeDisposeListener(fSubjectControlDisposeListener); fSubjectControl= null; @@ -1369,4 +1415,18 @@ abstract public class AbstractInformationControlManager { return bounds; } + + /** + * Returns an adapter that gives access to internal methods. + * <p> + * <strong>Note:</strong> This method is not intended to be referenced or overridden by clients.</p> + * + * @return the replaceable information control accessor + * @since 3.4 + * @noreference This method is not intended to be referenced by clients. + * @nooverride This method is not intended to be re-implemented or extended by clients. + */ + public InternalAccessor getInternalAccessor() { + return new MyInternalAccessor(); + } } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java index 665e456f7f4..1c23e1efd36 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/AdditionalInfoController.java @@ -22,10 +22,14 @@ import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; +import org.eclipse.jface.internal.text.InformationControlReplacer; import org.eclipse.jface.text.AbstractInformationControlManager; +import org.eclipse.jface.text.AbstractReusableInformationControlCreator; +import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IInformationControlExtension3; @@ -373,6 +377,14 @@ class AdditionalInfoController extends AbstractInformationControlManager { public void widgetDefaultSelected(SelectionEvent e) { } } + /** + * Default control creator. + */ + private static class DefaultInformationControlCreator extends AbstractReusableInformationControlCreator { + public IInformationControl doCreateInformationControl(Shell shell) { + return new DefaultInformationControl(shell, true); + } + } /** The proposal table. */ private Table fProposalTable; @@ -417,6 +429,9 @@ class AdditionalInfoController extends AbstractInformationControlManager { */ int spacing= -1; setMargins(spacing, spacing); // see also adjustment in #computeLocation + + InformationControlReplacer replacer= new InformationControlReplacer(new DefaultInformationControlCreator()); + getInternalAccessor().setInformationControlReplacer(replacer); } /* @@ -434,8 +449,13 @@ class AdditionalInfoController extends AbstractInformationControlManager { Assert.isTrue(control instanceof Table); fProposalTable= (Table) control; fProposalTable.addSelectionListener(fSelectionListener); + getInternalAccessor().getInformationControlReplacer().install(fProposalTable); + fTimer= new Timer(fProposalTable.getDisplay(), fDelay) { protected void showInformation(ICompletionProposal proposal, Object info) { + InformationControlReplacer replacer= getInternalAccessor().getInformationControlReplacer(); + if (replacer != null) + replacer.hideInformationControl(); AdditionalInfoController.this.showInformation(proposal, info); } }; @@ -551,6 +571,20 @@ class AdditionalInfoController extends AbstractInformationControlManager { sizeConstraint.y= size.y; return sizeConstraint; } + + /* + * @see org.eclipse.jface.text.AbstractInformationControlManager#hideInformationControl() + */ + protected void hideInformationControl() { + super.hideInformationControl(); + } + + /** + * @return the current information control, or <code>null</code> if none available + */ + public IInformationControl getCurrentInformationControl2() { + return getInternalAccessor().getCurrentInformationControl(); + } } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java index bb6a10d674d..76259e6c3ea 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java @@ -14,6 +14,13 @@ package org.eclipse.jface.text.contentassist; import java.util.ArrayList; import java.util.List; +import org.eclipse.core.runtime.Assert; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; + import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.custom.StyleRange; @@ -30,6 +37,8 @@ import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.TraverseEvent; +import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; @@ -48,22 +57,13 @@ import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; -import org.eclipse.core.commands.AbstractHandler; -import org.eclipse.core.commands.ExecutionEvent; -import org.eclipse.core.commands.ExecutionException; -import org.eclipse.core.commands.IHandler; - -import org.eclipse.core.runtime.Assert; - import org.eclipse.jface.bindings.keys.KeySequence; import org.eclipse.jface.bindings.keys.SWTKeySupport; import org.eclipse.jface.contentassist.IContentAssistSubjectControl; +import org.eclipse.jface.internal.text.InformationControlReplacer; import org.eclipse.jface.internal.text.TableOwnerDrawSupport; import org.eclipse.jface.preference.JFacePreferences; import org.eclipse.jface.resource.JFaceResources; -import org.eclipse.jface.util.Geometry; -import org.eclipse.jface.viewers.StyledString; - import org.eclipse.jface.text.AbstractInformationControlManager; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentEvent; @@ -71,12 +71,15 @@ import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.IEditingSupport; import org.eclipse.jface.text.IEditingSupportRegistry; +import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.IRewriteTarget; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.ITextViewerExtension; import org.eclipse.jface.text.TextUtilities; import org.eclipse.jface.text.AbstractInformationControlManager.Anchor; +import org.eclipse.jface.util.Geometry; +import org.eclipse.jface.viewers.StyledString; /** @@ -650,7 +653,7 @@ class CompletionProposalPopup implements IContentAssistListener { } }); - fPopupCloser.install(fContentAssistant, fProposalTable); + fPopupCloser.install(fContentAssistant, fProposalTable, fAdditionalInfoController); fProposalShell.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { @@ -693,12 +696,27 @@ class CompletionProposalPopup implements IContentAssistListener { if (commandSequence != null && !commandSequence.isEmpty() && fContentAssistant.isRepeatedInvocationMode()) { control.addFocusListener(new FocusListener() { private CommandKeyListener fCommandKeyListener; + private TraverseListener fTraverseListener; public void focusGained(FocusEvent e) { if (Helper.okToUse(control)) { if (fCommandKeyListener == null) { fCommandKeyListener= new CommandKeyListener(commandSequence); fProposalTable.addKeyListener(fCommandKeyListener); } + if (fTraverseListener == null) { + fTraverseListener= new TraverseListener() { + public void keyTraversed(TraverseEvent event) { + if (event.detail == SWT.TRAVERSE_TAB_NEXT) { + IInformationControl iControl= fAdditionalInfoController.getCurrentInformationControl2(); + if (fAdditionalInfoController.getInternalAccessor().canReplace(iControl)) { + fAdditionalInfoController.getInternalAccessor().replaceInformationControl(true); + event.doit= false; + } + } + } + }; + fProposalTable.addTraverseListener(fTraverseListener); + } } } public void focusLost(FocusEvent e) { @@ -706,6 +724,10 @@ class CompletionProposalPopup implements IContentAssistListener { control.removeKeyListener(fCommandKeyListener); fCommandKeyListener= null; } + if (fTraverseListener != null) { + control.removeTraverseListener(fTraverseListener); + fTraverseListener= null; + } } }); } @@ -940,8 +962,27 @@ class CompletionProposalPopup implements IContentAssistListener { * @return <code>true</code> if the popup has the focus */ public boolean hasFocus() { - if (Helper.okToUse(fProposalShell)) - return (fProposalShell.isFocusControl() || fProposalTable.isFocusControl()); + if (Helper.okToUse(fProposalShell)) { + if ((fProposalShell.isFocusControl() || fProposalTable.isFocusControl())) + return true; + /* + * We have to delegate this query to the additional info controller + * as well, since the content assistant is the widget token owner + * and its closer does not know that the additional info control can + * now also take focus. + */ + if (fAdditionalInfoController != null) { + IInformationControl informationControl= fAdditionalInfoController.getCurrentInformationControl2(); + if (informationControl != null && informationControl.isFocusControl()) + return true; + InformationControlReplacer replacer= fAdditionalInfoController.getInternalAccessor().getInformationControlReplacer(); + if (replacer != null) { + informationControl= replacer.getCurrentInformationControl2(); + if (informationControl != null && informationControl.isFocusControl()) + return true; + } + } + } return false; } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/PopupCloser.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/PopupCloser.java index 495401c1866..5d1e5a2ca69 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/PopupCloser.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/PopupCloser.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2005 IBM Corporation and others. + * Copyright (c) 2000, 2008 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 @@ -10,17 +10,28 @@ *******************************************************************************/ package org.eclipse.jface.text.contentassist; +import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.ShellAdapter; import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; +import org.eclipse.jface.internal.text.DelayedInputChangeListener; +import org.eclipse.jface.internal.text.InformationControlReplacer; +import org.eclipse.jface.text.IDelayedInputChangeProvider; +import org.eclipse.jface.text.IInformationControl; +import org.eclipse.jface.text.IInformationControlExtension5; +import org.eclipse.jface.text.IInputChangedListener; + /** * A generic closer class used to monitor various @@ -28,7 +39,7 @@ import org.eclipse.swt.widgets.Table; * a content assistant should be terminated and all * associated windows be closed. */ -class PopupCloser extends ShellAdapter implements FocusListener, SelectionListener { +class PopupCloser extends ShellAdapter implements FocusListener, SelectionListener, Listener { /** The content assistant to be monitored. */ private ContentAssistant fContentAssistant; @@ -43,6 +54,16 @@ class PopupCloser extends ShellAdapter implements FocusListener, SelectionListen * @since 3.1 */ private Shell fShell; + /** + * The display on which some filters are registered. + * @since 3.4 + */ + private Display fDisplay; + /** + * The additional info controller, or <code>null</code>. + * @since 3.4 + */ + private AdditionalInfoController fAdditionalInfoController; /** * Installs this closer on the given table opened by the given content assistant. @@ -51,19 +72,38 @@ class PopupCloser extends ShellAdapter implements FocusListener, SelectionListen * @param table the table to be tracked */ public void install(ContentAssistant contentAssistant, Table table) { + install(contentAssistant, table, null); + } + + /** + * Installs this closer on the given table opened by the given content assistant. + * + * @param contentAssistant the content assistant + * @param table the table to be tracked + * @param additionalInfoController the additional info controller, or <code>null</code> + * @since 3.4 + */ + public void install(ContentAssistant contentAssistant, Table table, AdditionalInfoController additionalInfoController) { fContentAssistant= contentAssistant; fTable= table; + fAdditionalInfoController= additionalInfoController; + if (Helper.okToUse(fTable)) { - Shell shell= fTable.getShell(); - if (Helper.okToUse(shell)) { - fShell= shell; - fShell.addShellListener(this); - } - + fShell= fTable.getShell(); + fDisplay= fShell.getDisplay(); + + fShell.addShellListener(this); fTable.addFocusListener(this); fScrollbar= fTable.getVerticalBar(); if (fScrollbar != null) fScrollbar.addSelectionListener(this); + + fDisplay.addFilter(SWT.Activate, this); + fDisplay.addFilter(SWT.MouseWheel, this); + + fDisplay.addFilter(SWT.Deactivate, this); + + fDisplay.addFilter(SWT.MouseUp, this); } } @@ -79,6 +119,14 @@ class PopupCloser extends ShellAdapter implements FocusListener, SelectionListen fScrollbar.removeSelectionListener(this); if (Helper.okToUse(fTable)) fTable.removeFocusListener(this); + if (fDisplay != null && ! fDisplay.isDisposed()) { + fDisplay.removeFilter(SWT.Activate, this); + fDisplay.removeFilter(SWT.MouseWheel, this); + + fDisplay.removeFilter(SWT.Deactivate, this); + + fDisplay.removeFilter(SWT.MouseUp, this); + } } /* @@ -120,7 +168,7 @@ class PopupCloser extends ShellAdapter implements FocusListener, SelectionListen * @since 3.1 */ public void shellDeactivated(ShellEvent e) { - if (fContentAssistant != null) + if (fContentAssistant != null && ! fContentAssistant.hasProposalPopupFocus()) fContentAssistant.hide(); } @@ -133,4 +181,88 @@ class PopupCloser extends ShellAdapter implements FocusListener, SelectionListen if (fContentAssistant != null) fContentAssistant.hide(); } + + /* + * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event) + * @since 3.4 + */ + public void handleEvent(Event event) { + switch (event.type) { + case SWT.Activate: + case SWT.MouseWheel: + if (fAdditionalInfoController == null) + return; + if (event.widget == fShell || event.widget == fTable || event.widget == fScrollbar) + return; + + if (fAdditionalInfoController.getInternalAccessor().getInformationControlReplacer() == null) + fAdditionalInfoController.hideInformationControl(); + else if (!fAdditionalInfoController.getInternalAccessor().isReplaceInProgress()) { + IInformationControl infoControl= fAdditionalInfoController.getCurrentInformationControl2(); + // During isReplaceInProgress(), events can come from the replacing information control + if (event.widget instanceof Control && infoControl instanceof IInformationControlExtension5) { + Control control= (Control) event.widget; + IInformationControlExtension5 iControl5= (IInformationControlExtension5) infoControl; + if (!(iControl5.containsControl(control))) + fAdditionalInfoController.hideInformationControl(); + else if (event.type == SWT.MouseWheel) + fAdditionalInfoController.getInternalAccessor().replaceInformationControl(false); + } else if (infoControl != null && infoControl.isFocusControl()) { + fAdditionalInfoController.getInternalAccessor().replaceInformationControl(true); + } + } + break; + + case SWT.MouseUp: + if (fAdditionalInfoController.getInternalAccessor().isReplaceInProgress()) + break; + if (event.widget instanceof Control) { + Control control= (Control) event.widget; + IInformationControl infoControl= fAdditionalInfoController.getCurrentInformationControl2(); + if (infoControl instanceof IInformationControlExtension5) { + final IInformationControlExtension5 iControl5= (IInformationControlExtension5) infoControl; + if (iControl5.containsControl(control)) { + if (infoControl instanceof IDelayedInputChangeProvider) { + final IDelayedInputChangeProvider delayedICP= (IDelayedInputChangeProvider) infoControl; + final IInputChangedListener inputChangeListener= new DelayedInputChangeListener(delayedICP, fAdditionalInfoController.getInternalAccessor().getInformationControlReplacer()); + delayedICP.setDelayedInputChangeListener(inputChangeListener); + // cancel automatic input updating after a small timeout: + control.getShell().getDisplay().timerExec(1000, new Runnable() { + public void run() { + delayedICP.setDelayedInputChangeListener(null); + } + }); + } + + // XXX: workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=212392 : + control.getShell().getDisplay().asyncExec(new Runnable() { + public void run() { + fAdditionalInfoController.getInternalAccessor().replaceInformationControl(true); + } + }); + } + } + } + break; + + case SWT.Deactivate: + InformationControlReplacer replacer= fAdditionalInfoController.getInternalAccessor().getInformationControlReplacer(); + if (replacer != null && fContentAssistant != null) { + IInformationControl iControl= replacer.getCurrentInformationControl2(); + if (event.widget instanceof Control && iControl instanceof IInformationControlExtension5) { + Control control= (Control) event.widget; + IInformationControlExtension5 iControl5= (IInformationControlExtension5) iControl; + if (iControl5.containsControl(control)) { + control.getDisplay().asyncExec(new Runnable() { + public void run() { + if (fContentAssistant != null && ! fContentAssistant.hasProposalPopupFocus()) + fContentAssistant.hide(); + } + }); + } + } + } + break; + } + } } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationBarHoverManager.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationBarHoverManager.java index 2dcef45b118..daadea9074e 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationBarHoverManager.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/AnnotationBarHoverManager.java @@ -35,6 +35,9 @@ import org.eclipse.swt.widgets.Listener; import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.internal.text.InformationControlReplacer; +import org.eclipse.jface.internal.text.InternalAccessor; + import org.eclipse.jface.text.AbstractHoverInformationControlManager; import org.eclipse.jface.text.AbstractInformationControlManager; import org.eclipse.jface.text.BadLocationException; @@ -46,6 +49,7 @@ import org.eclipse.jface.text.ITextViewerExtension5; import org.eclipse.jface.text.JFaceTextUtil; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextUtilities; +import org.eclipse.jface.text.ITextViewerExtension8.EnrichMode; /** @@ -734,5 +738,56 @@ public class AnnotationBarHoverManager extends AbstractHoverInformationControlMa public IAnnotationHover getCurrentAnnotationHover() { return fCurrentHover; } + + /** + * Returns an adapter that gives access to internal methods. + * <p> + * <strong>Note:</strong> This method is not intended to be referenced or overridden by clients. + * </p> + * + * @return the replaceable information control accessor + * @since 3.4 + * @noreference This method is not intended to be referenced by clients. + * @nooverride This method is not intended to be re-implemented or extended by clients. + */ + public InternalAccessor getInternalAccessor() { + return new InternalAccessor() { + public IInformationControl getCurrentInformationControl() { + return AnnotationBarHoverManager.super.getInternalAccessor().getCurrentInformationControl(); + } + + public void setInformationControlReplacer(InformationControlReplacer replacer) { + AnnotationBarHoverManager.super.getInternalAccessor().setInformationControlReplacer(replacer); + } + + public InformationControlReplacer getInformationControlReplacer() { + return AnnotationBarHoverManager.super.getInternalAccessor().getInformationControlReplacer(); + } + + public boolean canReplace(IInformationControl control) { + return AnnotationBarHoverManager.super.getInternalAccessor().canReplace(control); + } + + public boolean isReplaceInProgress() { + return AnnotationBarHoverManager.super.getInternalAccessor().isReplaceInProgress(); + } + + public void replaceInformationControl(boolean takeFocus) { + AnnotationBarHoverManager.super.getInternalAccessor().replaceInformationControl(takeFocus); + } + + public void cropToClosestMonitor(Rectangle bounds) { + AnnotationBarHoverManager.super.getInternalAccessor().cropToClosestMonitor(bounds); + } + + public void setHoverEnrichMode(EnrichMode mode) { + AnnotationBarHoverManager.super.getInternalAccessor().setHoverEnrichMode(mode); + } + + public boolean getAllowMouseExit() { + return fAllowMouseExit; + } + }; + } } diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.java index eba48bfe3aa..04471030774 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/SourceViewer.java @@ -21,13 +21,10 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Layout; -import org.eclipse.jface.internal.text.AccessorUtil; -import org.eclipse.jface.internal.text.IInformationControlReplacer; import org.eclipse.jface.internal.text.NonDeletingPositionUpdater; import org.eclipse.jface.internal.text.StickyHoverManager; import org.eclipse.jface.text.AbstractHoverInformationControlManager; -import org.eclipse.jface.text.AbstractInformationControlManager; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.DocumentRewriteSession; @@ -442,7 +439,7 @@ public class SourceViewer extends TextViewer implements ISourceViewer, ISourceVi if (fVerticalRuler != null && (fAnnotationHover != null || !isVerticalRulerOnlyShowingAnnotations()) && fVerticalRulerHoveringController == null && fHoverControlCreator != null) { fVerticalRulerHoveringController= new AnnotationBarHoverManager(fVerticalRuler, this, fAnnotationHover, fHoverControlCreator); fVerticalRulerHoveringController.install(fVerticalRuler.getControl()); - AccessorUtil.invoke(fVerticalRulerHoveringController, AbstractInformationControlManager.class, "setInformationControlReplacer", IInformationControlReplacer.class, new StickyHoverManager(this)); //$NON-NLS-1$ + fVerticalRulerHoveringController.getInternalAccessor().setInformationControlReplacer(new StickyHoverManager(this)); } } @@ -453,7 +450,7 @@ public class SourceViewer extends TextViewer implements ISourceViewer, ISourceVi if (fOverviewRuler != null && fOverviewRulerAnnotationHover != null && fOverviewRulerHoveringController == null && fHoverControlCreator != null) { fOverviewRulerHoveringController= new OverviewRulerHoverManager(fOverviewRuler, this, fOverviewRulerAnnotationHover, fHoverControlCreator); fOverviewRulerHoveringController.install(fOverviewRuler.getControl()); - AccessorUtil.invoke(fOverviewRulerHoveringController, AbstractInformationControlManager.class, "setInformationControlReplacer", IInformationControlReplacer.class, new StickyHoverManager(this)); //$NON-NLS-1$ + fOverviewRulerHoveringController.getInternalAccessor().setInformationControlReplacer(new StickyHoverManager(this)); } } @@ -464,9 +461,9 @@ public class SourceViewer extends TextViewer implements ISourceViewer, ISourceVi public void setHoverEnrichMode(EnrichMode mode) { super.setHoverEnrichMode(mode); if (fVerticalRulerHoveringController != null) - AccessorUtil.invoke(fVerticalRulerHoveringController, AbstractHoverInformationControlManager.class, "setHoverEnrichMode", EnrichMode.class, mode); //$NON-NLS-1$ + fVerticalRulerHoveringController.getInternalAccessor().setHoverEnrichMode(mode); if (fOverviewRulerHoveringController != null) - AccessorUtil.invoke(fOverviewRulerHoveringController, AbstractHoverInformationControlManager.class, "setHoverEnrichMode", EnrichMode.class, mode); //$NON-NLS-1$ + fOverviewRulerHoveringController.getInternalAccessor().setHoverEnrichMode(mode); } /* |