diff dwtx/jface/text/contentassist/ContextInformationPopup.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/ContextInformationPopup.d	Sat Aug 23 19:10:48 2008 +0200
@@ -0,0 +1,847 @@
+/*******************************************************************************
+ * 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.text.contentassist.ContextInformationPopup;
+
+import dwt.dwthelper.utils;
+
+import java.util.Iterator;
+import java.util.Stack;
+
+import dwt.DWT;
+import dwt.custom.BusyIndicator;
+import dwt.custom.StyledText;
+import dwt.events.KeyEvent;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.events.VerifyEvent;
+import dwt.graphics.Color;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.layout.GridData;
+import dwt.layout.GridLayout;
+import dwt.widgets.Control;
+import dwt.widgets.Display;
+import dwt.widgets.Shell;
+import dwt.widgets.Table;
+import dwt.widgets.TableItem;
+import dwtx.jface.contentassist.IContentAssistSubjectControl;
+import dwtx.jface.text.ITextViewer;
+import dwtx.jface.text.TextPresentation;
+
+
+/**
+ * This class is used to present context information to the user.
+ * If multiple contexts are valid at the current cursor location,
+ * a list is presented from which the user may choose one context.
+ * Once the user makes their choice, or if there was only a single
+ * possible context, the context information is shown in a tool tip like popup. <p>
+ * If the tool tip is visible and the user wants to see context information of
+ * a context embedded into the one for which context information is displayed,
+ * context information for the embedded context is shown. As soon as the
+ * cursor leaves the embedded context area, the context information for
+ * the embedding context is shown again.
+ *
+ * @see IContextInformation
+ * @see IContextInformationValidator
+ */
+class ContextInformationPopup : IContentAssistListener {
+
+
+    /**
+     * Represents the state necessary for embedding contexts.
+     *
+     * @since 2.0
+     */
+    static class ContextFrame {
+
+        final int fBeginOffset;
+        final int fOffset;
+        final int fVisibleOffset;
+        final IContextInformation fInformation;
+        final IContextInformationValidator fValidator;
+        final IContextInformationPresenter fPresenter;
+
+        /*
+         * @since 3.1
+         */
+        public ContextFrame(IContextInformation information, int beginOffset, int offset, int visibleOffset, IContextInformationValidator validator, IContextInformationPresenter presenter) {
+            fInformation = information;
+            fBeginOffset = beginOffset;
+            fOffset = offset;
+            fVisibleOffset = visibleOffset;
+            fValidator = validator;
+            fPresenter = presenter;
+        }
+
+        /*
+         * @see java.lang.Object#equals(java.lang.Object)
+         * @since 3.0
+         */
+        public bool equals(Object obj) {
+            if (obj instanceof ContextFrame) {
+                ContextFrame frame= (ContextFrame) obj;
+                return fInformation.equals(frame.fInformation) && fBeginOffset is frame.fBeginOffset;
+            }
+            return super.equals(obj);
+        }
+
+        /*
+         * @see java.lang.Object#hashCode()
+         * @since 3.1
+         */
+        public int hashCode() {
+            return (fInformation.hashCode() << 16) | fBeginOffset;
+        }
+    }
+
+    private ITextViewer fViewer;
+    private ContentAssistant fContentAssistant;
+
+    private PopupCloser fPopupCloser= new PopupCloser();
+    private Shell fContextSelectorShell;
+    private Table fContextSelectorTable;
+    private IContextInformation[] fContextSelectorInput;
+    private String fLineDelimiter= null;
+
+    private Shell fContextInfoPopup;
+    private StyledText fContextInfoText;
+    private TextPresentation fTextPresentation;
+
+    private Stack fContextFrameStack= new Stack();
+    /**
+     * The content assist subject control.
+     *
+     * @since 3.0
+     */
+    private IContentAssistSubjectControl fContentAssistSubjectControl;
+    /**
+     * The content assist subject control adapter.
+     *
+     * @since 3.0
+     */
+    private ContentAssistSubjectControlAdapter fContentAssistSubjectControlAdapter;
+
+    /**
+     * Selection listener on the text widget which is active
+     * while a context information pop up is shown.
+     *
+     * @since 3.0
+     */
+    private SelectionListener fTextWidgetSelectionListener;
+
+    /**
+     * The last removed context frame is remembered in order to not re-query the
+     * user about which context should be used.
+     *
+     * @since 3.0
+     */
+    private ContextFrame fLastContext= null;
+
+    /**
+     * Creates a new context information popup.
+     *
+     * @param contentAssistant the content assist for computing the context information
+     * @param viewer the viewer on top of which the context information is shown
+     */
+    public ContextInformationPopup(ContentAssistant contentAssistant, ITextViewer viewer) {
+        fContentAssistant= contentAssistant;
+        fViewer= viewer;
+        fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fViewer);
+    }
+
+    /**
+     * Creates a new context information popup.
+     *
+     * @param contentAssistant the content assist for computing the context information
+     * @param contentAssistSubjectControl the content assist subject control on top of which the context information is shown
+     * @since 3.0
+     */
+    public ContextInformationPopup(ContentAssistant contentAssistant, IContentAssistSubjectControl contentAssistSubjectControl) {
+        fContentAssistant= contentAssistant;
+        fContentAssistSubjectControl= contentAssistSubjectControl;
+        fContentAssistSubjectControlAdapter= new ContentAssistSubjectControlAdapter(fContentAssistSubjectControl);
+    }
+
+    /**
+     * Shows all possible contexts for the given cursor position of the viewer.
+     *
+     * @param autoActivated <code>true</code>  if auto activated
+     * @return  a potential error message or <code>null</code> in case of no error
+     */
+    public String showContextProposals(final bool autoActivated) {
+        final Control control= fContentAssistSubjectControlAdapter.getControl();
+        BusyIndicator.showWhile(control.getDisplay(), new Runnable() {
+            public void run() {
+
+                int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
+
+                IContextInformation[] contexts= computeContextInformation(offset);
+                int count = (contexts is null ? 0 : contexts.length);
+                if (count is 1) {
+
+                    ContextFrame frame= createContextFrame(contexts[0], offset);
+                    if (isDuplicate(frame))
+                        validateContextInformation();
+                    else
+                        // Show context information directly
+                        internalShowContextInfo(frame);
+
+                } else if (count > 0) {
+
+                    // if any of the proposed context matches any of the contexts on the stack,
+                    // assume that one (so, if context info is invoked repeatedly, the current
+                    // info is kept)
+                    for (int i= 0; i < contexts.length; i++) {
+                        IContextInformation info= contexts[i];
+                        ContextFrame frame= createContextFrame(info, offset);
+
+                        // check top of stack and stored context
+                        if (isDuplicate(frame)) {
+                            validateContextInformation();
+                            return;
+                        }
+
+                        if (isLastFrame(frame)) {
+                            internalShowContextInfo(frame);
+                            return;
+                        }
+
+                        // also check all other contexts
+                        for (Iterator it= fContextFrameStack.iterator(); it.hasNext(); ) {
+                            ContextFrame stackFrame= (ContextFrame) it.next();
+                            if (stackFrame.equals(frame)) {
+                                validateContextInformation();
+                                return;
+                            }
+                        }
+                    }
+
+                    // otherwise:
+                    // Precise context must be selected
+
+                    if (fLineDelimiter is null)
+                        fLineDelimiter= fContentAssistSubjectControlAdapter.getLineDelimiter();
+
+                    createContextSelector();
+                    setContexts(contexts);
+                    displayContextSelector();
+                }
+            }
+        });
+
+        return getErrorMessage();
+    }
+
+    /**
+     * Displays the given context information for the given offset.
+     *
+     * @param info the context information
+     * @param offset the offset
+     * @since 2.0
+     */
+    public void showContextInformation(final IContextInformation info, final int offset) {
+        Control control= fContentAssistSubjectControlAdapter.getControl();
+        BusyIndicator.showWhile(control.getDisplay(), new Runnable() {
+            public void run() {
+                if (info is null)
+                    validateContextInformation();
+                else {
+                    ContextFrame frame= createContextFrame(info, offset);
+                    if (isDuplicate(frame))
+                        validateContextInformation();
+                    else
+                        internalShowContextInfo(frame);
+                    hideContextSelector();
+                }
+            }
+        });
+    }
+
+    /**
+     * Displays the given context information for the given offset.
+     *
+     * @param frame the context frame to display, or <code>null</code>
+     * @since 3.0
+     */
+    private void internalShowContextInfo(ContextFrame frame) {
+        if (frame !is null) {
+            fContextFrameStack.push(frame);
+            if (fContextFrameStack.size() is 1)
+                fLastContext= null;
+            internalShowContextFrame(frame, fContextFrameStack.size() is 1);
+            validateContextInformation();
+        }
+    }
+
+    /**
+     * Creates a context frame for the given offset.
+     *
+     * @param information the context information
+     * @param offset the offset
+     * @return the created context frame
+     * @since 3.0
+     */
+    private ContextFrame createContextFrame(IContextInformation information, int offset) {
+        IContextInformationValidator validator= fContentAssistSubjectControlAdapter.getContextInformationValidator(fContentAssistant, offset);
+
+        if (validator !is null) {
+            int beginOffset= (information instanceof IContextInformationExtension) ? ((IContextInformationExtension) information).getContextInformationPosition() : offset;
+            if (beginOffset is -1) beginOffset= offset;
+            int visibleOffset= fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x - (offset - beginOffset);
+            IContextInformationPresenter presenter = fContentAssistSubjectControlAdapter.getContextInformationPresenter(fContentAssistant, offset);
+            return new ContextFrame(information, beginOffset, offset, visibleOffset, validator, presenter);
+        }
+
+        return null;
+    }
+
+    /**
+     * Compares <code>frame</code> with the top of the stack, returns <code>true</code>
+     * if the frames are the same.
+     *
+     * @param frame the frame to check
+     * @return <code>true</code> if <code>frame</code> matches the top of the stack
+     * @since 3.0
+     */
+    private bool isDuplicate(ContextFrame frame) {
+        if (frame is null)
+            return false;
+        if (fContextFrameStack.isEmpty())
+            return false;
+        // stack not empty
+        ContextFrame top= (ContextFrame) fContextFrameStack.peek();
+        return frame.equals(top);
+    }
+
+    /**
+     * Compares <code>frame</code> with most recently removed context frame, returns <code>true</code>
+     * if the frames are the same.
+     *
+     * @param frame the frame to check
+     * @return <code>true</code> if <code>frame</code> matches the most recently removed
+     * @since 3.0
+     */
+    private bool isLastFrame(ContextFrame frame) {
+        return frame !is null && frame.equals(fLastContext);
+    }
+
+    /**
+     * Shows the given context frame.
+     *
+     * @param frame the frame to display
+     * @param initial <code>true</code> if this is the first frame to be displayed
+     * @since 2.0
+     */
+    private void internalShowContextFrame(ContextFrame frame, bool initial) {
+
+        fContentAssistSubjectControlAdapter.installValidator(frame);
+
+        if (frame.fPresenter !is null) {
+            if (fTextPresentation is null)
+                fTextPresentation= new TextPresentation();
+            fContentAssistSubjectControlAdapter.installContextInformationPresenter(frame);
+            frame.fPresenter.updatePresentation(frame.fOffset, fTextPresentation);
+        }
+
+        createContextInfoPopup();
+
+        fContextInfoText.setText(frame.fInformation.getInformationDisplayString());
+        if (fTextPresentation !is null)
+            TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
+        resize(frame.fVisibleOffset);
+
+        if (initial) {
+            if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP)) {
+                if (fContentAssistSubjectControlAdapter.getControl() !is null) {
+                    fTextWidgetSelectionListener= new SelectionAdapter() {
+                        /*
+                         * @see dwt.events.SelectionAdapter#widgetSelected(dwt.events.SelectionEvent)
+                         */
+                        public void widgetSelected(SelectionEvent e) {
+                            validateContextInformation();
+                        }};
+                    fContentAssistSubjectControlAdapter.addSelectionListener(fTextWidgetSelectionListener);
+                }
+                fContentAssistant.addToLayout(this, fContextInfoPopup, ContentAssistant.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
+                fContextInfoPopup.setVisible(true);
+            }
+        } else {
+            fContentAssistant.layout(ContentAssistant.LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, frame.fVisibleOffset);
+        }
+    }
+
+    /**
+     * Computes all possible context information for the given offset.
+     *
+     * @param offset the offset
+     * @return all possible context information for the given offset
+     * @since 2.0
+     */
+    private IContextInformation[] computeContextInformation(int offset) {
+        return fContentAssistSubjectControlAdapter.computeContextInformation(fContentAssistant, offset);
+    }
+
+    /**
+     *Returns the error message generated while computing context information.
+     *
+     * @return the error message
+     */
+    private String getErrorMessage() {
+        return fContentAssistant.getErrorMessage();
+    }
+
+    /**
+     * Creates the context information popup. This is the tool tip like overlay window.
+     */
+    private void createContextInfoPopup() {
+        if (Helper.okToUse(fContextInfoPopup))
+            return;
+
+        Control control= fContentAssistSubjectControlAdapter.getControl();
+        Display display= control.getDisplay();
+
+        fContextInfoPopup= new Shell(control.getShell(), DWT.NO_TRIM | DWT.ON_TOP);
+        fContextInfoPopup.setBackground(display.getSystemColor(DWT.COLOR_BLACK));
+
+        fContextInfoText= new StyledText(fContextInfoPopup, DWT.MULTI | DWT.READ_ONLY | DWT.WRAP);
+
+        Color c= fContentAssistant.getContextInformationPopupBackground();
+        if (c is null)
+            c= display.getSystemColor(DWT.COLOR_INFO_BACKGROUND);
+        fContextInfoText.setBackground(c);
+
+        c= fContentAssistant.getContextInformationPopupForeground();
+        if (c is null)
+            c= display.getSystemColor(DWT.COLOR_INFO_FOREGROUND);
+        fContextInfoText.setForeground(c);
+    }
+
+    /**
+     * Resizes the context information popup.
+     *
+     * @param offset the caret offset in widget coordinates
+     * @since 2.0
+     */
+    private void resize(int offset) {
+        Point size= fContextInfoText.computeSize(DWT.DEFAULT, DWT.DEFAULT, true);
+        final int TEXT_PAD= 0;
+        final int BORDER_PAD= 2;
+        final int PAD= TEXT_PAD + BORDER_PAD;
+        size.x += PAD;
+        Rectangle bounds= fContentAssistant.getLayoutManager().computeBoundsAboveBelow(fContextInfoPopup, size, offset);
+        if (bounds.width < size.x)
+            // we don't fit on the screen - try again and wrap
+            size= fContextInfoText.computeSize(bounds.width - PAD, DWT.DEFAULT, true);
+        
+        size.x += TEXT_PAD;
+        fContextInfoText.setSize(size);
+        fContextInfoText.setLocation(1,1);
+        size.x += BORDER_PAD;
+        size.y += BORDER_PAD;
+        fContextInfoPopup.setSize(size);
+    }
+
+    /**
+     * Hides the context information popup.
+     */
+    private void hideContextInfoPopup() {
+
+        if (Helper.okToUse(fContextInfoPopup)) {
+
+            int size= fContextFrameStack.size();
+            if (size > 0) {
+                fLastContext= (ContextFrame) fContextFrameStack.pop();
+                -- size;
+            }
+
+            if (size > 0) {
+                ContextFrame current= (ContextFrame) fContextFrameStack.peek();
+                internalShowContextFrame(current, false);
+            } else {
+
+                fContentAssistant.removeContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP);
+
+                if (fContentAssistSubjectControlAdapter.getControl() !is null)
+                    fContentAssistSubjectControlAdapter.removeSelectionListener(fTextWidgetSelectionListener);
+                fTextWidgetSelectionListener= null;
+
+                fContextInfoPopup.setVisible(false);
+                fContextInfoPopup.dispose();
+                fContextInfoPopup= null;
+
+                if (fTextPresentation !is null) {
+                    fTextPresentation.clear();
+                    fTextPresentation= null;
+                }
+            }
+        }
+
+        if (fContextInfoPopup is null)
+            fContentAssistant.contextInformationClosed();
+    }
+
+    /**
+     * Creates the context selector in case the user has the choice between multiple valid contexts
+     * at a given offset.
+     */
+    private void createContextSelector() {
+        if (Helper.okToUse(fContextSelectorShell))
+            return;
+
+        Control control= fContentAssistSubjectControlAdapter.getControl();
+        fContextSelectorShell= new Shell(control.getShell(), DWT.ON_TOP | DWT.RESIZE);
+        GridLayout layout= new GridLayout();
+        layout.marginWidth= 0;
+        layout.marginHeight= 0;
+        fContextSelectorShell.setLayout(layout);
+        fContextSelectorShell.setBackground(control.getDisplay().getSystemColor(DWT.COLOR_BLACK));
+
+
+        fContextSelectorTable= new Table(fContextSelectorShell, DWT.H_SCROLL | DWT.V_SCROLL);
+        fContextSelectorTable.setLocation(1, 1);
+        GridData gd= new GridData(GridData.FILL_BOTH);
+        gd.heightHint= fContextSelectorTable.getItemHeight() * 10;
+        gd.widthHint= 300;
+        fContextSelectorTable.setLayoutData(gd);
+
+        fContextSelectorShell.pack(true);
+
+        Color c= fContentAssistant.getContextSelectorBackground();
+        if (c is null)
+            c= control.getDisplay().getSystemColor(DWT.COLOR_INFO_BACKGROUND);
+        fContextSelectorTable.setBackground(c);
+
+        c= fContentAssistant.getContextSelectorForeground();
+        if (c is null)
+            c= control.getDisplay().getSystemColor(DWT.COLOR_INFO_FOREGROUND);
+        fContextSelectorTable.setForeground(c);
+
+        fContextSelectorTable.addSelectionListener(new SelectionListener() {
+            public void widgetSelected(SelectionEvent e) {
+            }
+
+            public void widgetDefaultSelected(SelectionEvent e) {
+                insertSelectedContext();
+                hideContextSelector();
+            }
+        });
+
+        fPopupCloser.install(fContentAssistant, fContextSelectorTable);
+
+        fContextSelectorTable.setHeaderVisible(false);
+        fContentAssistant.addToLayout(this, fContextSelectorShell, ContentAssistant.LayoutManager.LAYOUT_CONTEXT_SELECTOR, fContentAssistant.getSelectionOffset());
+    }
+
+    /**
+     * Returns the minimal required height for the popup, may return 0 if the popup has not been
+     * created yet.
+     * 
+     * @return the minimal height
+     * @since 3.3
+     */
+    int getMinimalHeight() {
+        int height= 0;
+        if (Helper.okToUse(fContextSelectorTable)) {
+            int items= fContextSelectorTable.getItemHeight() * 10;
+            Rectangle trim= fContextSelectorTable.computeTrim(0, 0, DWT.DEFAULT, items);
+            height= trim.height;
+        }
+        return height;
+    }
+
+    /**
+     * Causes the context information of the context selected in the context selector
+     * to be displayed in the context information popup.
+     */
+    private void insertSelectedContext() {
+        int i= fContextSelectorTable.getSelectionIndex();
+
+        if (i < 0 || i >= fContextSelectorInput.length)
+            return;
+
+        int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
+        internalShowContextInfo(createContextFrame(fContextSelectorInput[i], offset));
+    }
+
+    /**
+     * Sets the contexts in the context selector to the given set.
+     *
+     * @param contexts the possible contexts
+     */
+    private void setContexts(IContextInformation[] contexts) {
+        if (Helper.okToUse(fContextSelectorTable)) {
+
+            fContextSelectorInput= contexts;
+
+            fContextSelectorTable.setRedraw(false);
+            fContextSelectorTable.removeAll();
+
+            TableItem item;
+            IContextInformation t;
+            for (int i= 0; i < contexts.length; i++) {
+                t= contexts[i];
+                item= new TableItem(fContextSelectorTable, DWT.NULL);
+                if (t.getImage() !is null)
+                    item.setImage(t.getImage());
+                item.setText(t.getContextDisplayString());
+            }
+
+            fContextSelectorTable.select(0);
+            fContextSelectorTable.setRedraw(true);
+        }
+    }
+
+    /**
+     * Displays the context selector.
+     */
+    private void displayContextSelector() {
+        if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_SELECTOR))
+            fContextSelectorShell.setVisible(true);
+    }
+
+    /**
+     * Hides the context selector.
+     */
+    private void hideContextSelector() {
+        if (Helper.okToUse(fContextSelectorShell)) {
+            fContentAssistant.removeContentAssistListener(this, ContentAssistant.CONTEXT_SELECTOR);
+
+            fPopupCloser.uninstall();
+            fContextSelectorShell.setVisible(false);
+            fContextSelectorShell.dispose();
+            fContextSelectorShell= null;
+        }
+
+        if (!Helper.okToUse(fContextInfoPopup))
+            fContentAssistant.contextInformationClosed();
+    }
+
+    /**
+     *Returns whether the context selector has the focus.
+     *
+     * @return <code>true</code> if the context selector has the focus
+     */
+    public bool hasFocus() {
+        if (Helper.okToUse(fContextSelectorShell))
+            return (fContextSelectorShell.isFocusControl() || fContextSelectorTable.isFocusControl());
+
+        return false;
+    }
+
+    /**
+     * Hides context selector and context information popup.
+     */
+    public void hide() {
+        hideContextSelector();
+        hideContextInfoPopup();
+    }
+
+    /**
+     * Returns whether this context information popup is active. I.e., either
+     * a context selector or context information is displayed.
+     *
+     * @return <code>true</code> if the context selector is active
+     */
+    public bool isActive() {
+        return (Helper.okToUse(fContextInfoPopup) || Helper.okToUse(fContextSelectorShell));
+    }
+
+    /*
+     * @see IContentAssistListener#verifyKey(VerifyEvent)
+     */
+    public bool verifyKey(VerifyEvent e) {
+        if (Helper.okToUse(fContextSelectorShell))
+            return contextSelectorKeyPressed(e);
+        if (Helper.okToUse(fContextInfoPopup))
+            return contextInfoPopupKeyPressed(e);
+        return true;
+    }
+
+    /**
+     * Processes a key stroke in the context selector.
+     *
+     * @param e the verify event describing the key stroke
+     * @return <code>true</code> if processing can be stopped
+     */
+    private bool contextSelectorKeyPressed(VerifyEvent e) {
+
+        char key= e.character;
+        if (key is 0) {
+
+            int newSelection= fContextSelectorTable.getSelectionIndex();
+            int visibleRows= (fContextSelectorTable.getSize().y / fContextSelectorTable.getItemHeight()) - 1;
+            int itemCount= fContextSelectorTable.getItemCount();
+            switch (e.keyCode) {
+                case DWT.ARROW_UP :
+                    newSelection -= 1;
+                    if (newSelection < 0)
+                        newSelection= itemCount - 1;
+                    break;
+
+                case DWT.ARROW_DOWN :
+                    newSelection += 1;
+                    if (newSelection > itemCount - 1)
+                        newSelection= 0;
+                    break;
+
+                case DWT.PAGE_DOWN :
+                    newSelection += visibleRows;
+                    if (newSelection >= itemCount)
+                        newSelection= itemCount - 1;
+                    break;
+
+                case DWT.PAGE_UP :
+                    newSelection -= visibleRows;
+                    if (newSelection < 0)
+                        newSelection= 0;
+                    break;
+
+                case DWT.HOME :
+                    newSelection= 0;
+                    break;
+
+                case DWT.END :
+                    newSelection= itemCount - 1;
+                    break;
+
+                default :
+                    if (e.keyCode !is DWT.CAPS_LOCK && e.keyCode !is DWT.MOD1 && e.keyCode !is DWT.MOD2 && e.keyCode !is DWT.MOD3 && e.keyCode !is DWT.MOD4)
+                        hideContextSelector();
+                    return true;
+            }
+
+            fContextSelectorTable.setSelection(newSelection);
+            fContextSelectorTable.showSelection();
+            e.doit= false;
+            return false;
+
+        } else if ('\t' is key) {
+            // switch focus to selector shell
+            e.doit= false;
+            fContextSelectorShell.setFocus();
+            return false;
+        } else if (key is DWT.ESC) {
+            e.doit= false;
+            hideContextSelector();
+        }
+
+        return true;
+    }
+
+    /**
+     * Processes a key stroke while the info popup is up.
+     *
+     * @param e the verify event describing the key stroke
+     * @return <code>true</code> if processing can be stopped
+     */
+    private bool contextInfoPopupKeyPressed(KeyEvent e) {
+
+        char key= e.character;
+        if (key is 0) {
+
+            switch (e.keyCode) {
+                case DWT.ARROW_LEFT:
+                case DWT.ARROW_RIGHT:
+                    validateContextInformation();
+                    break;
+                default:
+                    if (e.keyCode !is DWT.CAPS_LOCK && e.keyCode !is DWT.MOD1 && e.keyCode !is DWT.MOD2 && e.keyCode !is DWT.MOD3 && e.keyCode !is DWT.MOD4)
+                        hideContextInfoPopup();
+                    break;
+            }
+
+        } else if (key is DWT.ESC) {
+            e.doit= false;
+            hideContextInfoPopup();
+        } else {
+            validateContextInformation();
+        }
+        return true;
+    }
+
+    /*
+     * @see IEventConsumer#processEvent(VerifyEvent)
+     */
+    public void processEvent(VerifyEvent event) {
+        if (Helper.okToUse(fContextSelectorShell))
+            contextSelectorProcessEvent(event);
+        if (Helper.okToUse(fContextInfoPopup))
+            contextInfoPopupProcessEvent(event);
+    }
+
+    /**
+     * Processes a key stroke in the context selector.
+     *
+     * @param e the verify event describing the key stroke
+     */
+    private void contextSelectorProcessEvent(VerifyEvent e) {
+
+        if (e.start is e.end && e.text !is null && e.text.equals(fLineDelimiter)) {
+            e.doit= false;
+            insertSelectedContext();
+        }
+
+        hideContextSelector();
+    }
+
+    /**
+     * Processes a key stroke while the info popup is up.
+     *
+     * @param e the verify event describing the key stroke
+     */
+    private void contextInfoPopupProcessEvent(VerifyEvent e) {
+        if (e.start !is e.end && (e.text is null || e.text.length() is 0))
+            validateContextInformation();
+    }
+
+    /**
+     * Validates the context information for the viewer's actual cursor position.
+     */
+    private void validateContextInformation() {
+        /*
+         * Post the code in the event queue in order to ensure that the
+         * action described by this verify key event has already been executed.
+         * Otherwise, we'd validate the context information based on the
+         * pre-key-stroke state.
+         */
+        if (!Helper.okToUse(fContextInfoPopup))
+            return;
+
+        fContextInfoPopup.getDisplay().asyncExec(new Runnable() {
+
+            private ContextFrame fFrame= (ContextFrame) fContextFrameStack.peek();
+
+            public void run() {
+                // only do this if no other frames have been added in between
+                if (!fContextFrameStack.isEmpty() && fFrame is fContextFrameStack.peek()) {
+                    int offset= fContentAssistSubjectControlAdapter.getSelectedRange().x;
+
+                    // iterate all contexts on the stack
+                    while (Helper.okToUse(fContextInfoPopup) && !fContextFrameStack.isEmpty()) {
+                        ContextFrame top= (ContextFrame) fContextFrameStack.peek();
+                        if (top.fValidator is null || !top.fValidator.isContextInformationValid(offset)) {
+                            hideContextInfoPopup(); // loop variant: reduces the number of contexts on the stack
+                        } else if (top.fPresenter !is null && top.fPresenter.updatePresentation(offset, fTextPresentation)) {
+                            int widgetOffset= fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x;
+                            TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
+                            resize(widgetOffset);
+                            break;
+                        } else
+                            break;
+                    }
+                }
+            }
+        });
+    }
+}