Package org.eclipse.jface.text.contentassist

Source Code of org.eclipse.jface.text.contentassist.ContentAssistant$InternalListener

/*******************************************************************************
* 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
*     Guy Gurfinkel, guy.g@zend.com - [content assist][api] provide better access to ContentAssistant - https://bugs.eclipse.org/bugs/show_bug.cgi?id=169954
*     Anton Leherbauer (Wind River Systems) - [content assist][api] ContentAssistEvent should contain information about auto activation - https://bugs.eclipse.org/bugs/show_bug.cgi?id=193728
*******************************************************************************/
package org.eclipse.jface.text.contentassist;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
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.Point;
import org.eclipse.swt.graphics.Rectangle;
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.Monitor;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;

import org.eclipse.core.commands.IHandler;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ListenerList;

import org.eclipse.jface.bindings.keys.KeySequence;
import org.eclipse.jface.contentassist.IContentAssistSubjectControl;
import org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.preference.JFacePreferences;
import org.eclipse.jface.util.Geometry;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.IEventConsumer;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.IViewportListener;
import org.eclipse.jface.text.IWidgetTokenKeeper;
import org.eclipse.jface.text.IWidgetTokenKeeperExtension;
import org.eclipse.jface.text.IWidgetTokenOwner;
import org.eclipse.jface.text.IWidgetTokenOwnerExtension;
import org.eclipse.jface.text.TextUtilities;


/**
* The standard implementation of the <code>IContentAssistant</code> interface. Usually, clients
* instantiate this class and configure it before using it.
*/
public class ContentAssistant implements IContentAssistant, IContentAssistantExtension, IContentAssistantExtension2, IContentAssistantExtension3, IContentAssistantExtension4, IWidgetTokenKeeper, IWidgetTokenKeeperExtension {
 
 
 
  /**
   * Content assist command identifier for 'select next proposal'.
   *
   * @since 3.4
   */
  public static final String SELECT_NEXT_PROPOSAL_COMMAND_ID= "org.eclipse.ui.edit.text.contentAssist.selectNextProposal"; //$NON-NLS-1$
  /**
   * Content assist command identifier for 'select previous proposal'.
   *
   * @since 3.4
   */
  public static final String SELECT_PREVIOUS_PROPOSAL_COMMAND_ID= "org.eclipse.ui.edit.text.contentAssist.selectPreviousProposal"; //$NON-NLS-1$
 

  /**
   * A generic closer class used to monitor various interface events in order to determine whether
   * content-assist should be terminated and all associated windows closed.
   */
  class Closer implements ControlListener, MouseListener, FocusListener, DisposeListener, IViewportListener {

    /** The shell that a <code>ControlListener</code> is registered with. */
    private Shell fShell;
    /**
     * The control that a <code>MouseListener</code>, a<code>FocusListener</code> and a
     * <code>DisposeListener</code> are registered with.
     */
    private Control fControl;

    /**
     * Installs this closer on it's viewer's text widget.
     */
    protected void install() {
      Control control= fContentAssistSubjectControlAdapter.getControl();
      fControl= control;
      if (Helper.okToUse(control)) {

        Shell shell= control.getShell();
        fShell= shell;
        shell.addControlListener(this);

        control.addMouseListener(this);
        control.addFocusListener(this);

        /*
         * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of
         * Internal Errors
         */
        control.addDisposeListener(this);
      }
      if (fViewer != null)
        fViewer.addViewportListener(this);
    }

    /**
     * Uninstalls this closer from the viewer's text widget.
     */
    protected void uninstall() {
      Control shell= fShell;
      fShell= null;
      if (Helper.okToUse(shell))
        shell.removeControlListener(this);

      Control control= fControl;
      fControl= null;
      if (Helper.okToUse(control)) {

        control.removeMouseListener(this);
        control.removeFocusListener(this);

        /*
         * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of
         * Internal Errors
         */
        control.removeDisposeListener(this);
      }

      if (fViewer != null)
        fViewer.removeViewportListener(this);
    }

    /*
     * @see ControlListener#controlResized(ControlEvent)
     */
    public void controlResized(ControlEvent e) {
      hide();
    }

    /*
     * @see ControlListener#controlMoved(ControlEvent)
     */
    public void controlMoved(ControlEvent e) {
      hide();
    }

    /*
     * @see MouseListener#mouseDown(MouseEvent)
     */
    public void mouseDown(MouseEvent e) {
      hide();
    }

    /*
     * @see MouseListener#mouseUp(MouseEvent)
     */
    public void mouseUp(MouseEvent e) {
    }

    /*
     * @see MouseListener#mouseDoubleClick(MouseEvent)
     */
    public void mouseDoubleClick(MouseEvent e) {
      hide();
    }

    /*
     * @see FocusListener#focusGained(FocusEvent)
     */
    public void focusGained(FocusEvent e) {
    }

    /*
     * @see FocusListener#focusLost(FocusEvent)
     */
    public void focusLost(FocusEvent e) {
      Control control= fControl;
      if (Helper.okToUse(control)) {
        Display d= control.getDisplay();
        if (d != null) {
          d.asyncExec(new Runnable() {
            public void run() {
              if (!fProposalPopup.hasFocus() && (fContextInfoPopup == null || !fContextInfoPopup.hasFocus()))
                hide();
            }
          });
        }
      }
    }

    /*
     * @seeDisposeListener#widgetDisposed(DisposeEvent)
     */
    public void widgetDisposed(DisposeEvent e) {
      /*
       * 1GGYYWK: ITPJUI:ALL - Dismissing editor with code assist up causes lots of Internal
       * Errors
       */
      hide();
    }

    /*
     * @see IViewportListener#viewportChanged(int)
     */
    public void viewportChanged(int topIndex) {
      hide();
    }
  }

  /**
   * An implementation of <code>IContentAssistListener</code>, this class is used to monitor
   * key events in support of automatic activation of the content assistant. If enabled, the
   * implementation utilizes a thread to watch for input characters matching the activation
   * characters specified by the content assist processor, and if detected, will wait the
   * indicated delay interval before activating the content assistant.
   *
   * @since 3.4 protected, was added in 2.1 as private class
   */
  protected class AutoAssistListener extends KeyAdapter implements KeyListener, Runnable, VerifyKeyListener {

    private Thread fThread;
    private boolean fIsReset= false;
    private Object fMutex= new Object();
    private int fShowStyle;

    private final static int SHOW_PROPOSALS= 1;
    private final static int SHOW_CONTEXT_INFO= 2;

    protected AutoAssistListener() {
    }

    protected void start(int showStyle) {
      fShowStyle= showStyle;
      fThread= new Thread(this, JFaceTextMessages.getString("ContentAssistant.assist_delay_timer_name")); //$NON-NLS-1$
      fThread.start();
    }

    public void run() {
      try {
        while (true) {
          synchronized (fMutex) {
            if (fAutoActivationDelay != 0)
              fMutex.wait(fAutoActivationDelay);
            if (fIsReset) {
              fIsReset= false;
              continue;
            }
          }
          showAssist(fShowStyle);
          break;
        }
      } catch (InterruptedException e) {
      }
      fThread= null;
    }

    protected void reset(int showStyle) {
      synchronized (fMutex) {
        fShowStyle= showStyle;
        fIsReset= true;
        fMutex.notifyAll();
      }
    }

    protected void stop() {
      Thread threadToStop= fThread;
      if (threadToStop != null && threadToStop.isAlive())
        threadToStop.interrupt();
    }

    private boolean contains(char[] characters, char character) {
      if (characters != null) {
        for (int i= 0; i < characters.length; i++) {
          if (character == characters[i])
            return true;
        }
      }
      return false;
    }

    public void keyPressed(KeyEvent e) {
      // Only act on typed characters and ignore modifier-only events
      if (e.character == 0 && (e.keyCode & SWT.KEYCODE_BIT) == 0)
        return;
     
      if (e.character != 0 && (e.stateMask == SWT.ALT))
        return;
     
      // Only act on characters that are trigger candidates. This
      // avoids computing the model selection on every keystroke
      if (computeAllAutoActivationTriggers().indexOf(e.character) < 0) {
        stop();
        return;
      }

      int showStyle;
      int pos= fContentAssistSubjectControlAdapter.getSelectedRange().x;
      char[] activation;

      activation= fContentAssistSubjectControlAdapter.getCompletionProposalAutoActivationCharacters(ContentAssistant.this, pos);

      if (contains(activation, e.character) && !isProposalPopupActive())
        showStyle= SHOW_PROPOSALS;
      else {
        activation= fContentAssistSubjectControlAdapter.getContextInformationAutoActivationCharacters(ContentAssistant.this, pos);
        if (contains(activation, e.character) && !isContextInfoPopupActive())
          showStyle= SHOW_CONTEXT_INFO;
        else {
          stop();
          return;
        }
      }

      if (fThread != null && fThread.isAlive())
        reset(showStyle);
      else
        start(showStyle);
    }

    /*
     * @see org.eclipse.swt.custom.VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
     */
    public void verifyKey(VerifyEvent event) {
      keyPressed(event);
    }

    protected void showAssist(final int showStyle) {
      final Control control= fContentAssistSubjectControlAdapter.getControl();
      if (control == null)
        return;
     
      final Display d= control.getDisplay();
      if (d == null)
        return;
     
      try {
        d.syncExec(new Runnable() {
          public void run() {
            if (isProposalPopupActive())
              return;
           
            if (control.isDisposed() || !control.isFocusControl())
              return;
           
            if (showStyle == SHOW_PROPOSALS) {
              if (!prepareToShowCompletions(true))
                return;
              fProposalPopup.showProposals(true);
              fLastAutoActivation= System.currentTimeMillis();
            } else if (showStyle == SHOW_CONTEXT_INFO && fContextInfoPopup != null) {
              promoteKeyListener();
              fContextInfoPopup.showContextProposals(true);
            }
          }
        });
      } catch (SWTError e) {
      }
    }
  }

  /**
   * The layout manager layouts the various windows associated with the content assistant based on
   * the settings of the content assistant.
   */
  class LayoutManager implements Listener {

    // Presentation types.
    /** The presentation type for the proposal selection popup. */
    public final static int LAYOUT_PROPOSAL_SELECTOR= 0;
    /** The presentation type for the context selection popup. */
    public final static int LAYOUT_CONTEXT_SELECTOR= 1;
    /** The presentation type for the context information hover . */
    public final static int LAYOUT_CONTEXT_INFO_POPUP= 2;

    int fContextType= LAYOUT_CONTEXT_SELECTOR;
    Shell[] fShells= new Shell[3];
    Object[] fPopups= new Object[3];

    protected void add(Object popup, Shell shell, int type, int offset) {
      Assert.isNotNull(popup);
      Assert.isTrue(shell != null && !shell.isDisposed());
      checkType(type);

      if (fShells[type] != shell) {
        if (fShells[type] != null)
          fShells[type].removeListener(SWT.Dispose, this);
        shell.addListener(SWT.Dispose, this);
        fShells[type]= shell;
      }

      fPopups[type]= popup;
      if (type == LAYOUT_CONTEXT_SELECTOR || type == LAYOUT_CONTEXT_INFO_POPUP)
        fContextType= type;

      layout(type, offset);
      adjustListeners(type);
    }

    protected void checkType(int type) {
      Assert.isTrue(type == LAYOUT_PROPOSAL_SELECTOR ||
        type == LAYOUT_CONTEXT_SELECTOR || type == LAYOUT_CONTEXT_INFO_POPUP);
    }

    public void handleEvent(Event event) {
      Widget source= event.widget;
      source.removeListener(SWT.Dispose, this);

      int type= getShellType(source);
      checkType(type);
      fShells[type]= null;

      switch (type) {
        case LAYOUT_PROPOSAL_SELECTOR:
          if (fContextType == LAYOUT_CONTEXT_SELECTOR &&
              Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
            // Restore event notification to the tip popup.
            addContentAssistListener((IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
          }
          break;

        case LAYOUT_CONTEXT_SELECTOR:
          if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
            if (fProposalPopupOrientation == PROPOSAL_STACKED)
              layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
            // Restore event notification to the proposal popup.
            addContentAssistListener((IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
          }
          fContextType= LAYOUT_CONTEXT_INFO_POPUP;
          break;

        case LAYOUT_CONTEXT_INFO_POPUP:
          if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
            if (fContextInfoPopupOrientation == CONTEXT_INFO_BELOW)
              layout(LAYOUT_PROPOSAL_SELECTOR, getSelectionOffset());
          }
          fContextType= LAYOUT_CONTEXT_SELECTOR;
          break;
      }
    }

    protected int getShellType(Widget shell) {
      for (int i= 0; i < fShells.length; i++) {
        if (fShells[i] == shell)
          return i;
      }
      return -1;
    }

    /**
     * Layouts the popup defined by <code>type</code> at the given widget offset.
     *
     * @param type the kind of popup to layout
     * @param offset the widget offset
     */
    protected void layout(int type, int offset) {
      switch (type) {
        case LAYOUT_PROPOSAL_SELECTOR:
          layoutProposalSelector(offset);
          break;
        case LAYOUT_CONTEXT_SELECTOR:
          layoutContextSelector(offset);
          break;
        case LAYOUT_CONTEXT_INFO_POPUP:
          layoutContextInfoPopup(offset);
          break;
      }
    }

    protected void layoutProposalSelector(int offset) {
      if (fContextType == LAYOUT_CONTEXT_INFO_POPUP &&
          fContextInfoPopupOrientation == CONTEXT_INFO_BELOW &&
          Helper.okToUse(fShells[LAYOUT_CONTEXT_INFO_POPUP])) {
        // Stack proposal selector beneath the tip box.
        Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
        Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
        shell.setLocation(getStackedLocation(shell, parent));
      } else if (fContextType != LAYOUT_CONTEXT_SELECTOR ||
            !Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR])) {
        // There are no other presentations to be concerned with,
        // so place the proposal selector beneath the cursor line.
        Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
        CompletionProposalPopup popup= (CompletionProposalPopup) fPopups[LAYOUT_PROPOSAL_SELECTOR];
        shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
      } else {
        CompletionProposalPopup popup= ((CompletionProposalPopup) fPopups[LAYOUT_PROPOSAL_SELECTOR]);
        switch (fProposalPopupOrientation) {
          case PROPOSAL_REMOVE: {
            // Remove the tip selector and place the
            // proposal selector beneath the cursor line.
            fShells[LAYOUT_CONTEXT_SELECTOR].dispose();
            Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
            shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
            break;
          }
          case PROPOSAL_OVERLAY: {
            // Overlay the tip selector with the proposal selector.
            Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
            shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, popup));
            break;
          }
          case PROPOSAL_STACKED: {
            // Stack the proposal selector beneath the tip selector.
            Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
            Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
            shell.setLocation(getStackedLocation(shell, parent));
            break;
          }
        }
      }
    }

    protected void layoutContextSelector(int offset) {
      // Always place the context selector beneath the cursor line.
      Shell shell= fShells[LAYOUT_CONTEXT_SELECTOR];
      shell.setBounds(computeBoundsBelowAbove(shell, shell.getSize(), offset, null));

      if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
        switch (fProposalPopupOrientation) {
          case PROPOSAL_REMOVE:
            // Remove the proposal selector.
            fShells[LAYOUT_PROPOSAL_SELECTOR].dispose();
            break;

          case PROPOSAL_OVERLAY:
            // The proposal selector has been overlaid by the tip selector.
            break;

          case PROPOSAL_STACKED: {
            // Stack the proposal selector beneath the tip selector.
            shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
            Shell parent= fShells[LAYOUT_CONTEXT_SELECTOR];
            shell.setLocation(getStackedLocation(shell, parent));
            break;
          }
        }
      }
    }

    protected void layoutContextInfoPopup(int offset) {
      switch (fContextInfoPopupOrientation) {
        case CONTEXT_INFO_ABOVE: {
          // Place the popup above the cursor line.
          Shell shell= fShells[LAYOUT_CONTEXT_INFO_POPUP];
          shell.setBounds(computeBoundsAboveBelow(shell, shell.getSize(), offset));
          break;
        }
        case CONTEXT_INFO_BELOW: {
          // Place the popup beneath the cursor line.
          Shell parent= fShells[LAYOUT_CONTEXT_INFO_POPUP];
          parent.setBounds(computeBoundsBelowAbove(parent, parent.getSize(), offset, null));
          if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR])) {
            // Stack the proposal selector beneath the context info popup.
            Shell shell= fShells[LAYOUT_PROPOSAL_SELECTOR];
            shell.setLocation(getStackedLocation(shell, parent));
          }
          break;
        }
      }
    }

    /**
     * Moves <code>point</code> such that <code>rectangle</code> does not bleed outside of
     * <code>bounds</code>. All coordinates must have the same reference.
     *
     * @param point the point to move if needed
     * @param shellSize the size of the shell that may be moved
     * @param bounds the bounds
     * @since 3.3
     */
    protected void constrainLocation(Point point, Point shellSize, Rectangle bounds) {
      if (point.x + shellSize.x > bounds.x + bounds.width)
        point.x= bounds.x + bounds.width - shellSize.x;

      if (point.x < bounds.x)
        point.x= bounds.x;
     
      if (point.y + shellSize.y > bounds.y + bounds.height)
        point.y= bounds.y + bounds.height - shellSize.y;
     
      if (point.y < bounds.y)
        point.y= bounds.y;
    }

    protected Rectangle constrainHorizontally(Rectangle rect, Rectangle bounds) {
      // clip width
      if (rect.width > bounds.width)
        rect.width= bounds.width;
     
      if (rect.x + rect.width > bounds.x + bounds.width)
        rect.x= bounds.x + bounds.width - rect.width;
      if (rect.x < bounds.x)
        rect.x= bounds.x;
     
      return rect;
    }
   
    /**
     * Returns the display bounds for <code>shell</code> such that it appears right above
     * <code>offset</code>, or below it if above is not suitable. The returned bounds lie
     * within the monitor at the caret location and never overlap with the caret line.
     *
     * @param shell the shell to compute the placement for
     * @param preferred the preferred size for <code>shell</code>
     * @param offset the caret offset in the subject control
     * @return the point right above <code>offset</code> in display coordinates
     * @since 3.3
     */
    protected Rectangle computeBoundsAboveBelow(Shell shell, Point preferred, int offset) {
      Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
      Display display= subjectControl.getDisplay();
      Rectangle caret= getCaretRectangle(offset);
      Monitor monitor= getClosestMonitor(display, caret);
      Rectangle bounds= monitor.getClientArea();
      Geometry.moveInside(caret, bounds);
     
      int spaceAbove= caret.y - bounds.y;
      int caretLowerY= caret.y + caret.height;
      int spaceBelow= bounds.y + bounds.height - caretLowerY;
      Rectangle rect;
      if (spaceAbove >= preferred.y)
        rect= new Rectangle(caret.x, caret.y - preferred.y, preferred.x, preferred.y);
      else if (spaceBelow >= preferred.y)
        rect= new Rectangle(caret.x, caretLowerY, preferred.x, preferred.y);
      // we can't fit in the preferred size - squeeze into larger area
      else if (spaceBelow <= spaceAbove)
        rect= new Rectangle(caret.x, bounds.y, preferred.x, spaceAbove);
      else
        rect= new Rectangle(caret.x, caretLowerY, preferred.x, spaceBelow);
     
      return constrainHorizontally(rect, bounds);
    }

    /**
     * Returns the display bounds for <code>shell</code> such that it appears right below
     * <code>offset</code>, or above it if below is not suitable. The returned bounds lie
     * within the monitor at the caret location and never overlap with the caret line.
     *
     * @param shell the shell to compute the placement for
     * @param preferred the preferred size for <code>shell</code>
     * @param offset the caret offset in the subject control
     * @param popup a popup to inform if the location was switched to above, <code>null</code> to do nothing
     * @return the point right below <code>offset</code> in display coordinates
     * @since 3.3
     */
    protected Rectangle computeBoundsBelowAbove(Shell shell, Point preferred, int offset, CompletionProposalPopup popup) {
      Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
      Display display= subjectControl.getDisplay();
      Rectangle caret= getCaretRectangle(offset);
      Monitor monitor= getClosestMonitor(display, caret);
      Rectangle bounds= monitor.getClientArea();
      Geometry.moveInside(caret, bounds);

      int threshold= popup == null ? Integer.MAX_VALUE : popup.getMinimalHeight();
      int spaceAbove= caret.y - bounds.y;
      int spaceBelow= bounds.y + bounds.height - (caret.y + caret.height);
      Rectangle rect;
      boolean switched= false;
      if (spaceBelow >= preferred.y)
        rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, preferred.y);
      // squeeze in below if we have at least threshold space
      else if (spaceBelow >= threshold)
        rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, spaceBelow);
      else if (spaceAbove >= preferred.y) {
        rect= new Rectangle(caret.x, caret.y - preferred.y, preferred.x, preferred.y);
        switched= true;
      } else if (spaceBelow >= spaceAbove) {
        // we can't fit in the preferred size - squeeze into larger area
        rect= new Rectangle(caret.x, caret.y + caret.height, preferred.x, spaceBelow);
      } else {
        rect= new Rectangle(caret.x, bounds.y, preferred.x, spaceAbove);
        switched= true;
      }
     
      if (popup != null)
        popup.switchedPositionToAbove(switched);
     
      return constrainHorizontally(rect, bounds);
    }

    private Rectangle getCaretRectangle(int offset) {
      Point location= fContentAssistSubjectControlAdapter.getLocationAtOffset(offset);
      Control subjectControl= fContentAssistSubjectControlAdapter.getControl();
      Point controlSize= subjectControl.getSize();
      constrainLocation(location, new Point(0, 0), new Rectangle(0, 0, controlSize.x, controlSize.y));
      location= subjectControl.toDisplay(location);
      Rectangle subjectRectangle= new Rectangle(location.x, location.y, 1, fContentAssistSubjectControlAdapter.getLineHeight());
      return subjectRectangle;
    }
   
    protected Point getStackedLocation(Shell shell, Shell parent) {
      Point p= parent.getLocation();
      Point size= parent.getSize();
      p.x += size.x / 4;
      p.y += size.y;

      p= parent.toDisplay(p);

      Point shellSize= shell.getSize();
      Monitor monitor= getClosestMonitor(parent.getDisplay(), new Rectangle(p.x, p.y, 0, 0));
      Rectangle displayBounds= monitor.getClientArea();
      constrainLocation(p, shellSize, displayBounds);

      return p;
    }

    protected void adjustListeners(int type) {
      switch (type) {
        case LAYOUT_PROPOSAL_SELECTOR:
          if (fContextType == LAYOUT_CONTEXT_SELECTOR &&
              Helper.okToUse(fShells[LAYOUT_CONTEXT_SELECTOR]))
            // Disable event notification to the tip selector.
            removeContentAssistListener((IContentAssistListener) fPopups[LAYOUT_CONTEXT_SELECTOR], CONTEXT_SELECTOR);
          break;
        case LAYOUT_CONTEXT_SELECTOR:
          if (Helper.okToUse(fShells[LAYOUT_PROPOSAL_SELECTOR]))
            // Disable event notification to the proposal selector.
            removeContentAssistListener((IContentAssistListener) fPopups[LAYOUT_PROPOSAL_SELECTOR], PROPOSAL_SELECTOR);
          break;
        case LAYOUT_CONTEXT_INFO_POPUP:
          break;
      }
    }
   
    /**
     * Copied from org.eclipse.jface.window.Window.
     * Returns the monitor whose client area contains the given point. If no
     * monitor contains the point, returns the monitor that is closest to the
     * point. If this is ever made public, it should be moved into a separate
     * utility class.
     *
     * @param toSearch
     *            point to find (display coordinates)
     * @param rectangle
     *            rectangle to find (display coordinates)
     * @return the monitor closest to the given point
     * @since 3.3
     */
    private Monitor getClosestMonitor(Display toSearch, Rectangle rectangle) {
      int closest = Integer.MAX_VALUE;

      Point toFind= Geometry.centerPoint(rectangle);
      Monitor[] monitors = toSearch.getMonitors();
      Monitor result = monitors[0];

      for (int idx = 0; idx < monitors.length; idx++) {
        Monitor current = monitors[idx];

        Rectangle clientArea = current.getClientArea();

        if (clientArea.contains(toFind)) {
          return current;
        }

        int distance = Geometry.distanceSquared(Geometry.centerPoint(clientArea), toFind);
        if (distance < closest) {
          closest = distance;
          result = current;
        }
      }

      return result;
    }
  }

  /**
   * Internal key listener and event consumer.
   */
  class InternalListener implements VerifyKeyListener, IEventConsumer {

    /**
     * Verifies key events by notifying the registered listeners. Each listener is allowed to
     * indicate that the event has been handled and should not be further processed.
     *
     * @param e the verify event
     * @see VerifyKeyListener#verifyKey(org.eclipse.swt.events.VerifyEvent)
     */
    public void verifyKey(VerifyEvent e) {
      IContentAssistListener[] listeners= (IContentAssistListener[]) fListeners.clone();
      for (int i= 0; i < listeners.length; i++) {
        if (listeners[i] != null) {
          if (!listeners[i].verifyKey(e) || !e.doit)
            break;
        }
      }
      if (fAutoAssistListener != null)
        fAutoAssistListener.keyPressed(e);
    }

    /*
     * @see IEventConsumer#processEvent
     */
    public void processEvent(VerifyEvent event) {

      installKeyListener();

      IContentAssistListener[] listeners= (IContentAssistListener[]) fListeners.clone();
      for (int i= 0; i < listeners.length; i++) {
        if (listeners[i] != null) {
          listeners[i].processEvent(event);
          if (!event.doit)
            return;
        }
      }
    }
  }

  /**
   * Dialog store constants.
   *
   * @since 3.0
   */
  public static final String STORE_SIZE_X= "size.x"; //$NON-NLS-1$
  public static final String STORE_SIZE_Y= "size.y"; //$NON-NLS-1$

  // Content-Assist Listener types
  final static int CONTEXT_SELECTOR= 0;
  final static int PROPOSAL_SELECTOR= 1;
  final static int CONTEXT_INFO_POPUP= 2;

  /**
   * The popup priority: &gt; linked position proposals and hover pop-ups. Default value:
   * <code>20</code>;
   *
   * @since 3.0
   */
  public static final int WIDGET_PRIORITY= 20;

  private static final int DEFAULT_AUTO_ACTIVATION_DELAY= 500;

  private IInformationControlCreator fInformationControlCreator;
  private int fAutoActivationDelay= DEFAULT_AUTO_ACTIVATION_DELAY;
  private boolean fIsAutoActivated= false;
  private boolean fIsAutoInserting= false;
  private int fProposalPopupOrientation= PROPOSAL_OVERLAY;
  private int fContextInfoPopupOrientation= CONTEXT_INFO_ABOVE;
  private Map fProcessors;

  /**
   * The partitioning.
   *
   * @since 3.0
   */
  private String fPartitioning;

  private Color fContextInfoPopupBackground;
  private Color fContextInfoPopupForeground;
  private Color fContextSelectorBackground;
  private Color fContextSelectorForeground;
  private Color fProposalSelectorBackground;
  private Color fProposalSelectorForeground;

  private ITextViewer fViewer;
  private String fLastErrorMessage;

  private Closer fCloser;
  LayoutManager fLayoutManager;
  private AutoAssistListener fAutoAssistListener;
  private InternalListener fInternalListener;
  private CompletionProposalPopup fProposalPopup;
  private ContextInformationPopup fContextInfoPopup;

  /**
   * Flag which tells whether a verify key listener is hooked.
   *
   * @since 3.0
   */
  private boolean fVerifyKeyListenerHooked= false;
  private IContentAssistListener[] fListeners= new IContentAssistListener[4];
  /**
   * The content assist subject control.
   *
   * @since 3.0
   */
  private IContentAssistSubjectControl fContentAssistSubjectControl;
  /**
   * The content assist subject control's shell.
   *
   * @since 3.2
   */
  private Shell fContentAssistSubjectControlShell;
  /**
   * The content assist subject control's shell traverse listener.
   *
   * @since 3.2
   */
  private TraverseListener fCASCSTraverseListener;
  /**
   * The content assist subject control adapter.
   *
   * @since 3.0
   */
  private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
  /**
   * The dialog settings for the control's bounds.
   *
   * @since 3.0
   */
  private IDialogSettings fDialogSettings;
  /**
   * Prefix completion setting.
   *
   * @since 3.0
   */
  private boolean fIsPrefixCompletionEnabled= false;
  /**
   * The list of completion listeners.
   *
   * @since 3.2
   */
  private ListenerList fCompletionListeners= new ListenerList(ListenerList.IDENTITY);
  /**
   * The message to display at the bottom of the proposal popup.
   *
   * @since 3.2
   */
  private String fMessage= ""; //$NON-NLS-1$
  /**
   * The cycling mode property.
   *
   * @since 3.2
   */
  private boolean fIsRepetitionMode= false;
  /**
   * The show empty property.
   *
   * @since 3.2
   */
  private boolean fShowEmptyList= false;
  /**
   * The message line property.
   *
   * @since 3.2
   */
  private boolean fIsStatusLineVisible;
  /**
   * The last system time when auto activation performed.
   *
   * @since 3.2
   */
  private long fLastAutoActivation= Long.MIN_VALUE;
  /**
   * The iteration key sequence to listen for, or <code>null</code>.
   *
   * @since 3.2
   */
  private KeySequence fRepeatedInvocationKeySequence;
 
  /**
   * Maps handler to command identifiers.
   *
   * @since 3.4
   */
  private Map fHandlers;
 
  /**
   * Tells whether colored labels support is enabled.
   *
   * @since 3.4
   */
  private boolean fIsColoredLabelsSupportEnabled= false;


  /**
   * Creates a new content assistant. The content assistant is not automatically activated,
   * overlays the completion proposals with context information list if necessary, and shows the
   * context information above the location at which it was activated. If auto activation will be
   * enabled, without further configuration steps, this content assistant is activated after a 500
   * milliseconds delay. It uses the default partitioning.
   */
  public ContentAssistant() {
    fPartitioning= IDocumentExtension3.DEFAULT_PARTITIONING;
  }

  /**
   * Sets the document partitioning this content assistant is using.
   *
   * @param partitioning the document partitioning for this content assistant
   * @since 3.0
   */
  public void setDocumentPartitioning(String partitioning) {
    Assert.isNotNull(partitioning);
    fPartitioning= partitioning;
  }

  /*
   * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension#getDocumentPartitioning()
   * @since 3.0
   */
  public String getDocumentPartitioning() {
    return fPartitioning;
  }

  /**
   * Registers a given content assist processor for a particular content type. If there is already
   * a processor registered for this type, the new processor is registered instead of the old one.
   *
   * @param processor the content assist processor to register, or <code>null</code> to remove
   *        an existing one
   * @param contentType the content type under which to register
   */
  public void setContentAssistProcessor(IContentAssistProcessor processor, String contentType) {

    Assert.isNotNull(contentType);

    if (fProcessors == null)
      fProcessors= new HashMap();

    if (processor == null)
      fProcessors.remove(contentType);
    else
      fProcessors.put(contentType, processor);
  }

  /*
   * @see IContentAssistant#getContentAssistProcessor
   */
  public IContentAssistProcessor getContentAssistProcessor(String contentType) {
    if (fProcessors == null)
      return null;

    return (IContentAssistProcessor) fProcessors.get(contentType);
  }

  /**
   * Computes the sorted set of all auto activation trigger characters.
   *
   * @return the sorted set of all auto activation trigger characters
   * @since 3.1
   */
  private String computeAllAutoActivationTriggers() {
    if (fProcessors == null)
      return ""; //$NON-NLS-1$

    StringBuffer buf= new StringBuffer(5);
    Iterator iter= fProcessors.entrySet().iterator();
    while (iter.hasNext()) {
      Entry entry= (Entry) iter.next();
      IContentAssistProcessor processor= (IContentAssistProcessor) entry.getValue();
      char[] triggers= processor.getCompletionProposalAutoActivationCharacters();
      if (triggers != null)
        buf.append(triggers);
      triggers= processor.getContextInformationAutoActivationCharacters();
      if (triggers != null)
        buf.append(triggers);
    }
    return buf.toString();
  }

  /**
   * Enables the content assistant's auto activation mode.
   *
   * @param enabled indicates whether auto activation is enabled or not
   */
  public void enableAutoActivation(boolean enabled) {
    fIsAutoActivated= enabled;
    manageAutoActivation(fIsAutoActivated);
  }

  /**
   * Enables the content assistant's auto insertion mode. If enabled, the content assistant
   * inserts a proposal automatically if it is the only proposal. In the case of ambiguities, the
   * user must make the choice.
   *
   * @param enabled indicates whether auto insertion is enabled or not
   * @since 2.0
   */
  public void enableAutoInsert(boolean enabled) {
    fIsAutoInserting= enabled;
  }

  /**
   * Returns whether this content assistant is in the auto insertion mode or not.
   *
   * @return <code>true</code> if in auto insertion mode
   * @since 2.0
   */
  boolean isAutoInserting() {
    return fIsAutoInserting;
  }

  /**
   * Installs and uninstall the listeners needed for auto activation.
   *
   * @param start <code>true</code> if listeners must be installed, <code>false</code> if they
   *        must be removed
   * @since 2.0
   */
  private void manageAutoActivation(boolean start) {
    if (start) {

      if ((fContentAssistSubjectControlAdapter != null) && fAutoAssistListener == null) {
        fAutoAssistListener= createAutoAssistListener();
        // For details see https://bugs.eclipse.org/bugs/show_bug.cgi?id=49212
        if (fContentAssistSubjectControlAdapter.supportsVerifyKeyListener())
          fContentAssistSubjectControlAdapter.appendVerifyKeyListener(fAutoAssistListener);
        else
          fContentAssistSubjectControlAdapter.addKeyListener(fAutoAssistListener);
      }

    } else if (fAutoAssistListener != null) {
      // For details see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=49212
      if (fContentAssistSubjectControlAdapter.supportsVerifyKeyListener())
        fContentAssistSubjectControlAdapter.removeVerifyKeyListener(fAutoAssistListener);
      else
        fContentAssistSubjectControlAdapter.removeKeyListener(fAutoAssistListener);
      fAutoAssistListener= null;
    }
  }

  /**
   * This method allows subclasses to provide their own {@link AutoAssistListener}.
   *
   * @return a new auto assist listener
   * @since 3.4
   */
  protected AutoAssistListener createAutoAssistListener() {
    return new AutoAssistListener();
  }

  /**
   * Sets the delay after which the content assistant is automatically invoked if the cursor is
   * behind an auto activation character.
   *
   * @param delay the auto activation delay
   */
  public void setAutoActivationDelay(int delay) {
    fAutoActivationDelay= delay;
  }

  /**
   * Gets the delay after which the content assistant is automatically invoked if the cursor is
   * behind an auto activation character.
   *
   * @return the auto activation delay
   * @since 3.4
   */
  public int getAutoActivationDelay() {
    return fAutoActivationDelay;
  }

  /**
   * Sets the proposal pop-ups' orientation. The following values may be used:
   * <ul>
   *   <li>PROPOSAL_OVERLAY<p>
   *     proposal popup windows should overlay each other
   *   </li>
   *   <li>PROPOSAL_REMOVE<p>
   *     any currently shown proposal popup should be closed
   *   </li>
   *   <li>PROPOSAL_STACKED<p>
   *     proposal popup windows should be vertical stacked, with no overlap,
   *     beneath the line containing the current cursor location
   *   </li>
   * </ul>
   *
   * @param orientation the popup's orientation
   */
  public void setProposalPopupOrientation(int orientation) {
    fProposalPopupOrientation= orientation;
  }

  /**
   * Sets the context information popup's orientation.
   * The following values may be used:
   * <ul>
   *   <li>CONTEXT_ABOVE<p>
   *     context information popup should always appear above the line containing
   *     the current cursor location
   *   </li>
   *   <li>CONTEXT_BELOW<p>
   *     context information popup should always appear below the line containing
   *     the current cursor location
   *   </li>
   * </ul>
   *
   * @param orientation the popup's orientation
   */
  public void setContextInformationPopupOrientation(int orientation) {
    fContextInfoPopupOrientation= orientation;
  }

  /**
   * Sets the context information popup's background color.
   *
   * @param background the background color
   */
  public void setContextInformationPopupBackground(Color background) {
    fContextInfoPopupBackground= background;
  }

  /**
   * Returns the background of the context information popup.
   *
   * @return the background of the context information popup
   * @since 2.0
   */
  Color getContextInformationPopupBackground() {
    return fContextInfoPopupBackground;
  }

  /**
   * Sets the context information popup's foreground color.
   *
   * @param foreground the foreground color
   * @since 2.0
   */
  public void setContextInformationPopupForeground(Color foreground) {
    fContextInfoPopupForeground= foreground;
  }

  /**
   * Returns the foreground of the context information popup.
   *
   *
   * @return the foreground of the context information popup
   * @since 2.0
   */
  Color getContextInformationPopupForeground() {
    return fContextInfoPopupForeground;
  }

  /**
   * Sets the proposal selector's background color.
   * <p>
   * <strong>Note:</strong> As of 3.4, you should only call this
   * method if you want to override the {@link JFacePreferences#CONTENT_ASSIST_BACKGROUND_COLOR}.
   * </p>
   *
   * @param background the background color
   * @since 2.0
   */
  public void setProposalSelectorBackground(Color background) {
    fProposalSelectorBackground= background;
  }

  /**
   * Returns the custom background color of the proposal selector.
   *
   * @return the background of the proposal selector or <code>null</code> if not set
   * @since 2.0
   */
  Color getProposalSelectorBackground() {
    return fProposalSelectorBackground;
  }

  /**
   * Sets the proposal's foreground color.
   * <p>
   * <strong>Note:</strong> As of 3.4, you should only call this
   * method if you want to override the {@link JFacePreferences#CONTENT_ASSIST_FOREGROUND_COLOR}.
   * </p>
   *
   * @param foreground the foreground color
   * @since 2.0
   */
  public void setProposalSelectorForeground(Color foreground) {
    fProposalSelectorForeground= foreground;
  }

  /**
   * Returns the custom foreground color of the proposal selector.
   *
   * @return the foreground of the proposal selector or <code>null</code> if not set
   * @since 2.0
   */
  Color getProposalSelectorForeground() {
    return fProposalSelectorForeground;
  }

  /**
   * Sets the context selector's background color.
   *
   * @param background the background color
   * @since 2.0
   */
  public void setContextSelectorBackground(Color background) {
    fContextSelectorBackground= background;
  }

  /**
   * Returns the background of the context selector.
   *
   * @return the background of the context selector
   * @since 2.0
   */
  Color getContextSelectorBackground() {
    return fContextSelectorBackground;
  }

  /**
   * Sets the context selector's foreground color.
   *
   * @param foreground the foreground color
   * @since 2.0
   */
  public void setContextSelectorForeground(Color foreground) {
    fContextSelectorForeground= foreground;
  }

  /**
   * Returns the foreground of the context selector.
   *
   * @return the foreground of the context selector
   * @since 2.0
   */
  Color getContextSelectorForeground() {
    return fContextSelectorForeground;
  }

  /**
   * Sets the information control creator for the additional information control.
   *
   * @param creator the information control creator for the additional information control
   * @since 2.0
   */
  public void setInformationControlCreator(IInformationControlCreator creator) {
    fInformationControlCreator= creator;
  }

  /*
   * @see IControlContentAssistant#install(IContentAssistSubjectControl)
   * @since 3.0
   */
  protected void install(IContentAssistSubjectControl contentAssistSubjectControl) {
    fContentAssistSubjectControl= contentAssistSubjectControl;
    fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl);
    install();
  }

  /*
   * @see IContentAssist#install
   * @since 3.0
   */
  public void install(ITextViewer textViewer) {
    fViewer= textViewer;
    fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer);
    install();
  }

  protected void install() {

    fLayoutManager= new LayoutManager();
    fInternalListener= new InternalListener();

    AdditionalInfoController controller= null;
    if (fInformationControlCreator != null) {
      int delay= fAutoActivationDelay;
      if (delay == 0)
        delay= DEFAULT_AUTO_ACTIVATION_DELAY;
      delay= Math.round(delay * 1.5f);
      controller= new AdditionalInfoController(fInformationControlCreator, delay);
    }

    fContextInfoPopup= fContentAssistSubjectControlAdapter.createContextInfoPopup(this);
    fProposalPopup= fContentAssistSubjectControlAdapter.createCompletionProposalPopup(this, controller);

    registerHandler(SELECT_NEXT_PROPOSAL_COMMAND_ID, fProposalPopup.createProposalSelectionHandler(CompletionProposalPopup.ProposalSelectionHandler.SELECT_NEXT));
    registerHandler(SELECT_PREVIOUS_PROPOSAL_COMMAND_ID, fProposalPopup.createProposalSelectionHandler(CompletionProposalPopup.ProposalSelectionHandler.SELECT_PREVIOUS));

    if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) {
      fContentAssistSubjectControlShell= fContentAssistSubjectControlAdapter.getControl().getShell();
      fCASCSTraverseListener= new TraverseListener() {
        public void keyTraversed(TraverseEvent e) {
          if (e.detail == SWT.TRAVERSE_ESCAPE && isProposalPopupActive())
            e.doit= false;
        }
      };
      fContentAssistSubjectControlShell.addTraverseListener(fCASCSTraverseListener);
    }

    manageAutoActivation(fIsAutoActivated);
  }

  /*
   * @see IContentAssist#uninstall
   */
  public void uninstall() {
    hide();
    manageAutoActivation(false);
   
    if (fHandlers != null) {
      fHandlers.clear();
      fHandlers= null;
    }

    if (fCloser != null) {
      fCloser.uninstall();
      fCloser= null;
    }

    if (Helper.okToUse(fContentAssistSubjectControlShell))
      fContentAssistSubjectControlShell.removeTraverseListener(fCASCSTraverseListener);
    fCASCSTraverseListener= null;
    fContentAssistSubjectControlShell= null;

    fViewer= null;
    fContentAssistSubjectControl= null;
    fContentAssistSubjectControlAdapter= null;
  }

  /**
   * Adds the given shell of the specified type to the layout. Valid types are defined by
   * <code>LayoutManager</code>.
   *
   * @param popup a content assist popup
   * @param shell the shell of the content-assist popup
   * @param type the type of popup
   * @param visibleOffset the offset at which to layout the popup relative to the offset of the
   *        viewer's visible region
   * @since 2.0
   */
  void addToLayout(Object popup, Shell shell, int type, int visibleOffset) {
    fLayoutManager.add(popup, shell, type, visibleOffset);
  }

  /**
   * Layouts the registered popup of the given type relative to the given offset. The offset is
   * relative to the offset of the viewer's visible region. Valid types are defined by
   * <code>LayoutManager</code>.
   *
   * @param type the type of popup to layout
   * @param visibleOffset the offset at which to layout relative to the offset of the viewer's
   *        visible region
   * @since 2.0
   */
  void layout(int type, int visibleOffset) {
    fLayoutManager.layout(type, visibleOffset);
  }
 
  /**
   * Returns the layout manager.
   *
   * @return the layout manager
   * @since 3.3
   */
  LayoutManager getLayoutManager() {
    return fLayoutManager;
  }

  /**
   * Notifies the controller that a popup has lost focus.
   *
   * @param e the focus event
   */
  void popupFocusLost(FocusEvent e) {
    fCloser.focusLost(e);
  }

  /**
   * Returns the offset of the selection relative to the offset of the visible region.
   *
   * @return the offset of the selection relative to the offset of the visible region
   * @since 2.0
   */
  int getSelectionOffset() {
    return fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x;
  }

  /**
   * Returns whether the widget token could be acquired. The following are valid listener types:
   * <ul>
   *   <li>AUTO_ASSIST</li>
   *   <li>CONTEXT_SELECTOR</li>
   *   <li>PROPOSAL_SELECTOR</li>
   *   <li>CONTEXT_INFO_POPUP</li>
   * </ul>
   *
   * @param type the listener type for which to acquire
   * @return <code>true</code> if the widget token could be acquired
   * @since 2.0
   */
  private boolean acquireWidgetToken(int type) {
    switch (type) {
      case CONTEXT_SELECTOR:
      case PROPOSAL_SELECTOR:
        if (fContentAssistSubjectControl instanceof IWidgetTokenOwnerExtension) {
          IWidgetTokenOwnerExtension extension= (IWidgetTokenOwnerExtension) fContentAssistSubjectControl;
          return extension.requestWidgetToken(this, WIDGET_PRIORITY);
        } else if (fContentAssistSubjectControl instanceof IWidgetTokenOwner) {
          IWidgetTokenOwner owner= (IWidgetTokenOwner) fContentAssistSubjectControl;
          return owner.requestWidgetToken(this);
        } else if (fViewer instanceof IWidgetTokenOwnerExtension) {
          IWidgetTokenOwnerExtension extension= (IWidgetTokenOwnerExtension) fViewer;
          return extension.requestWidgetToken(this, WIDGET_PRIORITY);
        } else if (fViewer instanceof IWidgetTokenOwner) {
          IWidgetTokenOwner owner= (IWidgetTokenOwner) fViewer;
          return owner.requestWidgetToken(this);
        }
    }
    return true;
  }

  /**
   * Registers a content assist listener. The following are valid listener types:
   * <ul>
   *   <li>AUTO_ASSIST</li>
   *   <li>CONTEXT_SELECTOR</li>
   *   <li>PROPOSAL_SELECTOR</li>
   *   <li>CONTEXT_INFO_POPUP</li>
   * </ul>
   * Returns whether the listener could be added successfully. A listener can not be added if the
   * widget token could not be acquired.
   *
   * @param listener the listener to register
   * @param type the type of listener
   * @return <code>true</code> if the listener could be added
   */
  boolean addContentAssistListener(IContentAssistListener listener, int type) {

    if (acquireWidgetToken(type)) {

      fListeners[type]= listener;

      if (fCloser == null && getNumberOfListeners() == 1) {
        fCloser= new Closer();
        fCloser.install();
        fContentAssistSubjectControlAdapter.setEventConsumer(fInternalListener);
        installKeyListener();
      } else
        promoteKeyListener();
      return true;
    }

    return false;
  }

  /**
   * Re-promotes the key listener to the first position, using prependVerifyKeyListener. This
   * ensures no other instance is filtering away the keystrokes underneath, if we've been up for a
   * while (e.g. when the context info is showing.
   *
   * @since 3.0
   */
  private void promoteKeyListener() {
    uninstallVerifyKeyListener();
    installKeyListener();
  }

  /**
   * Installs a key listener on the text viewer's widget.
   */
  private void installKeyListener() {
    if (!fVerifyKeyListenerHooked) {
      if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl())) {
        fVerifyKeyListenerHooked= fContentAssistSubjectControlAdapter.prependVerifyKeyListener(fInternalListener);
      }
    }
  }

  /**
   * Releases the previously acquired widget token if the token is no longer necessary. The
   * following are valid listener types:
   * <ul>
   *   <li>AUTO_ASSIST</li>
   *   <li>CONTEXT_SELECTOR</li>
   *   <li>PROPOSAL_SELECTOR</li>
   *   <li>CONTEXT_INFO_POPUP</li>
   * </ul>
   *
   * @param type the listener type
   * @since 2.0
   */
  private void releaseWidgetToken(int type) {
    if (fListeners[CONTEXT_SELECTOR] == null && fListeners[PROPOSAL_SELECTOR] == null) {
      IWidgetTokenOwner owner= null;
      if (fContentAssistSubjectControl instanceof IWidgetTokenOwner)
        owner= (IWidgetTokenOwner) fContentAssistSubjectControl;
      else if (fViewer instanceof IWidgetTokenOwner)
        owner= (IWidgetTokenOwner) fViewer;
      if (owner != null)
        owner.releaseWidgetToken(this);
    }
  }

  /**
   * Unregisters a content assist listener.
   *
   * @param listener the listener to unregister
   * @param type the type of listener
   * @see #addContentAssistListener(IContentAssistListener, int)
   */
  void removeContentAssistListener(IContentAssistListener listener, int type) {
    fListeners[type]= null;

    if (getNumberOfListeners() == 0) {

      if (fCloser != null) {
        fCloser.uninstall();
        fCloser= null;
      }

      uninstallVerifyKeyListener();
      fContentAssistSubjectControlAdapter.setEventConsumer(null);
    }

    releaseWidgetToken(type);
  }

  /**
   * Uninstall the key listener from the text viewer's widget.
   *
   * @since 3.0
   */
  private void uninstallVerifyKeyListener() {
    if (fVerifyKeyListenerHooked) {
      if (Helper.okToUse(fContentAssistSubjectControlAdapter.getControl()))
        fContentAssistSubjectControlAdapter.removeVerifyKeyListener(fInternalListener);
      fVerifyKeyListenerHooked= false;
    }
  }

  /**
   * Returns the number of listeners.
   *
   * @return the number of listeners
   * @since 2.0
   */
  private int getNumberOfListeners() {
    int count= 0;
    for (int i= 0; i <= CONTEXT_INFO_POPUP; i++) {
      if (fListeners[i] != null)
        ++count;
    }
    return count;
  }

  /*
   * @see IContentAssist#showPossibleCompletions
   */
  public String showPossibleCompletions() {
    if (!prepareToShowCompletions(false))
      return null;
    if (fIsPrefixCompletionEnabled)
      return fProposalPopup.incrementalComplete();
    return fProposalPopup.showProposals(false);
  }

  /*
   * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension#completePrefix()
   * @since 3.0
   */
  public String completePrefix() {
    if (!prepareToShowCompletions(false))
      return null;
    return fProposalPopup.incrementalComplete();
  }

  /**
   * Prepares to show content assist proposals. It returns false if auto activation has kicked in
   * recently.
   *
   * @param isAutoActivated  whether completion was triggered by auto activation
   * @return <code>true</code> if the caller should continue and show the proposals,
   *         <code>false</code> otherwise.
   * @since 3.2
   */
  private boolean prepareToShowCompletions(boolean isAutoActivated) {
    long current= System.currentTimeMillis();
    int gracePeriod= Math.max(fAutoActivationDelay, 200);
    if (current < fLastAutoActivation + gracePeriod)
      return false;
   
      promoteKeyListener();
    fireSessionBeginEvent(isAutoActivated);
    return true;
    }

  /**
   * Callback to signal this content assistant that the presentation of the possible completions
   * has been stopped.
   *
   * @since 2.1
   */
  protected void possibleCompletionsClosed() {
    fLastAutoActivation= Long.MIN_VALUE;
    storeCompletionProposalPopupSize();
  }

  /*
   * @see IContentAssist#showContextInformation
   */
  public String showContextInformation() {
    promoteKeyListener();
    if (fContextInfoPopup != null)
      return fContextInfoPopup.showContextProposals(false);
    return null;
  }

  /**
   * Callback to signal this content assistant that the presentation of the context information
   * has been stopped.
   *
   * @since 2.1
   */
  protected void contextInformationClosed() {
  }

  /**
   * Requests that the specified context information to be shown.
   *
   * @param contextInformation the context information to be shown
   * @param offset the offset to which the context information refers to
   * @since 2.0
   */
  void showContextInformation(IContextInformation contextInformation, int offset) {
    if (fContextInfoPopup != null)
      fContextInfoPopup.showContextInformation(contextInformation, offset);
  }

  /**
   * Returns the current content assist error message.
   *
   * @return an error message or <code>null</code> if no error has occurred
   */
  String getErrorMessage() {
    return fLastErrorMessage;
  }

  /**
   * Returns the content assist processor for the content type of the specified document position.
   *
   * @param viewer the text viewer
   * @param offset a offset within the document
   * @return a content-assist processor or <code>null</code> if none exists
   * @since 3.0
   */
  private IContentAssistProcessor getProcessor(ITextViewer viewer, int offset) {
    try {

      IDocument document= viewer.getDocument();
      String type= TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true);

      return getContentAssistProcessor(type);

    } catch (BadLocationException x) {
    }

    return null;
  }

  /**
   * Returns the content assist processor for the content type of the specified document position.
   *
   * @param contentAssistSubjectControl the content assist subject control
   * @param offset a offset within the document
   * @return a content-assist processor or <code>null</code> if none exists
   * @since 3.0
   */
  private IContentAssistProcessor getProcessor(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
    try {

      IDocument document= contentAssistSubjectControl.getDocument();
      String type;
      if (document != null)
        type= TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true);
      else
        type= IDocument.DEFAULT_CONTENT_TYPE;

      return getContentAssistProcessor(type);

    } catch (BadLocationException x) {
    }

    return null;
  }

  /**
   * Returns an array of completion proposals computed based on the specified document position.
   * The position is used to determine the appropriate content assist processor to invoke.
   *
   * @param contentAssistSubjectControl the content assist subject control
   * @param offset a document offset
   * @return an array of completion proposals
   * @see IContentAssistProcessor#computeCompletionProposals(ITextViewer, int)
   * @since 3.0
   */
  ICompletionProposal[] computeCompletionProposals(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
    fLastErrorMessage= null;

    ICompletionProposal[] result= null;

    IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
    if (p instanceof ISubjectControlContentAssistProcessor) {
      result= ((ISubjectControlContentAssistProcessor) p).computeCompletionProposals(contentAssistSubjectControl, offset);
      fLastErrorMessage= p.getErrorMessage();
    }

    return result;
  }

  /**
   * Returns an array of completion proposals computed based on the specified document position.
   * The position is used to determine the appropriate content assist processor to invoke.
   *
   * @param viewer the viewer for which to compute the proposals
   * @param offset a document offset
   * @return an array of completion proposals or <code>null</code> if no proposals are possible
   * @see IContentAssistProcessor#computeCompletionProposals(ITextViewer, int)
   */
  ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
    fLastErrorMessage= null;

    ICompletionProposal[] result= null;

    IContentAssistProcessor p= getProcessor(viewer, offset);
    if (p != null) {
      result= p.computeCompletionProposals(viewer, offset);
      fLastErrorMessage= p.getErrorMessage();
    }

    return result;
  }

  /**
   * Returns an array of context information objects computed based on the specified document
   * position. The position is used to determine the appropriate content assist processor to
   * invoke.
   *
   * @param viewer the viewer for which to compute the context information
   * @param offset a document offset
   * @return an array of context information objects
   * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
   */
  IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
    fLastErrorMessage= null;

    IContextInformation[] result= null;

    IContentAssistProcessor p= getProcessor(viewer, offset);
    if (p != null) {
      result= p.computeContextInformation(viewer, offset);
      fLastErrorMessage= p.getErrorMessage();
    }

    return result;
  }

  /**
   * Returns an array of context information objects computed based on the specified document
   * position. The position is used to determine the appropriate content assist processor to
   * invoke.
   *
   * @param contentAssistSubjectControl the content assist subject control
   * @param offset a document offset
   * @return an array of context information objects
   * @see IContentAssistProcessor#computeContextInformation(ITextViewer, int)
   * @since 3.0
   */
  IContextInformation[] computeContextInformation(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
    fLastErrorMessage= null;

    IContextInformation[] result= null;

    IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
    if (p instanceof ISubjectControlContentAssistProcessor) {
      result= ((ISubjectControlContentAssistProcessor) p).computeContextInformation(contentAssistSubjectControl, offset);
      fLastErrorMessage= p.getErrorMessage();
    }

    return result;
  }

  /**
   * Returns the context information validator that should be used to determine when the currently
   * displayed context information should be dismissed. The position is used to determine the
   * appropriate content assist processor to invoke.
   *
   * @param viewer the text viewer
   * @param offset a document offset
   * @return an validator
   * @see IContentAssistProcessor#getContextInformationValidator()
   * @since 3.0
   */
  IContextInformationValidator getContextInformationValidator(ITextViewer viewer, int offset) {
    IContentAssistProcessor p= getProcessor(viewer, offset);
    return p != null ? p.getContextInformationValidator() : null;
  }

  /**
   * Returns the context information validator that should be used to determine when the currently
   * displayed context information should be dismissed. The position is used to determine the
   * appropriate content assist processor to invoke.
   *
   * @param contentAssistSubjectControl the content assist subject control
   * @param offset a document offset
   * @return an validator
   * @see IContentAssistProcessor#getContextInformationValidator()
   * @since 3.0
   */
  IContextInformationValidator getContextInformationValidator(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
    IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
    return p != null ? p.getContextInformationValidator() : null;
  }

  /**
   * Returns the context information presenter that should be used to display context information.
   * The position is used to determine the appropriate content assist processor to invoke.
   *
   * @param viewer the text viewer
   * @param offset a document offset
   * @return a presenter
   * @since 2.0
   */
  IContextInformationPresenter getContextInformationPresenter(ITextViewer viewer, int offset) {
    IContextInformationValidator validator= getContextInformationValidator(viewer, offset);
    if (validator instanceof IContextInformationPresenter)
      return (IContextInformationPresenter) validator;
    return null;
  }

  /**
   * Returns the context information presenter that should be used to display context information.
   * The position is used to determine the appropriate content assist processor to invoke.
   *
   * @param contentAssistSubjectControl the content assist subject control
   * @param offset a document offset
   * @return a presenter
   * @since 3.0
   */
  IContextInformationPresenter getContextInformationPresenter(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
    IContextInformationValidator validator= getContextInformationValidator(contentAssistSubjectControl, offset);
    if (validator instanceof IContextInformationPresenter)
      return (IContextInformationPresenter) validator;
    return null;
  }

  /**
   * Returns the characters which when typed by the user should automatically initiate proposing
   * completions. The position is used to determine the appropriate content assist processor to
   * invoke.
   *
   * @param contentAssistSubjectControl the content assist subject control
   * @param offset a document offset
   * @return the auto activation characters
   * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
   * @since 3.0
   */
  char[] getCompletionProposalAutoActivationCharacters(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
    IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
    return p != null ? p.getCompletionProposalAutoActivationCharacters() : null;
  }

  /**
   * Returns the characters which when typed by the user should automatically initiate proposing
   * completions. The position is used to determine the appropriate content assist processor to
   * invoke.
   *
   * @param viewer the text viewer
   * @param offset a document offset
   * @return the auto activation characters
   * @see IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
   */
  char[] getCompletionProposalAutoActivationCharacters(ITextViewer viewer, int offset) {
    IContentAssistProcessor p= getProcessor(viewer, offset);
    return p != null ? p.getCompletionProposalAutoActivationCharacters() : null;
  }

  /**
   * Returns the characters which when typed by the user should automatically initiate the
   * presentation of context information. The position is used to determine the appropriate
   * content assist processor to invoke.
   *
   * @param viewer the text viewer
   * @param offset a document offset
   * @return the auto activation characters
   * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters()
   * @since 3.0
   */
  char[] getContextInformationAutoActivationCharacters(ITextViewer viewer, int offset) {
    IContentAssistProcessor p= getProcessor(viewer, offset);
    return p != null ? p.getContextInformationAutoActivationCharacters() : null;
  }

  /**
   * Returns the characters which when typed by the user should automatically initiate the
   * presentation of context information. The position is used to determine the appropriate
   * content assist processor to invoke.
   *
   * @param contentAssistSubjectControl the content assist subject control
   * @param offset a document offset
   * @return the auto activation characters
   * @see IContentAssistProcessor#getContextInformationAutoActivationCharacters()
   * @since 3.0
   */
  char[] getContextInformationAutoActivationCharacters(IContentAssistSubjectControl contentAssistSubjectControl, int offset) {
    IContentAssistProcessor p= getProcessor(contentAssistSubjectControl, offset);
    return p != null ? p.getContextInformationAutoActivationCharacters() : null;
  }

  /*
   * @see org.eclipse.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
   * @since 2.0
   */
  public boolean requestWidgetToken(IWidgetTokenOwner owner) {
    return false;
  }

  /*
   * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenOwner,
   *      int)
   * @since 3.0
   */
  public boolean requestWidgetToken(IWidgetTokenOwner owner, int priority) {
    if (priority > WIDGET_PRIORITY) {
      hide();
      return true;
    }
    return false;
  }

  /*
   * @see org.eclipse.jface.text.IWidgetTokenKeeperExtension#setFocus(org.eclipse.jface.text.IWidgetTokenOwner)
   * @since 3.0
   */
  public boolean setFocus(IWidgetTokenOwner owner) {
    if (fProposalPopup != null) {
      fProposalPopup.setFocus();
      return fProposalPopup.hasFocus();
    }
    return false;
  }

  /**
   * Hides any open pop-ups.
   *
   * @since 3.0
   */
  protected void hide() {
    if (fProposalPopup != null)
      fProposalPopup.hide();

    if (fContextInfoPopup != null)
      fContextInfoPopup.hide();
  }

  // ------ control's size handling dialog settings ------

  /**
   * Tells this information control manager to open the information control with the values
   * contained in the given dialog settings and to store the control's last valid size in the
   * given dialog settings.
   * <p>
   * Note: This API is only valid if the information control implements
   * {@link org.eclipse.jface.text.IInformationControlExtension3}. Not following this restriction
   * will later result in an {@link UnsupportedOperationException}.
   * </p>
   * <p>
   * The constants used to store the values are:
   * <ul>
   * <li>{@link ContentAssistant#STORE_SIZE_X}</li>
   * <li>{@link ContentAssistant#STORE_SIZE_Y}</li>
   * </ul>
   * </p>
   *
   * @param dialogSettings
   * @since 3.0
   */
  public void setRestoreCompletionProposalSize(IDialogSettings dialogSettings) {
    Assert.isTrue(dialogSettings != null);
    fDialogSettings= dialogSettings;
  }

  /**
   * Stores the content assist pop-up's size.
   */
  protected void storeCompletionProposalPopupSize() {
    if (fDialogSettings == null || fProposalPopup == null)
      return;

    Point size= fProposalPopup.getSize();
    if (size == null)
      return;

    fDialogSettings.put(STORE_SIZE_X, size.x);
    fDialogSettings.put(STORE_SIZE_Y, size.y);
  }

  /**
   * Restores the content assist pop-up's size.
   *
   * @return the stored size
   * @since 3.0
   */
  protected Point restoreCompletionProposalPopupSize() {
    if (fDialogSettings == null)
      return null;

    Point size= new Point(-1, -1);

    try {
      size.x= fDialogSettings.getInt(STORE_SIZE_X);
      size.y= fDialogSettings.getInt(STORE_SIZE_Y);
    } catch (NumberFormatException ex) {
      size.x= -1;
      size.y= -1;
    }

    // sanity check
    if (size.x == -1 && size.y == -1)
      return null;

    Rectangle maxBounds= null;
    if (fContentAssistSubjectControl != null && Helper.okToUse(fContentAssistSubjectControl.getControl()))
      maxBounds= fContentAssistSubjectControl.getControl().getDisplay().getBounds();
    else {
      // fallback
      Display display= Display.getCurrent();
      if (display == null)
        display= Display.getDefault();
      if (display != null && !display.isDisposed())
        maxBounds= display.getBounds();
    }

    if (size.x > -1 && size.y > -1) {
      if (maxBounds != null) {
        size.x= Math.min(size.x, maxBounds.width);
        size.y= Math.min(size.y, maxBounds.height);
      }

      // Enforce an absolute minimal size
      size.x= Math.max(size.x, 30);
      size.y= Math.max(size.y, 30);
    }

    return size;
  }

  /**
   * Sets the prefix completion property. If enabled, content assist delegates completion to
   * prefix completion.
   *
   * @param enabled <code>true</code> to enable prefix completion, <code>false</code> to
   *        disable
   */
  public void enablePrefixCompletion(boolean enabled) {
    fIsPrefixCompletionEnabled= enabled;
  }
 
  /**
   * Returns the prefix completion state.
   *
     * @return <code>true</code> if prefix completion is enabled, <code>false</code> otherwise
     * @since 3.2
     */
    boolean isPrefixCompletionEnabled() {
      return fIsPrefixCompletionEnabled;
    }

  /**
   * Returns whether the content assistant proposal popup has the focus.
   *
   * @return <code>true</code> if the proposal popup has the focus
   * @since 3.0
   */
  public boolean hasProposalPopupFocus() {
    return fProposalPopup.hasFocus();
  }

  /*
   * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#addCompletionListener(org.eclipse.jface.text.contentassist.ICompletionListener)
   * @since 3.2
   */
  public void addCompletionListener(ICompletionListener listener) {
    Assert.isLegal(listener != null);
    fCompletionListeners.add(listener);
  }

  /*
   * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#removeCompletionListener(org.eclipse.jface.text.contentassist.ICompletionListener)
   * @since 3.2
   */
  public void removeCompletionListener(ICompletionListener listener) {
    fCompletionListeners.remove(listener);
  }

  /**
   * Fires a session begin event to all registered {@link ICompletionListener}s.
   *
   * @param isAutoActivated  <code>true</code> if this session was triggered by auto activation
   * @since 3.2
   */
  void fireSessionBeginEvent(boolean isAutoActivated) {
    if (fContentAssistSubjectControlAdapter != null && !isProposalPopupActive()) {
      IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x);
      ContentAssistEvent event= new ContentAssistEvent(this, processor, isAutoActivated);
      Object[] listeners= fCompletionListeners.getListeners();
      for (int i= 0; i < listeners.length; i++) {
        ICompletionListener listener= (ICompletionListener)listeners[i];
        listener.assistSessionStarted(event);
      }
    }
  }
 
  /**
   * Fires a session restart event to all registered {@link ICompletionListener}s.
   *
   * @since 3.4
   */
  void fireSessionRestartEvent() {
    if (fContentAssistSubjectControlAdapter != null) {
      IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x);
      ContentAssistEvent event= new ContentAssistEvent(this, processor);
      Object[] listeners= fCompletionListeners.getListeners();
      for (int i= 0; i < listeners.length; i++) {
        ICompletionListener listener= (ICompletionListener)listeners[i];
        if (listener instanceof ICompletionListenerExtension)
          ((ICompletionListenerExtension)listener).assistSessionRestarted(event);
      }
    }
  }

  /**
   * Fires a session end event to all registered {@link ICompletionListener}s.
   *
   * @since 3.2
   */
  void fireSessionEndEvent() {
    if (fContentAssistSubjectControlAdapter != null) {
      IContentAssistProcessor processor= getProcessor(fContentAssistSubjectControlAdapter, fContentAssistSubjectControlAdapter.getSelectedRange().x);
      ContentAssistEvent event= new ContentAssistEvent(this, processor);
      Object[] listeners= fCompletionListeners.getListeners();
      for (int i= 0; i < listeners.length; i++) {
        ICompletionListener listener= (ICompletionListener)listeners[i];
        listener.assistSessionEnded(event);
      }
    }
  }

  /*
   * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setRepeatedInvocationMode(boolean)
   * @since 3.2
   */
  public void setRepeatedInvocationMode(boolean cycling) {
    fIsRepetitionMode= cycling;
  }

  /**
   * Returns <code>true</code> if repeated invocation mode is enabled, <code>false</code>
   * otherwise.
   *
   * @return <code>true</code> if repeated invocation mode is enabled, <code>false</code>
   *         otherwise
   * @since 3.2
   */
  boolean isRepeatedInvocationMode() {
    return fIsRepetitionMode;
  }

  /*
   * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setShowEmptyList(boolean)
   * @since 3.2
   */
  public void setShowEmptyList(boolean showEmpty) {
    fShowEmptyList= showEmpty;
  }

  /**
   * Returns <code>true</code> if empty lists should be displayed, <code>false</code>
   * otherwise.
   *
   * @return <code>true</code> if empty lists should be displayed, <code>false</code>
   *         otherwise
   * @since 3.2
   */
  boolean isShowEmptyList() {
    return fShowEmptyList;
  }

  /*
   * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setStatusLineVisible(boolean)
   * @since 3.2
   */
  public void setStatusLineVisible(boolean show) {
    fIsStatusLineVisible= show;
    if (fProposalPopup != null)
      fProposalPopup.setStatusLineVisible(show);
  }

  /**
   * Returns <code>true</code> if a message line should be displayed, <code>false</code>
   * otherwise.
   *
   * @return <code>true</code> if a message line should be displayed, <code>false</code>
   *         otherwise
   * @since 3.2
   */
  boolean isStatusLineVisible() {
    return fIsStatusLineVisible;
  }

  /*
   * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setStatusMessage(java.lang.String)
   * @since 3.2
   */
  public void setStatusMessage(String message) {
    Assert.isLegal(message != null);
    fMessage= message;
    if (fProposalPopup != null)
      fProposalPopup.setMessage(message);
  }

  /**
   * Returns the affordance caption for the cycling affordance at the bottom of the pop-up.
   *
   * @return the affordance caption for the cycling affordance at the bottom of the pop-up
   * @since 3.2
   */
  String getStatusMessage() {
    return fMessage;
  }

  /*
   * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2#setEmptyMessage(java.lang.String)
   * @since 3.2
   */
  public void setEmptyMessage(String message) {
    Assert.isLegal(message != null);
    if (fProposalPopup != null)
      fProposalPopup.setEmptyMessage(message);
  }

  /**
   * Fires a selection event, see {@link ICompletionListener}.
   *
   * @param proposal the selected proposal, possibly <code>null</code>
   * @param smartToggle true if the smart toggle is on
   * @since 3.2
   */
  void fireSelectionEvent(ICompletionProposal proposal, boolean smartToggle) {
    Object[] listeners= fCompletionListeners.getListeners();
    for (int i= 0; i < listeners.length; i++) {
      ICompletionListener listener= (ICompletionListener)listeners[i];
      listener.selectionChanged(proposal, smartToggle);
    }
  }

  /*
   * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension3#setInvocationTrigger(org.eclipse.jface.bindings.keys.KeySequence)
   * @since 3.2
   */
  public void setRepeatedInvocationTrigger(KeySequence sequence) {
    fRepeatedInvocationKeySequence= sequence;
  }

  /**
   * Returns the repeated invocation key sequence.
   *
   * @return the repeated invocation key sequence or <code>null</code>, if none
   * @since 3.2
   */
  KeySequence getRepeatedInvocationKeySequence() {
    return fRepeatedInvocationKeySequence;
  }
 
  /**
   * Returns whether proposal popup is active.
   *
   * @return <code>true</code> if the proposal popup is active, <code>false</code> otherwise
   * @since 3.4
   */
  protected boolean isProposalPopupActive(){
    return fProposalPopup != null && fProposalPopup.isActive();
  }

  /**
   * Returns whether the context information popup is active.
   *
   * @return <code>true</code> if the context information popup is active, <code>false</code> otherwise
   * @since 3.4
   */
  protected boolean isContextInfoPopupActive(){
    return fContextInfoPopup != null && fContextInfoPopup.isActive();
  }

  /**
   * {@inheritDoc}
   *
   * @since 3.4
   */
  public final IHandler getHandler(String commandId) {
    if (fHandlers == null)
      throw new IllegalStateException();

    IHandler handler= (IHandler)fHandlers.get(commandId);
    if (handler != null)
      return handler;
   
    Assert.isLegal(false);
    return null;
  }

  /**
   * Registers the given handler under the given command identifier.
   *
   * @param commandId the command identifier
   * @param handler the handler
   * @since 3.4
   */
  protected final void registerHandler(String commandId, IHandler handler) {
    if (fHandlers == null)
      fHandlers= new HashMap(2);
    fHandlers.put(commandId, handler);
  }

  /**
   * Tells whether the support for colored labels is enabled.
   *
   * @return <code>true</code> if the support for colored labels is enabled, <code>false</code> otherwise
   * @since 3.4
   */
  boolean isColoredLabelsSupportEnabled() {
      return fIsColoredLabelsSupportEnabled;
  }

  /**
   * Enables the support for colored labels in the proposal popup.
   * <p>Completion proposals can implement {@link ICompletionProposalExtension6}
   * to provide colored proposal labels.</p>
   *
   * @param isEnabled if <code>true</code> the support for colored labels is enabled in the proposal popup
   * @since 3.4
   */
  public void enableColoredLabels(boolean isEnabled) {
    fIsColoredLabelsSupportEnabled= isEnabled;
  }

}
TOP

Related Classes of org.eclipse.jface.text.contentassist.ContentAssistant$InternalListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.