/******************************************************************************* * 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jface.contentassist; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.VerifyKeyListener; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.LabelProviderChangedEvent; import org.eclipse.jface.text.IEventConsumer; /** * An <code>AbstractControlContentAssistSubjectAdapter</code> delegates assistance requests from a * {@linkplain org.eclipse.jface.text.contentassist.ContentAssistant content assistant} * to a <code>Control</code>. * * A visual feedback can be configured via {@link #setContentAssistCueProvider(ILabelProvider)}. * * @since 3.0 * @deprecated As of 3.2, replaced by Platform UI's field assist support */ @Deprecated public abstract class AbstractControlContentAssistSubjectAdapter implements IContentAssistSubjectControl { protected static final boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.jface.text/debug/ContentAssistSubjectAdapters")); //$NON-NLS-1$//$NON-NLS-2$ /** * VerifyKeyListeners for the control. */ private List<VerifyKeyListener> fVerifyKeyListeners; /** * KeyListeners for the control. */ private Set<KeyListener> fKeyListeners; /** * The Listener installed on the control which passes events to * {@link #fVerifyKeyListeners fVerifyKeyListeners} and {@link #fKeyListeners}. */ private Listener fControlListener; /** * The cue label provider, or <code>null</code> iff none. * @since 3.3 */ private ILabelProvider fCueLabelProvider; /** * The control decoration, or <code>null</code> iff fCueLabelProvider is null. * @since 3.3 */ private ControlDecoration fControlDecoration; /** * The default cue image, or <code>null</code> if not cached yet. * @since 3.3 */ private Image fCachedDefaultCueImage; /** * Creates a new {@link AbstractControlContentAssistSubjectAdapter}. */ public AbstractControlContentAssistSubjectAdapter() { fVerifyKeyListeners= new ArrayList<>(1); fKeyListeners= new HashSet<>(1); } @Override public abstract Control getControl(); @Override public void addKeyListener(KeyListener keyListener) { fKeyListeners.add(keyListener); if (DEBUG) System.out.println("AbstractControlContentAssistSubjectAdapter#addKeyListener()"); //$NON-NLS-1$ installControlListener(); } @Override public void removeKeyListener(KeyListener keyListener) { boolean deleted= fKeyListeners.remove(keyListener); if (DEBUG) { if (!deleted) System.out.println("removeKeyListener -> wasn't here"); //$NON-NLS-1$ System.out.println("AbstractControlContentAssistSubjectAdapter#removeKeyListener() -> " + fKeyListeners.size()); //$NON-NLS-1$ } uninstallControlListener(); } @Override public boolean supportsVerifyKeyListener() { return true; } @Override public boolean appendVerifyKeyListener(final VerifyKeyListener verifyKeyListener) { fVerifyKeyListeners.add(verifyKeyListener); if (DEBUG) System.out.println("AbstractControlContentAssistSubjectAdapter#appendVerifyKeyListener() -> " + fVerifyKeyListeners.size()); //$NON-NLS-1$ installControlListener(); return true; } @Override public boolean prependVerifyKeyListener(final VerifyKeyListener verifyKeyListener) { fVerifyKeyListeners.add(0, verifyKeyListener); if (DEBUG) System.out.println("AbstractControlContentAssistSubjectAdapter#prependVerifyKeyListener() -> " + fVerifyKeyListeners.size()); //$NON-NLS-1$ installControlListener(); return true; } @Override public void removeVerifyKeyListener(VerifyKeyListener verifyKeyListener) { fVerifyKeyListeners.remove(verifyKeyListener); if (DEBUG) System.out.println("AbstractControlContentAssistSubjectAdapter#removeVerifyKeyListener() -> " + fVerifyKeyListeners.size()); //$NON-NLS-1$ uninstallControlListener(); } @Override public void setEventConsumer(IEventConsumer eventConsumer) { // this is not supported if (DEBUG) System.out.println("AbstractControlContentAssistSubjectAdapter#setEventConsumer()"); //$NON-NLS-1$ } @Override public String getLineDelimiter() { return System.getProperty("line.separator"); //$NON-NLS-1$ } /** * Installs <code>fControlListener</code>, which handles VerifyEvents and KeyEvents by * passing them to {@link #fVerifyKeyListeners} and {@link #fKeyListeners}. */ private void installControlListener() { if (DEBUG) System.out.println("AbstractControlContentAssistSubjectAdapter#installControlListener() -> k: " + fKeyListeners.size() + ", v: " + fVerifyKeyListeners.size()); //$NON-NLS-1$ //$NON-NLS-2$ if (fControlListener != null) return; fControlListener= new Listener() { @Override public void handleEvent(Event e) { if (! getControl().isFocusControl()) return; //SWT.TRAVERSE_MNEMONIC events can also come in to inactive widgets VerifyEvent verifyEvent= new VerifyEvent(e); KeyEvent keyEvent= new KeyEvent(e); switch (e.type) { case SWT.Traverse : if (DEBUG) dump("before traverse", e, verifyEvent); //$NON-NLS-1$ verifyEvent.doit= true; for (VerifyKeyListener verifyKeyListener : fVerifyKeyListeners) { verifyKeyListener.verifyKey(verifyEvent); if (! verifyEvent.doit) { e.detail= SWT.TRAVERSE_NONE; e.doit= true; if (DEBUG) dump("traverse eaten by verify", e, verifyEvent); //$NON-NLS-1$ return; } if (DEBUG) dump("traverse OK", e, verifyEvent); //$NON-NLS-1$ } break; case SWT.KeyDown: for (VerifyKeyListener verifyKeyListener : fVerifyKeyListeners) { verifyKeyListener.verifyKey(verifyEvent); if (! verifyEvent.doit) { e.doit= verifyEvent.doit; if (DEBUG) dump("keyDown eaten by verify", e, verifyEvent); //$NON-NLS-1$ return; } } if (DEBUG) dump("keyDown OK", e, verifyEvent); //$NON-NLS-1$ for (KeyListener keyListener : fKeyListeners) { keyListener.keyPressed(keyEvent); } break; default : Assert.isTrue(false); } } /** * Dump the given events to "standard" output. * * @param who who dump's * @param e the event * @param ve the verify event */ private void dump(String who, Event e, VerifyEvent ve) { StringBuffer sb= new StringBuffer("--- [AbstractControlContentAssistSubjectAdapter]\n"); //$NON-NLS-1$ sb.append(who); sb.append(" - e: keyCode="+e.keyCode+hex(e.keyCode)); //$NON-NLS-1$ sb.append("; character="+e.character+hex(e.character)); //$NON-NLS-1$ sb.append("; stateMask="+e.stateMask+hex(e.stateMask)); //$NON-NLS-1$ sb.append("; doit="+e.doit); //$NON-NLS-1$ sb.append("; detail="+e.detail+hex(e.detail)); //$NON-NLS-1$ sb.append("; widget="+e.widget); //$NON-NLS-1$ sb.append("\n"); //$NON-NLS-1$ sb.append(" verifyEvent keyCode="+e.keyCode+hex(e.keyCode)); //$NON-NLS-1$ sb.append("; character="+e.character+hex(e.character)); //$NON-NLS-1$ sb.append("; stateMask="+e.stateMask+hex(e.stateMask)); //$NON-NLS-1$ sb.append("; doit="+ve.doit); //$NON-NLS-1$ sb.append("; widget="+e.widget); //$NON-NLS-1$ System.out.println(sb); } private String hex(int i) { return "[0x" + Integer.toHexString(i) + ']'; //$NON-NLS-1$ } }; getControl().addListener(SWT.Traverse, fControlListener); getControl().addListener(SWT.KeyDown, fControlListener); if (DEBUG) System.out.println("AbstractControlContentAssistSubjectAdapter#installControlListener() - installed"); //$NON-NLS-1$ } /** * Uninstalls <code>fControlListener</code> iff there are no <code>KeyListener</code>s and no * <code>VerifyKeyListener</code>s registered. * Otherwise does nothing. */ private void uninstallControlListener() { if (fControlListener == null || fKeyListeners.size() + fVerifyKeyListeners.size() != 0) { if (DEBUG) System.out.println("AbstractControlContentAssistSubjectAdapter#uninstallControlListener() -> k: " + fKeyListeners.size() + ", v: " + fVerifyKeyListeners.size()); //$NON-NLS-1$ //$NON-NLS-2$ return; } getControl().removeListener(SWT.Traverse, fControlListener); getControl().removeListener(SWT.KeyDown, fControlListener); fControlListener= null; if (DEBUG) System.out.println("AbstractControlContentAssistSubjectAdapter#uninstallControlListener() - done"); //$NON-NLS-1$ } /** * Sets the visual feedback provider for content assist. * The given {@link ILabelProvider} methods are called with * {@link #getControl()} as argument. * * <ul> * <li><code>getImage(Object)</code> provides the visual cue image. * The image can maximally be 5 pixels wide and 8 pixels high. * If <code>getImage(Object)</code> returns <code>null</code>, a default image is used. * </li> * <li><code>getText(Object)</code> provides the hover info text. * It is shown when hovering over the cue image or the adapted {@link Control}. * No info text is shown if <code>getText(Object)</code> returns <code>null</code>. * </li> * </ul> * <p> * The given {@link ILabelProvider} becomes owned by the {@link AbstractControlContentAssistSubjectAdapter}, * i.e. it gets disposed when the adapted {@link Control} is disposed * or when another {@link ILabelProvider} is set. * </p> * * @param labelProvider a {@link ILabelProvider}, or <code>null</code> * if no visual feedback should be shown */ public void setContentAssistCueProvider(final ILabelProvider labelProvider) { if (fCueLabelProvider != null) { fCueLabelProvider.dispose(); } fCueLabelProvider= labelProvider; if (labelProvider == null) { if (fControlDecoration != null) { fControlDecoration.dispose(); fControlDecoration= null; } } else { if (fControlDecoration == null) { fControlDecoration= new ControlDecoration(getControl(), (SWT.TOP | SWT.LEFT)); getControl().addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { if (fCueLabelProvider != null) { fCueLabelProvider.dispose(); fCueLabelProvider= null; } if (fControlDecoration != null) { fControlDecoration.dispose(); fControlDecoration= null; } if (fCachedDefaultCueImage != null) { fCachedDefaultCueImage.dispose(); fCachedDefaultCueImage= null; } } }); fControlDecoration.setShowHover(true); fControlDecoration.setShowOnlyOnFocus(true); } ILabelProviderListener listener= new ILabelProviderListener() { @Override public void labelProviderChanged(LabelProviderChangedEvent event) { fControlDecoration.setDescriptionText(labelProvider.getText(getControl())); Image image= labelProvider.getImage(getControl()); if (image == null) image= getDefaultCueImage(); fControlDecoration.setImage(image); } }; labelProvider.addListener(listener); //initialize control decoration: listener.labelProviderChanged(new LabelProviderChangedEvent(labelProvider)); } } /** * Returns the default cue image. * * @return the default cue image * @since 3.3 */ private Image getDefaultCueImage() { if (fCachedDefaultCueImage == null) { ImageDescriptor cueID= ImageDescriptor.createFromFile(AbstractControlContentAssistSubjectAdapter.class, "images/content_assist_cue.png"); //$NON-NLS-1$ fCachedDefaultCueImage= cueID.createImage(getControl().getDisplay()); } return fCachedDefaultCueImage; } }