/** * Copyright (c) 2005-2006 Aptana, Inc. * * 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. If redistributing this code, * this entire header must remain intact. */ package com.aptana.ide.editors.unified.contentassist; /*************************************************************************************************** * Copyright (c) 2000, 2005 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 **************************************************************************************************/ import java.util.Iterator; import java.util.Stack; import org.eclipse.jface.contentassist.IContentAssistSubjectControl; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.TextPresentation; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.jface.text.contentassist.IContextInformationExtension; import org.eclipse.jface.text.contentassist.IContextInformationPresenter; import org.eclipse.jface.text.contentassist.IContextInformationValidator; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.BusyIndicator; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import com.aptana.ide.editors.unified.contentassist.ContentAssistant.LayoutManager; /** * 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 implements IContentAssistListener, KeyListener { /** * Outer border thickness in pixels. * * @since 3.1 */ private static final int OUTER_BORDER = 1; /** * Inner border thickness in pixels. * * @since 3.1 */ private static final int INNER_BORDER = 3; /** * 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; /** * @param information * @param beginOffset * @param offset * @param visibleOffset * @param validator * @param presenter * @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) */ public boolean equals(Object obj) { if (obj instanceof ContextFrame) { ContextFrame frame = (ContextFrame) obj; return fInformation.equals(frame.fInformation) && fBeginOffset == frame.fBeginOffset; } return super.equals(obj); } /** * @see java.lang.Object#hashCode() */ 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 code assist subject control. * * @since 3.0 */ private IContentAssistSubjectControl fContentAssistSubjectControl; /** * The code 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; private char fActivationKey; /** * Creates a new context information popup. * * @param contentAssistant * the code 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 code assist for computing the context information * @param contentAssistSubjectControl * the code 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 boolean 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 == null ? 0 : contexts.length); if (count == 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 the 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 == null) { fLineDelimiter = fContentAssistSubjectControlAdapter.getLineDelimiter(); } createContextSelector(); setContexts(contexts); displayContextSelector(); hideContextInfoPopup(); } } }); 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 == 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 != null) { fContextFrameStack.push(frame); if (fContextFrameStack.size() == 1) { fLastContext = null; } internalShowContextFrame(frame, fContextFrameStack.size() == 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 != null) { int beginOffset = (information instanceof IContextInformationExtension) ? ((IContextInformationExtension) information) .getContextInformationPosition() : offset; if (beginOffset == -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 boolean isDuplicate(ContextFrame frame) { if (frame == 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 boolean isLastFrame(ContextFrame frame) { return frame != 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, boolean initial) { fContentAssistSubjectControlAdapter.installValidator(frame); if (frame.fPresenter != null) { if (fTextPresentation == null) { fTextPresentation = new TextPresentation(); } fContentAssistSubjectControlAdapter.installContextInformationPresenter(frame); frame.fPresenter.updatePresentation(frame.fOffset, fTextPresentation); } createContextInfoPopup(); fContextInfoText.setText(frame.fInformation.getInformationDisplayString()); if (fTextPresentation != null) { TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText); } resize(); if (initial) { if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP)) { if (fContentAssistSubjectControlAdapter.getControl() != null) { fTextWidgetSelectionListener = new SelectionAdapter() { /* * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.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); fContentAssistSubjectControlAdapter.addKeyListener(this); } } 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; } int shellStyle = SWT.TOOL; int style = SWT.NONE; GridLayout layout; GridData gd; Control control = fContentAssistSubjectControlAdapter.getControl(); Display display = control.getDisplay(); fContextInfoPopup = new Shell(control.getShell(), SWT.NO_FOCUS | SWT.ON_TOP | shellStyle); fContextInfoPopup.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); Composite composite = fContextInfoPopup; layout = new GridLayout(1, false); int border = ((shellStyle & SWT.NO_TRIM) == 0) ? 0 : OUTER_BORDER; layout.marginHeight = border; layout.marginWidth = border; composite.setLayout(layout); gd = new GridData(GridData.FILL_BOTH); composite.setLayoutData(gd); composite = new Composite(composite, SWT.NONE); layout = new GridLayout(1, false); layout.marginHeight = 1; layout.marginWidth = 1; layout.verticalSpacing = 3; composite.setLayout(layout); gd = new GridData(GridData.FILL_BOTH); composite.setLayoutData(gd); composite.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); composite.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); // Text field fContextInfoText = new StyledText(composite, SWT.MULTI | SWT.READ_ONLY | style); gd = new GridData(GridData.BEGINNING | GridData.FILL_BOTH); gd.horizontalIndent = INNER_BORDER; gd.verticalIndent = INNER_BORDER; fContextInfoText.setLayoutData(gd); fContextInfoText.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); fContextInfoText.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); fContextInfoText.setLocation(1, 1); } /** * Resizes the context information popup. * * @since 2.0 */ private void resize() { Point size = fContextInfoText.computeSize(SWT.DEFAULT, SWT.DEFAULT, true); size.x += 2; fContextInfoText.setSize(size); size.x += 8; size.y += 10; 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() != null) { fContentAssistSubjectControlAdapter.removeSelectionListener(fTextWidgetSelectionListener); fContentAssistSubjectControlAdapter.removeKeyListener(this); } fTextWidgetSelectionListener = null; fContextInfoPopup.setVisible(false); fContextInfoPopup.dispose(); fContextInfoPopup = null; if (fTextPresentation != null) { fTextPresentation.clear(); fTextPresentation = null; } } } if (fContextInfoPopup == 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(), SWT.ON_TOP | SWT.RESIZE); GridLayout layout = new GridLayout(); layout.marginWidth = 0; layout.marginHeight = 0; fContextSelectorShell.setLayout(layout); fContextSelectorShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK)); fContextSelectorTable = new Table(fContextSelectorShell, SWT.H_SCROLL | SWT.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 == null) { c = control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND); } fContextSelectorTable.setBackground(c); c = fContentAssistant.getContextSelectorForeground(); if (c == null) { c = control.getDisplay().getSystemColor(SWT.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()); } /** * 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, SWT.NULL); if (t.getImage() != 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 boolean hasFocus() { if (Helper.okToUse(fContextSelectorShell)) { return (fContextSelectorShell.isFocusControl() || fContextSelectorTable.isFocusControl()); } return false; } /** * Hides context selector and context information popup. */ public void hide() { fContentAssistSubjectControlAdapter.removeKeyListener(this); 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 boolean isActive() { return (Helper.okToUse(fContextInfoPopup) || Helper.okToUse(fContextSelectorShell)); } /** * @see com.aptana.ide.editors.unified.contentassist.IContentAssistListener#verifyKey(org.eclipse.swt.events.VerifyEvent) */ public boolean 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 boolean contextSelectorKeyPressed(VerifyEvent e) { char key = e.character; if (key == 0) { int change; int visibleRows = (fContextSelectorTable.getSize().y / fContextSelectorTable.getItemHeight()) - 1; int selection = fContextSelectorTable.getSelectionIndex(); switch (e.keyCode) { case SWT.ARROW_UP: change = (fContextSelectorTable.getSelectionIndex() > 0 ? -1 : 0); break; case SWT.ARROW_DOWN: change = (fContextSelectorTable.getSelectionIndex() < fContextSelectorTable.getItemCount() - 1 ? 1 : 0); break; case SWT.PAGE_DOWN: change = visibleRows; if (selection + change >= fContextSelectorTable.getItemCount()) { change = fContextSelectorTable.getItemCount() - selection; } break; case SWT.PAGE_UP: change = -visibleRows; if (selection + change < 0) { change = -selection; } break; case SWT.HOME: change = -selection; break; case SWT.END: change = fContextSelectorTable.getItemCount() - selection; break; default: if (e.keyCode != SWT.CAPS_LOCK && e.keyCode != SWT.MOD1 && e.keyCode != SWT.MOD2 && e.keyCode != SWT.MOD3 && e.keyCode != SWT.MOD4) { hideContextSelector(); } return true; } fContextSelectorTable.setSelection(selection + change); fContextSelectorTable.showSelection(); e.doit = false; return false; } else if ('\t' == key) { // switch focus to selector shell e.doit = false; fContextSelectorShell.setFocus(); return false; } else if (key == 0x1B) { // terminate on Esc 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 boolean contextInfoPopupKeyPressed(KeyEvent e) { char key = e.character; if (key == 0) { switch (e.keyCode) { case SWT.ARROW_LEFT: case SWT.ARROW_RIGHT: validateContextInformation(); break; default: if (e.keyCode != SWT.CAPS_LOCK && e.keyCode != SWT.MOD1 && e.keyCode != SWT.MOD2 && e.keyCode != SWT.MOD3 && e.keyCode != SWT.MOD4) { hideContextInfoPopup(); } break; } } else if (key == 0x1B) { // terminate on Esc hideContextInfoPopup(); } else { validateContextInformation(); } return true; } /** * @see org.eclipse.jface.text.IEventConsumer#processEvent(org.eclipse.swt.events.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 == e.end && e.text != 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 != e.end && (e.text == null || e.text.length() == 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 == 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 == null || !top.fValidator.isContextInformationValid(offset)) { hideContextInfoPopup(); // loop variant: reduces the number of contexts // on the stack } else if (top.fPresenter != null && top.fPresenter.updatePresentation(offset, fTextPresentation)) { TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText); resize(); break; } else { break; } } } } }); } /** * Gets the activation key * * @return the activation key */ public char getActivationKey() { return fActivationKey; } /** * Sets the activation key * * @param activationKey */ public void setActivationKey(char activationKey) { fActivationKey = activationKey; } /** * @see org.eclipse.swt.events.KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent) */ public void keyPressed(KeyEvent e) { } /** * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent) */ public void keyReleased(KeyEvent e) { int key = e.character; if (key == 13) { int offset = fContentAssistSubjectControlAdapter.getSelectedRange().x; this.fContentAssistant.layout(LayoutManager.LAYOUT_CONTEXT_INFO_POPUP, offset); } } }