Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java')
-rw-r--r--org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java603
1 files changed, 603 insertions, 0 deletions
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java
new file mode 100644
index 00000000000..da95fcc306b
--- /dev/null
+++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/contentassist/CompletionProposalPopup.java
@@ -0,0 +1,603 @@
+/**********************************************************************
+Copyright (c) 2000, 2002 IBM Corp. and others.
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Common Public License v1.0
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/cpl-v10.html
+
+Contributors:
+ IBM Corporation - Initial implementation
+**********************************************************************/
+
+package org.eclipse.jface.text.contentassist;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.BusyIndicator;
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+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.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextViewer;
+
+
+
+/**
+ * This class is used to present proposals to the user. If additional
+ * information exists for a proposal, then selecting that proposal
+ * will result in the information being displayed in a secondary
+ * window.
+ *
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal
+ */
+class CompletionProposalPopup implements IContentAssistListener {
+
+ private ITextViewer fViewer;
+ private ContentAssistant fContentAssistant;
+ private AdditionalInfoController fAdditionalInfoController;
+
+ private PopupCloser fPopupCloser= new PopupCloser();
+ private Shell fProposalShell;
+ private Table fProposalTable;
+ private boolean fInserting= false;
+
+ private long fInvocationCounter= 0;
+ private ICompletionProposal[] fFilteredProposals;
+ private ICompletionProposal[] fComputedProposals;
+ private int fInvocationOffset;
+ private int fFilterOffset;
+
+ private String fLineDelimiter;
+
+
+ /**
+ * Creates a new completion proposal popup for the given elements.
+ *
+ * @param contentAssistant the content assistant feeding this popup
+ * @param viewer the viewer on top of which this popup appears
+ * @param infoController the info control collaborating with this popup
+ * @since 2.0
+ */
+ public CompletionProposalPopup(ContentAssistant contentAssistant, ITextViewer viewer, AdditionalInfoController infoController) {
+ fContentAssistant= contentAssistant;
+ fViewer= viewer;
+ fAdditionalInfoController= infoController;
+ }
+
+ /**
+ * Computes and presents completion proposals. The flag indicates whether this call has
+ * be made out of an auto activation context.
+ *
+ * @param autoActivated <code>true</code> if auto activation context
+ * @return an error message or <code>null</code> in case of no error
+ */
+ public String showProposals(final boolean autoActivated) {
+ final StyledText styledText= fViewer.getTextWidget();
+ BusyIndicator.showWhile(styledText.getDisplay(), new Runnable() {
+ public void run() {
+
+
+ fInvocationOffset= fViewer.getSelectedRange().x;
+ fComputedProposals= computeProposals(fInvocationOffset);
+
+
+ int count= (fComputedProposals == null ? 0 : fComputedProposals.length);
+ if (count == 0) {
+
+ if (!autoActivated)
+ styledText.getDisplay().beep();
+
+ } else {
+
+ if (count == 1 && !autoActivated && fContentAssistant.isAutoInserting())
+
+ insertProposal(fComputedProposals[0], (char) 0, fInvocationOffset);
+
+ else {
+
+ if (fLineDelimiter == null)
+ fLineDelimiter= styledText.getLineDelimiter();
+
+ createProposalSelector();
+ setProposals(fComputedProposals);
+ displayProposals();
+ }
+ }
+ }
+ });
+
+ return getErrorMessage();
+ }
+
+ /**
+ * Returns the completion proposal available at the given offset of the
+ * viewer's document. Delegates the work to the content assistant.
+ *
+ * @param offset the offset
+ * @return the completion proposals available at this offset
+ */
+ private ICompletionProposal[] computeProposals(int offset) {
+ return fContentAssistant.computeCompletionProposals(fViewer, offset);
+ }
+
+ /**
+ * Returns the error message.
+ *
+ * @return the error message
+ */
+ private String getErrorMessage() {
+ return fContentAssistant.getErrorMessage();
+ }
+
+ /**
+ * Creates the proposal selector.
+ */
+ private void createProposalSelector() {
+ if (Helper.okToUse(fProposalShell))
+ return;
+
+ Control control= fViewer.getTextWidget();
+ fProposalShell= new Shell(control.getShell(), SWT.ON_TOP | SWT.RESIZE );
+ fProposalTable= new Table(fProposalShell, SWT.H_SCROLL | SWT.V_SCROLL);
+
+ fProposalTable.setLocation(0, 0);
+ fAdditionalInfoController.setSizeConstraints(50, 10, true, false);
+
+ GridLayout layout= new GridLayout();
+ layout.marginWidth= 0;
+ layout.marginHeight= 0;
+ fProposalShell.setLayout(layout);
+
+ GridData data= new GridData(GridData.FILL_BOTH);
+ data.heightHint= fProposalTable.getItemHeight() * 10;
+ data.widthHint= 300;
+ fProposalTable.setLayoutData(data);
+
+ fProposalShell.pack();
+
+ fProposalShell.addControlListener(new ControlListener() {
+
+ public void controlMoved(ControlEvent e) {}
+
+ public void controlResized(ControlEvent e) {
+ // resets the cached resize constraints
+ fAdditionalInfoController.setSizeConstraints(50, 10, true, false);
+ }
+ });
+
+
+ fProposalShell.setBackground(control.getDisplay().getSystemColor(SWT.COLOR_BLACK));
+
+ Color c= fContentAssistant.getProposalSelectorBackground();
+ if (c == null)
+ c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND);
+ fProposalTable.setBackground(c);
+
+ c= fContentAssistant.getProposalSelectorForeground();
+ if (c == null)
+ c= control.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND);
+ fProposalTable.setForeground(c);
+
+ fProposalTable.addSelectionListener(new SelectionListener() {
+ public void widgetSelected(SelectionEvent e) {
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ selectProposal();
+ }
+ });
+
+ fPopupCloser.install(fContentAssistant, fProposalTable);
+
+ fProposalTable.setHeaderVisible(false);
+ fContentAssistant.addToLayout(this, fProposalShell, ContentAssistant.LayoutManager.LAYOUT_PROPOSAL_SELECTOR, fContentAssistant.getSelectionOffset());
+ }
+
+ /**
+ * Returns the proposal selected in the proposal selector.
+ *
+ * @return the selected proposal
+ * @since 2.0
+ */
+ private ICompletionProposal getSelectedProposal() {
+ int i= fProposalTable.getSelectionIndex();
+ if (i < 0 || i >= fFilteredProposals.length)
+ return null;
+ return fFilteredProposals[i];
+ }
+
+ /**
+ * Takes the selected proposal and applies it.
+ * @since 2.0
+ */
+ private void selectProposal() {
+ ICompletionProposal p= getSelectedProposal();
+ hide();
+ if (p != null)
+ insertProposal(p, (char) 0, fViewer.getSelectedRange().x);
+ }
+
+ /**
+ * Applies the given proposal at the given offset. The given character is the
+ * one that triggered the insertion of this proposal.
+ *
+ * @param p the completion proposal
+ * @param trigger the trigger character
+ * @param offset the offset
+ *
+ * @since 2.0
+ */
+ private void insertProposal(ICompletionProposal p, char trigger, int offset) {
+
+ fInserting= true;
+
+ try {
+ IDocument document= fViewer.getDocument();
+
+ if (p instanceof ICompletionProposalExtension) {
+ ICompletionProposalExtension e= (ICompletionProposalExtension) p;
+ e.apply(document, trigger, offset);
+ } else {
+ p.apply(document);
+ }
+
+ Point selection= p.getSelection(document);
+ if (selection != null) {
+ fViewer.setSelectedRange(selection.x, selection.y);
+ fViewer.revealRange(selection.x, selection.y);
+ }
+
+ IContextInformation info= p.getContextInformation();
+ if (info != null) {
+
+ int position;
+ if (p instanceof ICompletionProposalExtension) {
+ ICompletionProposalExtension e= (ICompletionProposalExtension) p;
+ position= e.getContextInformationPosition();
+ } else {
+ if (selection == null)
+ selection= fViewer.getSelectedRange();
+ position= selection.x + selection.y;
+ }
+
+ fContentAssistant.showContextInformation(info, position);
+ }
+
+ } finally {
+ fInserting= false;
+ }
+
+ }
+
+ /**
+ * Returns whether this popup has the focus.
+ * @return <code>true</code> if the popup has the focus
+ */
+ public boolean hasFocus() {
+ if (Helper.okToUse(fProposalShell))
+ return (fProposalShell.isFocusControl() || fProposalTable.isFocusControl());
+
+ return false;
+ }
+
+ /**
+ * Hides this popup.
+ */
+ public void hide() {
+ if (Helper.okToUse(fProposalShell)) {
+
+ fContentAssistant.removeContentAssistListener(this, ContentAssistant.PROPOSAL_SELECTOR);
+
+ fPopupCloser.uninstall();
+ fProposalShell.setVisible(false);
+ fProposalShell.dispose();
+ fProposalShell= null;
+ }
+
+ fFilteredProposals= null;
+ }
+
+ /**
+ *Returns whether this popup is active. It is active if the propsal selector is visible.
+ * @return <code>true</code> if this popup is active
+ */
+ public boolean isActive() {
+ return fProposalShell != null && !fProposalShell.isDisposed();
+ }
+
+ /**
+ * Initializes the proposal selector with these given proposals.
+ *
+ * @param proposals the proposals
+ */
+ private void setProposals(ICompletionProposal[] proposals) {
+ if (Helper.okToUse(fProposalTable)) {
+
+ fFilteredProposals= proposals;
+
+ fProposalTable.setRedraw(false);
+ fProposalTable.removeAll();
+
+ TableItem item;
+ ICompletionProposal p;
+ for (int i= 0; i < proposals.length; i++) {
+ p= proposals[i];
+ item= new TableItem(fProposalTable, SWT.NULL);
+ if (p.getImage() != null)
+ item.setImage(p.getImage());
+ item.setText(p.getDisplayString());
+ item.setData(p);
+ }
+
+ Point currentLocation= fProposalShell.getLocation();
+ Point newLocation= getLocation();
+ if ((newLocation.x < currentLocation.x && newLocation.y == currentLocation.y) || newLocation.y < currentLocation.y)
+ fProposalShell.setLocation(newLocation);
+
+ selectProposal(0);
+ fProposalTable.setRedraw(true);
+ }
+ }
+
+ /**
+ * Returns the graphical location at which this popup should be made visible.
+ * @return the location of this popup
+ */
+ private Point getLocation() {
+ StyledText text= fViewer.getTextWidget();
+ int caret= text.getCaretOffset();
+ Point p= text.getLocationAtOffset(caret);
+ p= new Point(p.x, p.y + text.getLineHeight());
+ return text.toDisplay(p);
+ }
+
+ /**
+ *Displays this popup and install the additional info controller, so that additional info
+ * is displayed when a proposal is selected and additional info is available.
+ */
+ private void displayProposals() {
+ if (fContentAssistant.addContentAssistListener(this, ContentAssistant.PROPOSAL_SELECTOR)) {
+ fProposalShell.setVisible(true);
+ if (fAdditionalInfoController != null) {
+ fAdditionalInfoController.install(fProposalTable);
+ fAdditionalInfoController.handleTableSelectionChanged();
+ }
+ }
+ }
+
+ /*
+ * @see IContentAssistListener#verifyKey(VerifyEvent)
+ */
+ public boolean verifyKey(VerifyEvent e) {
+ if (!Helper.okToUse(fProposalShell))
+ return true;
+
+ char key= e.character;
+ if (key == 0) {
+ int newSelection= fProposalTable.getSelectionIndex();
+ int visibleRows= (fProposalTable.getSize().y / fProposalTable.getItemHeight()) - 1;
+ switch (e.keyCode) {
+
+ case SWT.ARROW_LEFT :
+ case SWT.ARROW_RIGHT :
+ filterProposal();
+ return true;
+
+ case SWT.ARROW_UP :
+ newSelection -= 1;
+ if (newSelection < 0)
+ newSelection= fProposalTable.getItemCount() - 1;
+ break;
+
+ case SWT.ARROW_DOWN :
+ newSelection += 1;
+ if (newSelection > fProposalTable.getItemCount() - 1)
+ newSelection= 0;
+ break;
+
+ case SWT.PAGE_DOWN :
+ newSelection += visibleRows;
+ if (newSelection >= fProposalTable.getItemCount())
+ newSelection= fProposalTable.getItemCount() - 1;
+ break;
+
+ case SWT.PAGE_UP :
+ newSelection -= visibleRows;
+ if (newSelection < 0)
+ newSelection= 0;
+ break;
+
+ case SWT.HOME :
+ newSelection= 0;
+ break;
+
+ case SWT.END :
+ newSelection= fProposalTable.getItemCount() - 1;
+ break;
+
+ case SWT.CTRL :
+ case SWT.SHIFT :
+ return true;
+
+ default :
+ hide();
+ return true;
+ }
+
+ selectProposal(newSelection);
+
+ e.doit= false;
+ return false;
+
+ } else {
+
+ switch (key) {
+ case 0x1B : // Esc
+ e.doit= false;
+ hide();
+ break;
+
+ case 0x0D : // Enter
+ e.doit= false;
+ selectProposal();
+ break;
+
+ default:
+
+ if ('\t' == key) {
+ e.doit= false;
+ fProposalShell.setFocus();
+ return false;
+ }
+
+ ICompletionProposal p= getSelectedProposal();
+ if (p instanceof ICompletionProposalExtension) {
+ ICompletionProposalExtension t= (ICompletionProposalExtension) p;
+ char[] triggers= t.getTriggerCharacters();
+ if (contains(triggers, key)) {
+ e.doit= false;
+ hide();
+ insertProposal(p, key, fViewer.getSelectedRange().x);
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Selects the entry with the given index in the proposal selector and feeds
+ * the selection to the additional info controller.
+ *
+ * @param index the index in the list
+ * @since 2.0
+ */
+ private void selectProposal(int index) {
+ fProposalTable.setSelection(index);
+ fProposalTable.showSelection();
+ if (fAdditionalInfoController != null)
+ fAdditionalInfoController.handleTableSelectionChanged();
+ }
+
+ /**
+ * Returns whether the given character is contained in the given array of
+ * characters.
+ *
+ * @param characters the list of characters
+ * @param c the character to look for in the list
+ * @return <code>true</code> if character belongs to the list
+ * @since 2.0
+ */
+ private boolean contains(char[] characters, char c) {
+
+ if (characters == null)
+ return false;
+
+ for (int i= 0; i < characters.length; i++) {
+ if (c == characters[i])
+ return true;
+ }
+
+ return false;
+ }
+
+ /*
+ * @see IEventConsumer#processEvent(VerifyEvent)
+ */
+ public void processEvent(VerifyEvent e) {
+ if (!fInserting)
+ filterProposal();
+ }
+
+ /**
+ * Filters the displayed proposal based on the given cursor position and the
+ * offset of the original invocation of the content assistant.
+ */
+ private void filterProposal() {
+ ++ fInvocationCounter;
+ Control control= fViewer.getTextWidget();
+ control.getDisplay().asyncExec(new Runnable() {
+ long fCounter= fInvocationCounter;
+ public void run() {
+
+ if (fCounter != fInvocationCounter) return;
+
+ int offset= fViewer.getSelectedRange().x;
+ ICompletionProposal[] proposals= (offset == -1 ? null : computeFilteredProposals(offset));
+ fFilterOffset= offset;
+
+ if (proposals != null && proposals.length > 0)
+ setProposals(proposals);
+ else
+ hide();
+ }
+ });
+ }
+
+ /**
+ * Computes the subset of already computed propsals that are still valid for
+ * the given offset.
+ *
+ * @param offset the offset
+ * @return the set of filtered proposals
+ * @since 2.0
+ */
+ private ICompletionProposal[] computeFilteredProposals(int offset) {
+
+ if (offset == fInvocationOffset)
+ return fComputedProposals;
+
+ if (offset < fInvocationOffset) {
+ fInvocationOffset= offset;
+ fComputedProposals= computeProposals(fInvocationOffset);
+ return fComputedProposals;
+ }
+
+ ICompletionProposal[] proposals= fComputedProposals;
+ if (offset > fFilterOffset)
+ proposals= fFilteredProposals;
+
+ if (proposals == null)
+ return null;
+
+ IDocument document= fViewer.getDocument();
+ int length= proposals.length;
+ List filtered= new ArrayList(length);
+ for (int i= 0; i < length; i++) {
+ if (proposals[i] instanceof ICompletionProposalExtension) {
+
+ ICompletionProposalExtension p= (ICompletionProposalExtension) proposals[i];
+ if (p.isValidFor(document, offset))
+ filtered.add(p);
+
+ } else {
+ // restore original behavior
+ fInvocationOffset= offset;
+ fComputedProposals= computeProposals(fInvocationOffset);
+ return fComputedProposals;
+ }
+ }
+
+ ICompletionProposal[] p= new ICompletionProposal[filtered.size()];
+ filtered.toArray(p);
+ return p;
+ }
+}
+
+

Back to the top