blob: 600741249f02e12fd80ac0ce4d0b7e07d8c5a76a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jface.text.contentassist;
import java.util.Iterator;
import java.util.Stack;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.VerifyKeyListener;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyEvent;
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.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
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 org.eclipse.jface.contentassist.IContentAssistSubjectControl;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.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 implements 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;
}
@Override
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);
}
@Override
public int hashCode() {
return (fInformation.hashCode() << 16) | fBeginOffset;
}
}
private ITextViewer fViewer;
private ContentAssistant fContentAssistant;
private PopupCloser fPopupCloser= new PopupCloser();
private Shell fContextSelectorShell;
private Point fContextSelectorPopupSize;
private Table fContextSelectorTable;
private IContextInformation[] fContextSelectorInput;
private String fLineDelimiter= null;
private Shell fContextInfoPopup;
private StyledText fContextInfoText;
private TextPresentation fTextPresentation;
private Stack<ContextFrame> 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 boolean autoActivated) {
final Control control= fContentAssistSubjectControlAdapter.getControl();
BusyIndicator.showWhile(control.getDisplay(), new Runnable() {
@Override
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 any of the contexts on the stack,
// assume that one (so, if context info is invoked repeatedly, the current
// info is kept)
int index= 0;
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)) {
index= i;
}
// also check all other contexts
for (Iterator<ContextFrame> it= fContextFrameStack.iterator(); it.hasNext(); ) {
ContextFrame stackFrame= it.next();
if (stackFrame.equals(frame)) {
validateContextInformation();
return;
}
}
}
// otherwise:
// Precise context must be selected
if (fLineDelimiter == null)
fLineDelimiter= fContentAssistSubjectControlAdapter.getLineDelimiter();
createContextSelector();
setContexts(contexts, index);
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() {
@Override
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= 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(frame.fVisibleOffset);
if (initial) {
if (fContentAssistant.addContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP)) {
if (fContentAssistSubjectControlAdapter.getControl() != null) {
fTextWidgetSelectionListener= new SelectionAdapter() {
@Override
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(), SWT.NO_TRIM | SWT.ON_TOP);
fContextInfoPopup.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
fContextInfoText= new StyledText(fContextInfoPopup, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP);
Color c= fContentAssistant.getContextInformationPopupBackground();
if (c == null)
c= display.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
fContextInfoText.setBackground(c);
c= fContentAssistant.getContextInformationPopupForeground();
if (c == null)
c= display.getSystemColor(SWT.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(SWT.DEFAULT, SWT.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, SWT.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= fContextFrameStack.pop();
-- size;
}
if (size > 0) {
ContextFrame current= fContextFrameStack.peek();
internalShowContextFrame(current, false);
} else {
fContentAssistant.removeContentAssistListener(this, ContentAssistant.CONTEXT_INFO_POPUP);
if (fContentAssistSubjectControlAdapter.getControl() != null)
fContentAssistSubjectControlAdapter.removeSelectionListener(fTextWidgetSelectionListener);
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));
fContextSelectorShell.addControlListener(new ControlListener() {
@Override
public void controlMoved(ControlEvent e) {
}
@Override
public void controlResized(ControlEvent e) {
fContextSelectorPopupSize= fContextSelectorShell.getSize();
}
});
if (fViewer instanceof ITextViewerExtension) {
final ITextViewerExtension textViewerExtension= (ITextViewerExtension)fViewer;
final StyledText textWidget= fViewer.getTextWidget();
final VerifyKeyListener verifyListener= new VerifyKeyListener() {
@Override
public void verifyKey(VerifyEvent event) {
if (isActive() && event.keyCode == 13 && event.character == '\r' && event.widget == textWidget) {
event.doit= false;
insertSelectedContext();
hideContextSelector();
}
}
};
textViewerExtension.prependVerifyKeyListener(verifyListener);
fContextSelectorShell.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
textViewerExtension.removeVerifyKeyListener(verifyListener);
}
});
}
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);
Point size= fContentAssistant.restoreContextSelectorPopupSize();
if (size != null)
fContextSelectorShell.setSize(size);
else
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() {
@Override
public void widgetSelected(SelectionEvent e) {
}
@Override
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, SWT.DEFAULT, items);
height= trim.height;
}
return height;
}
/**
* Returns the size of the context selector pop-up.
*
* @return a Point containing the size
* @since 3.9
*/
Point getContextSelectorPopupSize() {
return fContextSelectorPopupSize;
}
/**
* 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
* @param selectionIndex the index of the proposal to select
*/
private void setContexts(IContextInformation[] contexts, int selectionIndex) {
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(selectionIndex);
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.storeContextSelectorPopupSize();
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() {
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));
}
@Override
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 newSelection= fContextSelectorTable.getSelectionIndex();
int visibleRows= (fContextSelectorTable.getSize().y / fContextSelectorTable.getItemHeight()) - 1;
int itemCount= fContextSelectorTable.getItemCount();
switch (e.keyCode) {
case SWT.ARROW_UP :
newSelection -= 1;
if (newSelection < 0)
newSelection= itemCount - 1;
break;
case SWT.ARROW_DOWN :
newSelection += 1;
if (newSelection > itemCount - 1)
newSelection= 0;
break;
case SWT.PAGE_DOWN :
newSelection += visibleRows;
if (newSelection >= itemCount)
newSelection= itemCount - 1;
break;
case SWT.PAGE_UP :
newSelection -= visibleRows;
if (newSelection < 0)
newSelection= 0;
break;
case SWT.HOME :
newSelection= 0;
break;
case SWT.END :
newSelection= itemCount - 1;
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(newSelection);
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 == SWT.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 boolean contextInfoPopupKeyPressed(KeyEvent e) {
char key= e.character;
if (key == 0) {
switch (e.keyCode) {
case SWT.ARROW_LEFT:
case SWT.ARROW_RIGHT:
case SWT.ARROW_UP:
case SWT.ARROW_DOWN:
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 == SWT.ESC) {
e.doit= false;
hideContextInfoPopup();
} else {
validateContextInformation();
}
return true;
}
@Override
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= fContextFrameStack.peek();
@Override
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= 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)) {
int widgetOffset= fContentAssistSubjectControlAdapter.getWidgetSelectionRange().x;
TextPresentation.applyTextPresentation(fTextPresentation, fContextInfoText);
resize(widgetOffset);
break;
} else
break;
}
}
}
});
}
}