diff dwtx/jface/text/contentassist/ContentAssistant.d @ 129:eb30df5ca28b

Added JFace Text sources
author Frank Benoit <benoit@tionex.de>
date Sat, 23 Aug 2008 19:10:48 +0200
parents
children c4fb132a086c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/text/contentassist/ContentAssistant.d	Sat Aug 23 19:10:48 2008 +0200
@@ -0,0 +1,2456 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.text.contentassist.ContentAssistant;
+
+import dwt.dwthelper.utils;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import dwt.DWT;
+import dwt.DWTError;
+import dwt.custom.VerifyKeyListener;
+import dwt.events.ControlEvent;
+import dwt.events.ControlListener;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.FocusEvent;
+import dwt.events.FocusListener;
+import dwt.events.KeyAdapter;
+import dwt.events.KeyEvent;
+import dwt.events.KeyListener;
+import dwt.events.MouseEvent;
+import dwt.events.MouseListener;
+import dwt.events.TraverseEvent;
+import dwt.events.TraverseListener;
+import dwt.events.VerifyEvent;
+import dwt.graphics.Color;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Control;
+import dwt.widgets.Display;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.widgets.Monitor;
+import dwt.widgets.Shell;
+import dwt.widgets.Widget;
+import dwtx.core.commands.IHandler;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.ListenerList;
+import dwtx.jface.bindings.keys.KeySequence;
+import dwtx.jface.contentassist.IContentAssistSubjectControl;
+import dwtx.jface.contentassist.ISubjectControlContentAssistProcessor;
+import dwtx.jface.dialogs.IDialogSettings;
+import dwtx.jface.preference.JFacePreferences;
+import dwtx.jface.text.BadLocationException;
+import dwtx.jface.text.IDocument;
+import dwtx.jface.text.IDocumentExtension3;
+import dwtx.jface.text.IEventConsumer;
+import dwtx.jface.text.IInformationControlCreator;
+import dwtx.jface.text.ITextViewer;
+import dwtx.jface.text.IViewportListener;
+import dwtx.jface.text.IWidgetTokenKeeper;
+import dwtx.jface.text.IWidgetTokenKeeperExtension;
+import dwtx.jface.text.IWidgetTokenOwner;
+import dwtx.jface.text.IWidgetTokenOwnerExtension;
+import dwtx.jface.text.TextUtilities;
+import dwtx.jface.util.Geometry;
+
+
+/**
+ * The standard implementation of the <code>IContentAssistant</code> interface. Usually, clients
+ * instantiate this class and configure it before using it.
+ */
+public class ContentAssistant : 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= "dwtx.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= "dwtx.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 : 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 !is 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 !is 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 !is null) {
+                    d.asyncExec(new Runnable() {
+                        public void run() {
+                            if (!fProposalPopup.hasFocus() && (fContextInfoPopup is 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 : KeyAdapter , KeyListener, Runnable, VerifyKeyListener {
+
+        private Thread fThread;
+        private bool 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 !is 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 !is null && threadToStop.isAlive())
+                threadToStop.interrupt();
+        }
+
+        private bool contains(char[] characters, char character) {
+            if (characters !is null) {
+                for (int i= 0; i < characters.length; i++) {
+                    if (character is characters[i])
+                        return true;
+                }
+            }
+            return false;
+        }
+
+        public void keyPressed(KeyEvent e) {
+            // Only act on typed characters and ignore modifier-only events
+            if (e.character is 0 && (e.keyCode & DWT.KEYCODE_BIT) is 0)
+                return;
+            
+            if (e.character !is 0 && (e.stateMask is DWT.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 !is null && fThread.isAlive())
+                reset(showStyle);
+            else
+                start(showStyle);
+        }
+
+        /*
+         * @see dwt.custom.VerifyKeyListener#verifyKey(dwt.events.VerifyEvent)
+         */
+        public void verifyKey(VerifyEvent event) {
+            keyPressed(event);
+        }
+
+        protected void showAssist(final int showStyle) {
+            final Control control= fContentAssistSubjectControlAdapter.getControl();
+            if (control is null)
+                return;
+            
+            final Display d= control.getDisplay();
+            if (d is null)
+                return;
+            
+            try {
+                d.syncExec(new Runnable() {
+                    public void run() {
+                        if (isProposalPopupActive())
+                            return;
+                        
+                        if (control.isDisposed() || !control.isFocusControl())
+                            return;
+                        
+                        if (showStyle is SHOW_PROPOSALS) {
+                            if (!prepareToShowCompletions(true))
+                                return;
+                            fProposalPopup.showProposals(true);
+                            fLastAutoActivation= System.currentTimeMillis();
+                        } else if (showStyle is SHOW_CONTEXT_INFO && fContextInfoPopup !is null) {
+                            promoteKeyListener();
+                            fContextInfoPopup.showContextProposals(true);
+                        }
+                    }
+                });
+            } catch (DWTError e) {
+            }
+        }
+    }
+
+    /**
+     * The layout manager layouts the various windows associated with the content assistant based on
+     * the settings of the content assistant.
+     */
+    class LayoutManager : 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 !is null && !shell.isDisposed());
+            checkType(type);
+
+            if (fShells[type] !is shell) {
+                if (fShells[type] !is null)
+                    fShells[type].removeListener(DWT.Dispose, this);
+                shell.addListener(DWT.Dispose, this);
+                fShells[type]= shell;
+            }
+
+            fPopups[type]= popup;
+            if (type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP)
+                fContextType= type;
+
+            layout(type, offset);
+            adjustListeners(type);
+        }
+
+        protected void checkType(int type) {
+            Assert.isTrue(type is LAYOUT_PROPOSAL_SELECTOR ||
+                type is LAYOUT_CONTEXT_SELECTOR || type is LAYOUT_CONTEXT_INFO_POPUP);
+        }
+
+        public void handleEvent(Event event) {
+            Widget source= event.widget;
+            source.removeListener(DWT.Dispose, this);
+
+            int type= getShellType(source);
+            checkType(type);
+            fShells[type]= null;
+
+            switch (type) {
+                case LAYOUT_PROPOSAL_SELECTOR:
+                    if (fContextType is 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 is 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 is 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] is 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 is LAYOUT_CONTEXT_INFO_POPUP &&
+                    fContextInfoPopupOrientation is 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 !is 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 is null ? Integer.MAX_VALUE : popup.getMinimalHeight();
+            int spaceAbove= caret.y - bounds.y;
+            int spaceBelow= bounds.y + bounds.height - (caret.y + caret.height);
+            Rectangle rect;
+            bool 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 !is 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 is 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 dwtx.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 : 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(dwt.events.VerifyEvent)
+         */
+        public void verifyKey(VerifyEvent e) {
+            IContentAssistListener[] listeners= (IContentAssistListener[]) fListeners.clone();
+            for (int i= 0; i < listeners.length; i++) {
+                if (listeners[i] !is null) {
+                    if (!listeners[i].verifyKey(e) || !e.doit)
+                        break;
+                }
+            }
+            if (fAutoAssistListener !is 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] !is 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 bool fIsAutoActivated= false;
+    private bool 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 bool 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 bool 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 bool fIsRepetitionMode= false;
+    /**
+     * The show empty property.
+     *
+     * @since 3.2
+     */
+    private bool fShowEmptyList= false;
+    /**
+     * The message line property.
+     *
+     * @since 3.2
+     */
+    private bool 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 bool 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 dwtx.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 is null)
+            fProcessors= new HashMap();
+
+        if (processor is null)
+            fProcessors.remove(contentType);
+        else
+            fProcessors.put(contentType, processor);
+    }
+
+    /*
+     * @see IContentAssistant#getContentAssistProcessor
+     */
+    public IContentAssistProcessor getContentAssistProcessor(String contentType) {
+        if (fProcessors is 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 is 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 !is null)
+                buf.append(triggers);
+            triggers= processor.getContextInformationAutoActivationCharacters();
+            if (triggers !is 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(bool 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(bool 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
+     */
+    bool 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(bool start) {
+        if (start) {
+
+            if ((fContentAssistSubjectControlAdapter !is null) && fAutoAssistListener is 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 !is 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 !is null) {
+            int delay= fAutoActivationDelay;
+            if (delay is 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 is DWT.TRAVERSE_ESCAPE && isProposalPopupActive())
+                        e.doit= false;
+                }
+            };
+            fContentAssistSubjectControlShell.addTraverseListener(fCASCSTraverseListener);
+        }
+
+        manageAutoActivation(fIsAutoActivated);
+    }
+
+    /*
+     * @see IContentAssist#uninstall
+     */
+    public void uninstall() {
+        hide();
+        manageAutoActivation(false);
+        
+        if (fHandlers !is null) {
+            fHandlers.clear();
+            fHandlers= null;
+        }
+
+        if (fCloser !is 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 bool 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
+     */
+    bool addContentAssistListener(IContentAssistListener listener, int type) {
+
+        if (acquireWidgetToken(type)) {
+
+            fListeners[type]= listener;
+
+            if (fCloser is null && getNumberOfListeners() is 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] is null && fListeners[PROPOSAL_SELECTOR] is null) {
+            IWidgetTokenOwner owner= null;
+            if (fContentAssistSubjectControl instanceof IWidgetTokenOwner)
+                owner= (IWidgetTokenOwner) fContentAssistSubjectControl;
+            else if (fViewer instanceof IWidgetTokenOwner)
+                owner= (IWidgetTokenOwner) fViewer;
+            if (owner !is 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() is 0) {
+
+            if (fCloser !is 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] !is 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 dwtx.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 bool prepareToShowCompletions(bool 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 !is 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 !is 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 !is 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 !is 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 !is 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 !is 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 !is 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 !is 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 !is 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 !is 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 !is null ? p.getContextInformationAutoActivationCharacters() : null;
+    }
+
+    /*
+     * @see dwtx.jface.text.IWidgetTokenKeeper#requestWidgetToken(IWidgetTokenOwner)
+     * @since 2.0
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner) {
+        return false;
+    }
+
+    /*
+     * @see dwtx.jface.text.IWidgetTokenKeeperExtension#requestWidgetToken(dwtx.jface.text.IWidgetTokenOwner,
+     *      int)
+     * @since 3.0
+     */
+    public bool requestWidgetToken(IWidgetTokenOwner owner, int priority) {
+        if (priority > WIDGET_PRIORITY) {
+            hide();
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * @see dwtx.jface.text.IWidgetTokenKeeperExtension#setFocus(dwtx.jface.text.IWidgetTokenOwner)
+     * @since 3.0
+     */
+    public bool setFocus(IWidgetTokenOwner owner) {
+        if (fProposalPopup !is null) {
+            fProposalPopup.setFocus();
+            return fProposalPopup.hasFocus();
+        }
+        return false;
+    }
+
+    /**
+     * Hides any open pop-ups.
+     *
+     * @since 3.0
+     */
+    protected void hide() {
+        if (fProposalPopup !is null)
+            fProposalPopup.hide();
+
+        if (fContextInfoPopup !is 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 dwtx.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 !is null);
+        fDialogSettings= dialogSettings;
+    }
+
+    /**
+     * Stores the content assist pop-up's size.
+     */
+    protected void storeCompletionProposalPopupSize() {
+        if (fDialogSettings is null || fProposalPopup is null)
+            return;
+
+        Point size= fProposalPopup.getSize();
+        if (size is 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 is 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 is -1 && size.y is -1)
+            return null;
+
+        Rectangle maxBounds= null;
+        if (fContentAssistSubjectControl !is null && Helper.okToUse(fContentAssistSubjectControl.getControl()))
+            maxBounds= fContentAssistSubjectControl.getControl().getDisplay().getBounds();
+        else {
+            // fallback
+            Display display= Display.getCurrent();
+            if (display is null)
+                display= Display.getDefault();
+            if (display !is null && !display.isDisposed())
+                maxBounds= display.getBounds();
+        }
+
+        if (size.x > -1 && size.y > -1) {
+            if (maxBounds !is 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(bool enabled) {
+        fIsPrefixCompletionEnabled= enabled;
+    }
+    
+    /**
+     * Returns the prefix completion state.
+     * 
+     * @return <code>true</code> if prefix completion is enabled, <code>false</code> otherwise
+     * @since 3.2
+     */
+    bool 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 bool hasProposalPopupFocus() {
+        return fProposalPopup.hasFocus();
+    }
+
+    /*
+     * @see dwtx.jface.text.contentassist.IContentAssistantExtension2#addCompletionListener(dwtx.jface.text.contentassist.ICompletionListener)
+     * @since 3.2
+     */
+    public void addCompletionListener(ICompletionListener listener) {
+        Assert.isLegal(listener !is null);
+        fCompletionListeners.add(listener);
+    }
+
+    /*
+     * @see dwtx.jface.text.contentassist.IContentAssistantExtension2#removeCompletionListener(dwtx.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(bool isAutoActivated) {
+        if (fContentAssistSubjectControlAdapter !is 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 !is 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 !is 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 dwtx.jface.text.contentassist.IContentAssistantExtension2#setRepeatedInvocationMode(bool)
+     * @since 3.2
+     */
+    public void setRepeatedInvocationMode(bool 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
+     */
+    bool isRepeatedInvocationMode() {
+        return fIsRepetitionMode;
+    }
+
+    /*
+     * @see dwtx.jface.text.contentassist.IContentAssistantExtension2#setShowEmptyList(bool)
+     * @since 3.2
+     */
+    public void setShowEmptyList(bool 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
+     */
+    bool isShowEmptyList() {
+        return fShowEmptyList;
+    }
+
+    /*
+     * @see dwtx.jface.text.contentassist.IContentAssistantExtension2#setStatusLineVisible(bool)
+     * @since 3.2
+     */
+    public void setStatusLineVisible(bool show) {
+        fIsStatusLineVisible= show;
+        if (fProposalPopup !is 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
+     */
+    bool isStatusLineVisible() {
+        return fIsStatusLineVisible;
+    }
+
+    /*
+     * @see dwtx.jface.text.contentassist.IContentAssistantExtension2#setStatusMessage(java.lang.String)
+     * @since 3.2
+     */
+    public void setStatusMessage(String message) {
+        Assert.isLegal(message !is null);
+        fMessage= message;
+        if (fProposalPopup !is 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 dwtx.jface.text.contentassist.IContentAssistantExtension2#setEmptyMessage(java.lang.String)
+     * @since 3.2
+     */
+    public void setEmptyMessage(String message) {
+        Assert.isLegal(message !is null);
+        if (fProposalPopup !is 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, bool smartToggle) {
+        Object[] listeners= fCompletionListeners.getListeners();
+        for (int i= 0; i < listeners.length; i++) {
+            ICompletionListener listener= (ICompletionListener)listeners[i];
+            listener.selectionChanged(proposal, smartToggle);
+        }
+    }
+
+    /*
+     * @see dwtx.jface.text.contentassist.IContentAssistantExtension3#setInvocationTrigger(dwtx.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 bool isProposalPopupActive(){
+        return fProposalPopup !is 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 bool isContextInfoPopupActive(){
+        return fContextInfoPopup !is null && fContextInfoPopup.isActive();
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @since 3.4
+     */
+    public final IHandler getHandler(String commandId) {
+        if (fHandlers is null)
+            throw new IllegalStateException();
+
+        IHandler handler= (IHandler)fHandlers.get(commandId);
+        if (handler !is 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 is 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
+     */
+    bool 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(bool isEnabled) {
+        fIsColoredLabelsSupportEnabled= isEnabled;
+    }
+
+}