Package org.eclipse.wst.sse.ui.contentassist

Source Code of org.eclipse.wst.sse.ui.contentassist.StructuredContentAssistProcessor$CompletionListener

/*******************************************************************************
* Copyright (c) 2010, 2011 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.wst.sse.ui.contentassist;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.action.LegacyActionTools;
import org.eclipse.jface.bindings.TriggerSequence;
import org.eclipse.jface.bindings.keys.KeySequence;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextInputListener;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.ContentAssistEvent;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.ICompletionListener;
import org.eclipse.jface.text.contentassist.ICompletionListenerExtension;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContentAssistantExtension2;
import org.eclipse.jface.text.contentassist.IContentAssistantExtension3;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.ui.internal.ExtendedConfigurationBuilder;
import org.eclipse.wst.sse.ui.internal.IReleasable;
import org.eclipse.wst.sse.ui.internal.SSEUIMessages;
import org.eclipse.wst.sse.ui.internal.SSEUIPlugin;
import org.eclipse.wst.sse.ui.internal.contentassist.CompletionProposalCategory;
import org.eclipse.wst.sse.ui.internal.contentassist.CompletionProposalComputerRegistry;
import org.eclipse.wst.sse.ui.internal.contentassist.CompletionProposoalCatigoriesConfigurationRegistry;
import org.eclipse.wst.sse.ui.internal.contentassist.CompoundContentAssistProcessor;
import org.eclipse.wst.sse.ui.internal.contentassist.ContextInformationValidator;
import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal;
import org.eclipse.wst.sse.ui.internal.contentassist.OptionalMessageDialog;
import org.eclipse.wst.sse.ui.preferences.ICompletionProposalCategoriesConfigurationReader;
import org.eclipse.wst.sse.ui.preferences.ICompletionProposalCategoriesConfigurationWriter;


/**
* <p>A content assist processor that aggregates the proposals of the
* {@link org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer}s contributed via the
* <code>org.eclipse.wst.sse.ui.completionProposal</code> extension point.</p>
* <p>
* Extenders may extend:
* <ul>
* <li>{@link #propertyChange(PropertyChangeEvent)}to react to property change events
* that occur in the {@link IPreferenceStore} given to the constructor in case the behavior
* of the processor needs to change according to user preferences</li>
* <li>{@link #getCompletionProposalAutoActivationCharacters()}</li>
* <li>{@link #getContextInformationAutoActivationCharacters()}</li>
* <li>{@link #filterAndSortProposals(List, IProgressMonitor, CompletionProposalInvocationContext)}
* to add sorting and filtering</li>
* <li>{@link #filterAndSortContextInformation(List, IProgressMonitor)} to add sorting and filtering</li>
* <li>{@link #createProgressMonitor()} to change the way progress is reported</li>
* <li>{@link #createContext(ITextViewer, int)} to provide the context object
* passed to the computers</li>
* <li>{@link #getContextInformationValidator()} to add context validation (needed if any
* contexts are provided)</li>
* <li>{@link #getErrorMessage()} to change error reporting</li>
* </ul>
* </p>
*
* @base org.eclipse.jdt.internal.ui.text.java.ContentAssistProcessor
*/
public class StructuredContentAssistProcessor implements IContentAssistProcessor, IPropertyChangeListener, IReleasable {

  /** Legacy editor configuration extension point. */
  private static final String CONTENT_ASSIST_PROCESSOR_EXTENDED_ID = "contentassistprocessor"; //$NON-NLS-1$

  /** Content assist processors added through the now legacy editor configuration extension point */
  private List fLegacyExtendedContentAssistProcessors;
 
  /**
   * Dialog settings key for the "all categories are disabled" warning dialog. See
   * {@link OptionalMessageDialog}.
   */
  private static final String PREF_WARN_ABOUT_EMPTY_ASSIST_CATEGORY= "EmptyDefaultAssistCategory"; //$NON-NLS-1$

  /**
   * Used to sort categories by their page order so they are cycled in the correct order
   */
  private final Comparator PAGE_ORDER_COMPARATOR = new Comparator() {
    public int compare(Object o1, Object o2) {
      CompletionProposalCategory d1= (CompletionProposalCategory) o1;
      CompletionProposalCategory d2= (CompletionProposalCategory) o2;

      return d1.getPageSortRank(fContentTypeID) - d2.getPageSortRank(fContentTypeID);
    }
  };
 
  /**
   * Used to sort categories by their default page order so they are
   * ordered correctly on the default page
   */
  private final Comparator DEFAULT_PAGE_ORDER_COMPARATOR = new Comparator() {
    public int compare(Object o1, Object o2) {
      CompletionProposalCategory d1= (CompletionProposalCategory) o1;
      CompletionProposalCategory d2= (CompletionProposalCategory) o2;

      return d1.getDefaultPageSortRank(fContentTypeID) - d2.getDefaultPageSortRank(fContentTypeID);
    }
  };

  /** List of {@link CompletionProposalCategory}s supported by this processor */
  private List fCategories;
 
  /** content type ID this processor is associated with */
  String fContentTypeID;
 
  /** partition type ID this processor is associated with */
  private final String fPartitionTypeID;
 
  /** Content assistant used for giving the user status messages and listening to completion results*/
  private ContentAssistant fAssistant;

  /* cycling stuff */
  private int fRepetition= -1;
  private List fCategoryIteration = null;
  private String fIterationGesture = null;
  private int fNumberOfComputedResults = 0;
  private String fErrorMessage;
 
  /** Optionally specified preference store for listening to property change events */
  private IPreferenceStore fPreferenceStore;
 
  /** The viewer this processor is associated with */
  private ITextViewer fViewer;

  /**
   * the {@link ITextInputListener} used to set the content type when
   * a document is set for this processors associated viewer.
   */
  private ITextInputListener fTextInputListener;

  private CompletionListener fCompletionListener;
 
  /** the context information validator for this processor */
  private IContextInformationValidator fContextInformationValidator;
 
  private AutoActivationDelegate fAutoActivation;

  /**
   * <p>Create a new content assist processor for a specific partition type.
   * The content type will be determined when a document is set on the viewer</p>
   *
   * <p>If the given {@link IPreferenceStore} is not <code>null</code> then this
   * processor will be registered as a {@link IPropertyChangeListener} on the given store
   * so that implementers of this class can change the way the processor acts based on
   * user preferences</p>
   *
   * @param assistant {@link ContentAssistant} to use
   * @param partitionTypeID the partition type this processor is for
   * @param viewer {@link ITextViewer} this processor is acting in
   * @param preferenceStore This processor will be registered as a {@link IPropertyChangeListener}
   * on this store and the processor itself will take care of removing itself as a listener, if
   * <code>null</code> then will not be registered as a {@link IPropertyChangeListener}
   */
  public StructuredContentAssistProcessor(ContentAssistant assistant, String partitionTypeID,
      ITextViewer viewer, IPreferenceStore preferenceStore) {
   
    Assert.isNotNull(partitionTypeID);
    Assert.isNotNull(assistant);
   
    //be sure the registry has been loaded, none blocking
    CompletionProposalComputerRegistry.getDefault().initialize();
   
    //register on the preference store
    this.fPreferenceStore = preferenceStore;
    if(this.fPreferenceStore != null) {
      this.fPreferenceStore.addPropertyChangeListener(this);
    }
   
    //The content type can not be determined until a document has been set
    this.fContentTypeID = null;
    this.fViewer = viewer;
    if(viewer != null) {
      this.fTextInputListener = new TextInputListener();
      viewer.addTextInputListener(this.fTextInputListener);
   
      if(viewer.getDocument() != null) {
        /* it is highly unlike the document has already been set, but check
         * just for sanity
         */
        this.fTextInputListener.inputDocumentChanged(null, viewer.getDocument());
      }
    }
   
    //set the associated partition type
    this.fPartitionTypeID = partitionTypeID;
   
    //add completion listener
    fAssistant = assistant;
    fCompletionListener = new CompletionListener();
    fAssistant.addCompletionListener(fCompletionListener);
   
    //lazy load these to speed up initial editor opening
    fLegacyExtendedContentAssistProcessors = null;
    fCategories = null;
  }

  /**
   * <p>Collect the proposals using the extension points</p>
   *
   * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
   */
  public final ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
    clearState();

    IProgressMonitor monitor= createProgressMonitor();
    monitor.beginTask(SSEUIMessages.ContentAssist_computing_proposals, getProposalCategories().size() + 1);

    CompletionProposalInvocationContext context = createContext(viewer, offset);

    monitor.subTask(SSEUIMessages.ContentAssist_collecting_proposals);
    List proposals = collectProposals(viewer, offset, monitor, context);

    monitor.subTask(SSEUIMessages.ContentAssist_sorting_proposals);
    List filtered = filterAndSortProposals(proposals, monitor, context);
    fNumberOfComputedResults= filtered.size();

    ICompletionProposal[] result= (ICompletionProposal[]) filtered.toArray(new ICompletionProposal[filtered.size()]);
    monitor.done();

    return result;
  }

  /**
   * <p>Collect the context information using the extension points</p>
   *
   * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int)
   */
  public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
    clearState();

    IProgressMonitor monitor= createProgressMonitor();
    monitor.beginTask(SSEUIMessages.ContentAssist_computing_contexts, getProposalCategories().size() + 1);

    monitor.subTask(SSEUIMessages.ContentAssist_collecting_contexts);
    List proposals= collectContextInformation(viewer, offset, monitor);

    monitor.subTask(SSEUIMessages.ContentAssist_sorting_contexts);
    List filtered= filterAndSortContextInformation(proposals, monitor);
    fNumberOfComputedResults= filtered.size();

    IContextInformation[] result= (IContextInformation[]) filtered.toArray(new IContextInformation[filtered.size()]);
    monitor.done();
    return result;
  }

  /**
   * <p>Default implementation is to return <code>null</code></p>
   * <p>Extenders may override</p>
   *
   * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
   */
  public char[] getCompletionProposalAutoActivationCharacters() {
    return (fAutoActivation != null) ? fAutoActivation.getCompletionProposalAutoActivationCharacters() : null;
  }

  /**
   * <p>Default implementation is to return <code>null</code></p>
   * <p>Extenders may override</p>
   *
   * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters()
   */
  public char[] getContextInformationAutoActivationCharacters() {
    return (fAutoActivation != null) ? fAutoActivation.getContextInformationAutoActivationCharacters() : null;
  }

  /**
   * <p>Extenders may override this function to change error reporting</p>
   *
   * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage()
   */
  public String getErrorMessage() {
    if (fErrorMessage != null)
      return fErrorMessage;
    if (fNumberOfComputedResults > 0)
      return null;
    return SSEUIMessages.ContentAssist_no_completions;
  }

  /**
   * @see org.eclipse.wst.sse.ui.contentassist.StructuredContentAssistProcessor#getContextInformationValidator()
   */
  public IContextInformationValidator getContextInformationValidator() {
    if (this.fContextInformationValidator == null) {
      this.fContextInformationValidator = new ContextInformationValidator();
    }
    return this.fContextInformationValidator;
  }
 
  public void install(ITextViewer viewer) {
    if (fPreferenceStore != null) {
      fPreferenceStore.addPropertyChangeListener(this);
    }
    if (fViewer != null) {
      fViewer.removeTextInputListener(fTextInputListener);
    }
    fViewer = viewer;
    if (fViewer != null) {
      fViewer.addTextInputListener(fTextInputListener);
    }
    if (fAssistant != null) {
      fAssistant.addCompletionListener(fCompletionListener);
    }
  }

  /**
   * <p>Extenders may override, but should always be sure to call the super implementation</p>
   *
   * @see org.eclipse.wst.sse.ui.internal.IReleasable#release()
   */
  public void release() {
    if (fAutoActivation != null) {
      fAutoActivation.dispose();
      fAutoActivation = null;
    }
    if(this.fPreferenceStore != null) {
      this.fPreferenceStore.removePropertyChangeListener(this);
    }
   
    if(this.fViewer != null) {
      this.fViewer.removeTextInputListener(this.fTextInputListener);
      this.fViewer = null;
    }
    if (this.fAssistant != null) {
      this.fAssistant.removeCompletionListener(fCompletionListener);
    }
  }
 
  /**
   * <p>Intended to be overridden by extenders wishing to change the behavior
   * of the processor based on user preferences from the store optionally
   * associated with this processor.  If no store was given to the constructor
   * when creating this assistant then this method will never be invoked.</p>
   *
   * <p>The default implementation does not react to the events in any way</p>
   *
   * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
   */
  public void propertyChange(PropertyChangeEvent event) {
  }
 
  /**
   * <p>Filters and sorts the proposals. The passed list may be modified
   * and returned, or a new list may be created and returned.</p>
   *
   * <p>The default implementation does not do any sorting or filtering.</p>
   * <p>Extenders may override this function.</p>
   *
   * @param proposals the list of collected proposals (element type:
   *        {@link ICompletionProposal})
   * @param monitor a progress monitor
   * @param context TODO
   * @return the list of filtered and sorted proposals, ready for
   *         display (element type: {@link ICompletionProposal})
   */
  protected List filterAndSortProposals(List proposals, IProgressMonitor monitor, CompletionProposalInvocationContext context) {
    for(int i=0; i<proposals.size(); i++){
      if(proposals.get(i) instanceof CustomCompletionProposal){
        CustomCompletionProposal ccp = (CustomCompletionProposal) proposals.get(i);
        if(ccp.getDisplayString().startsWith("t:"))
          ccp.setImage(tapestryImage);
      }
    }
    return proposals;
  }
 
  private Image tapestryImage = ImageDescriptor.createFromURL(Platform.getBundle("org.eclipse.jst.tapestry.ui").getEntry("icons/tapestry.gif")).createImage();
 
  /**
   * <p>Filters and sorts the context information objects. The passed
   * list may be modified and returned, or a new list may be created
   * and returned.</p>
   *
   * <p>The default implementation does not do any sorting or filtering</p>
   * <p>Extenders may override this method</p>
   *
   * @param contexts the list of collected proposals (element type:
   *        {@link IContextInformation})
   * @param monitor a progress monitor
   * @return the list of filtered and sorted proposals, ready for
   *         display (element type: {@link IContextInformation})
   */
  protected List filterAndSortContextInformation(List contexts, IProgressMonitor monitor) {
    return contexts;
  }

  /**
   * <p>Creates a progress monitor.</p>
   * <p>The default implementation creates a {@link NullProgressMonitor}.</p>
   *
   * <p>Extenders may override this method</p>
   *
   * @return a progress monitor
   */
  protected IProgressMonitor createProgressMonitor() {
    return new NullProgressMonitor();
  }

  /**
   * <p>Creates the context that is passed to the completion proposal
   * computers.</p>
   *
   * <p>Extenders may override this method</p>
   *
   * @param viewer the viewer that content assist is invoked on
   * @param offset the content assist offset
   * @return the context to be passed to the computers
   */
  protected CompletionProposalInvocationContext createContext(ITextViewer viewer, int offset) {
    return new CompletionProposalInvocationContext(viewer, offset);
  }
 
  /**
   * @return the associated preference store
   */
  protected IPreferenceStore getPreferenceStore() {
    return this.fPreferenceStore;
  }
 
  /**
   * Clears the state
   */
  private void clearState() {
    fErrorMessage=null;
    fNumberOfComputedResults= 0;
  }

  /**
   * <p>Collects the proposals from the extensions.</p>
   *
   * @param viewer the text viewer
   * @param offset the offset
   * @param monitor the progress monitor
   * @param context the code assist invocation context
   * @return the list of proposals
   */
  private List collectProposals(ITextViewer viewer, int offset, IProgressMonitor monitor, CompletionProposalInvocationContext context) {
    List proposals = new ArrayList();
    List categories = getCategories();
    for (Iterator it = categories.iterator(); it.hasNext();) {
      CompletionProposalCategory cat = (CompletionProposalCategory) it.next();
      List computed = cat.computeCompletionProposals(context, this.fContentTypeID, this.fPartitionTypeID, new SubProgressMonitor(monitor, 1));
      proposals.addAll(computed);
      if (fErrorMessage == null) {
        fErrorMessage= cat.getErrorMessage();
      }
    }
   
    // if default page
    // Deal with adding in proposals from processors added through the legacy extension
    if (isFirstPage() && getLegacyExtendedContentAssistProcessors() != null &&
        !getLegacyExtendedContentAssistProcessors().isEmpty()) {
     
      Iterator iter = getLegacyExtendedContentAssistProcessors().iterator();
      while (iter.hasNext()) {
        IContentAssistProcessor legacyProcessor = (IContentAssistProcessor) iter.next();
        ICompletionProposal[] legacyComputed = legacyProcessor.computeCompletionProposals(viewer, offset);
        if (legacyComputed != null) {
          proposals.addAll(Arrays.asList(legacyComputed));
        }
      }
    }
   
    return proposals;
  }
 
  /**
   * <p>Collects the context information from the extensions.</p>
   *
   * @param viewer
   * @param offset
   * @param monitor
   * @return
   */
  private List collectContextInformation(ITextViewer viewer, int offset, IProgressMonitor monitor) {
    List proposals = new ArrayList();
    CompletionProposalInvocationContext context= createContext(viewer, offset);

    List providers= getCategories();
    for (Iterator it= providers.iterator(); it.hasNext();) {
      CompletionProposalCategory cat= (CompletionProposalCategory) it.next();
      List computed= cat.computeContextInformation(context, this.fContentTypeID, this.fPartitionTypeID, new SubProgressMonitor(monitor, 1));
      proposals.addAll(computed);
      if (fErrorMessage == null) {
        fErrorMessage= cat.getErrorMessage();
      }
    }
   
    // Deal with adding in contexts from processors added through the legacy extension
    if (getLegacyExtendedContentAssistProcessors() != null &&
        !getLegacyExtendedContentAssistProcessors().isEmpty()) {
     
      Iterator iter = getLegacyExtendedContentAssistProcessors().iterator();
      while (iter.hasNext()) {
        IContentAssistProcessor legacyProcessor = (IContentAssistProcessor) iter.next();
        IContextInformation[] legacyComputed = legacyProcessor.computeContextInformation(viewer, offset);
        if(legacyComputed != null) {
          proposals.addAll(Arrays.asList(legacyComputed));
        }
      }
    }

    return proposals;
  }

  /**
   * @return the next set of categories
   */
  private List getCategories() {
    List categories;
    if (fCategoryIteration == null) {
      categories =  getProposalCategories();
    } else {
      int iteration= fRepetition % fCategoryIteration.size();
      fAssistant.setStatusMessage(createIterationMessage());
      fAssistant.setEmptyMessage(createEmptyMessage());
      fRepetition++;
 
      categories = (List) fCategoryIteration.get(iteration);
    }
   
    return categories;
  }

  /**
   *  This may show the warning dialog if all categories are disabled
   */
  private void resetCategoryIteration() {
    fCategoryIteration = getCategoryIteration();
  }

  /**
   * @return {@link List} of {@link List}s of {@link CompletionProposalCategory}s, this is
   * the ordered list of the completion categories to cycle through
   */
  private List getCategoryIteration() {
    List sequence= new ArrayList();
    sequence.add(getDefaultCategories());
    for (Iterator it= getSortedOwnPageCategories().iterator(); it.hasNext();) {
      CompletionProposalCategory cat= (CompletionProposalCategory) it.next();
      sequence.add(Collections.singletonList(cat));
    }
    return sequence;
  }

  /**
   * @return the sorted categories for the default page
   */
  private List getDefaultCategories() {
    // default mix - enable all included computers
    List included= getDefaultCategoriesUnchecked();

    if (included.size() == 0 && CompletionProposalComputerRegistry.getDefault().hasUninstalledComputers()) {
      if (informUserAboutEmptyDefaultCategory()) {
        // preferences were restored - recompute the default categories
        included= getDefaultCategoriesUnchecked();
      }
      CompletionProposalComputerRegistry.getDefault().resetUnistalledComputers();
    }
   
    Collections.sort(included, DEFAULT_PAGE_ORDER_COMPARATOR);

    return included;
  }

  /**
   * <p>Gets the default categories with no error checking.</p>
   *
   * @return the default {@link CompletionProposalCategory}s
   */
  private List getDefaultCategoriesUnchecked() {
    List included = new ArrayList();
    for (Iterator it = getProposalCategories().iterator(); it.hasNext();) {
      CompletionProposalCategory category = (CompletionProposalCategory) it.next();
      if (category.isIncludedOnDefaultPage(this.fContentTypeID) && category.hasComputers(fContentTypeID, fPartitionTypeID))
        included.add(category);
    }
    return included;
  }

  /**
   * <p>Informs the user about the fact that there are no enabled categories in the default content
   * assist set and shows a link to the preferences.</p>
   *
   * @return  <code>true</code> if the default should be restored
   */
  private boolean informUserAboutEmptyDefaultCategory() {
    /*If warn about empty default category and there are associated properties for this
     * processors content type and those properties have an associated properties page then
     * display warning message to user.
     */
    ICompletionProposalCategoriesConfigurationReader properties = CompletionProposoalCatigoriesConfigurationRegistry.getDefault().getReadableConfiguration(this.fContentTypeID);
    if (OptionalMessageDialog.isDialogEnabled(PREF_WARN_ABOUT_EMPTY_ASSIST_CATEGORY) &&
        properties instanceof ICompletionProposalCategoriesConfigurationWriter  &&
        ((ICompletionProposalCategoriesConfigurationWriter)properties).hasAssociatedPropertiesPage()) {
     
      ICompletionProposalCategoriesConfigurationWriter propertiesExtension = (ICompletionProposalCategoriesConfigurationWriter)properties;
     
      final Shell shell= SSEUIPlugin.getActiveWorkbenchShell();
      String title= SSEUIMessages.ContentAssist_all_disabled_title;
      String message= SSEUIMessages.ContentAssist_all_disabled_message;
      // see PreferencePage#createControl for the 'defaults' label
      final String restoreButtonLabel= JFaceResources.getString("defaults"); //$NON-NLS-1$
      final String linkMessage= NLS.bind(SSEUIMessages.ContentAssist_all_disabled_preference_link, LegacyActionTools.removeMnemonics(restoreButtonLabel));
      final int restoreId= IDialogConstants.CLIENT_ID + 10;
      final int settingsId= IDialogConstants.CLIENT_ID + 11;
      final OptionalMessageDialog dialog= new OptionalMessageDialog(PREF_WARN_ABOUT_EMPTY_ASSIST_CATEGORY, shell, title, null /* default image */, message, MessageDialog.WARNING, new String[] { restoreButtonLabel, IDialogConstants.CLOSE_LABEL }, 1) {
        /*
         * @see org.eclipse.jdt.internal.ui.dialogs.OptionalMessageDialog#createCustomArea(org.eclipse.swt.widgets.Composite)
         */
        protected Control createCustomArea(Composite composite) {
          // wrap link and checkbox in one composite without space
          Composite parent= new Composite(composite, SWT.NONE);
          GridLayout layout= new GridLayout();
          layout.marginHeight= 0;
          layout.marginWidth= 0;
          layout.verticalSpacing= 0;
          parent.setLayout(layout);

          Composite linkComposite= new Composite(parent, SWT.NONE);
          layout= new GridLayout();
          layout.marginHeight= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
          layout.marginWidth= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
          layout.horizontalSpacing= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
          linkComposite.setLayout(layout);

              Link link= new Link(linkComposite, SWT.NONE);
              link.setText(linkMessage);
              link.addSelectionListener(new SelectionAdapter() {
                public void widgetSelected(SelectionEvent e) {
                  setReturnCode(settingsId);
                  close();
                }
              });
              GridData gridData= new GridData(SWT.FILL, SWT.BEGINNING, true, false);
              gridData.widthHint= this.getMinimumMessageWidth();
          link.setLayoutData(gridData);

          // create checkbox and "don't show this message" prompt
          super.createCustomArea(parent);

          return parent;
            }

        /*
         * @see org.eclipse.jface.dialogs.MessageDialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
         */
        protected void createButtonsForButtonBar(Composite parent) {
              Button[] buttons= new Button[2];
          buttons[0]= createButton(parent, restoreId, restoreButtonLabel, false);
              buttons[1]= createButton(parent, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, true);
              setButtons(buttons);
        }
          };
          int returnValue = dialog.open();
         
          //based on user actions either reset defaults or open preference dialog
          if (restoreId == returnValue || settingsId == returnValue) {
            if (restoreId == returnValue) {
              propertiesExtension.loadDefaults();
              propertiesExtension.saveConfiguration();
            }
            if (settingsId == returnValue) {
          PreferencesUtil.createPreferenceDialogOn(shell,
              propertiesExtension.getPropertiesPageID(), null, null).open();
            }
           
            return true;
          }
    }
    return false;
  }

  /**
   * @return a sorted {@link List} of {@link CompletionProposalCategory}s that
   * should be displayed on their own content assist page
   */
  private List getSortedOwnPageCategories() {
    ArrayList sorted= new ArrayList();
    for (Iterator it= getProposalCategories().iterator(); it.hasNext();) {
      CompletionProposalCategory category= (CompletionProposalCategory) it.next();
      if (category.isDisplayedOnOwnPage(this.fContentTypeID) &&
          category.hasComputers(fContentTypeID, fPartitionTypeID)) {
       
        sorted.add(category);
      }
    }
    Collections.sort(sorted, PAGE_ORDER_COMPARATOR);
    return sorted;
  }

  /**
   * @return a user message describing that there are no content assist suggestions for the current page
   */
  private String createEmptyMessage() {
    return NLS.bind(SSEUIMessages.ContentAssist_no_message, new String[]{getCategoryLabel(fRepetition)});
  }

  /**
   * @return user message describing what the next page of content assist holds
   */
  private String createIterationMessage() {
    return NLS.bind(SSEUIMessages.ContentAssist_toggle_affordance_update_message,
        new String[]{ getCategoryLabel(fRepetition), fIterationGesture, getCategoryLabel(fRepetition + 1) });
  }

  /**
   * @param repetition which category to get the label for
   * @return the label of the category
   */
  private String getCategoryLabel(int repetition) {
    int iteration= (fCategoryIteration != null ? repetition % fCategoryIteration.size() : 0);
    if (iteration == 0)
      return SSEUIMessages.ContentAssist_defaultProposalCategory_title;
    return ((CompletionProposalCategory) ((List) fCategoryIteration.get(iteration)).get(0)).getDisplayName();
  }

  /**
   * @return {@link String} representing the user command to iterate to the next page
   */
  private String getIterationGesture() {
    TriggerSequence binding= getIterationBinding();
    return binding != null ?
        NLS.bind(SSEUIMessages.ContentAssist_press, new Object[] { binding.format() })
        : SSEUIMessages.ContentAssist_click;
  }

  /**
   * @return {@link KeySequence} used by user to iterate to the next page
   */
  private KeySequence getIterationBinding() {
      final IBindingService bindingSvc= (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class);
    TriggerSequence binding= bindingSvc.getBestActiveBindingFor(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS);
    if (binding instanceof KeySequence)
      return (KeySequence) binding;
    return null;
    }
 
  /**
   * @return <code>true</code> if displaying first page, <code>false</code> otherwise
   */
  private boolean isFirstPage() {
    return fCategoryIteration == null || fCategoryIteration.size() == 1 || fRepetition % fCategoryIteration.size() == 1;
  }
 
  /**
   * <p><b>NOTE: </b>This method should be used over accessing the
   * {@link #fLegacyExtendedContentAssistProcessors} field directly so as to
   * facilitate the lazy initialization of the field.</p>
   *
   * @return the legacy extended content assist processors
   */
  private List getLegacyExtendedContentAssistProcessors() {
    if(fLegacyExtendedContentAssistProcessors == null) {
      fLegacyExtendedContentAssistProcessors =
        ExtendedConfigurationBuilder.getInstance().getConfigurations(
            CONTENT_ASSIST_PROCESSOR_EXTENDED_ID, fPartitionTypeID);
    }
   
    return fLegacyExtendedContentAssistProcessors;
  }
 
  /**
   * <p><b>NOTE: </b>This method should be used over accessing the {@link #fCategories}
   * field directly so as to facilitate the lazy initialization of the field.</p>
   *
   * @return the categories associated with the content type this processor is associated with
   */
  private List getProposalCategories() {
    if(fCategories == null) {
      fCategories =
        CompletionProposalComputerRegistry.getDefault().getProposalCategories(fContentTypeID);
    }
   
    return fCategories;
  }
 
  /**
   * The completion listener class for this processor.
   */
  private final class CompletionListener implements ICompletionListener, ICompletionListenerExtension {
    /**
     * @see org.eclipse.jface.text.contentassist.ICompletionListener#assistSessionStarted(org.eclipse.jface.text.contentassist.ContentAssistEvent)
     */
    public void assistSessionStarted(ContentAssistEvent event) {
      if (event.processor == StructuredContentAssistProcessor.this ||
          (event.processor instanceof CompoundContentAssistProcessor &&
              ((CompoundContentAssistProcessor)event.processor).containsProcessor(StructuredContentAssistProcessor.this))) {
       
        fIterationGesture= getIterationGesture();
        KeySequence binding= getIterationBinding();
 
        // This may show the warning dialog if all categories are disabled
        resetCategoryIteration();
        for (Iterator it= StructuredContentAssistProcessor.this.getProposalCategories().iterator(); it.hasNext();) {
          CompletionProposalCategory cat= (CompletionProposalCategory) it.next();
          cat.sessionStarted();
        }
 
        fRepetition= 0;
        if (event.assistant instanceof IContentAssistantExtension2) {
          IContentAssistantExtension2 extension= (IContentAssistantExtension2) event.assistant;
 
          if (fCategoryIteration.size() == 1) {
            extension.setRepeatedInvocationMode(false);
            extension.setShowEmptyList(false);
          } else {
            extension.setRepeatedInvocationMode(true);
            extension.setStatusLineVisible(true);
            extension.setStatusMessage(createIterationMessage());
            extension.setShowEmptyList(true);
            if (extension instanceof IContentAssistantExtension3) {
              IContentAssistantExtension3 ext3= (IContentAssistantExtension3) extension;
              ((ContentAssistant) ext3).setRepeatedInvocationTrigger(binding);
            }
          }
        }
      }
    }

    /**
     * @see org.eclipse.jface.text.contentassist.ICompletionListener#assistSessionEnded(org.eclipse.jface.text.contentassist.ContentAssistEvent)
     */
    public void assistSessionEnded(ContentAssistEvent event) {
      if (event.processor == StructuredContentAssistProcessor.this ||  (event.processor instanceof CompoundContentAssistProcessor && ((CompoundContentAssistProcessor)event.processor).containsProcessor(StructuredContentAssistProcessor.this))) {
        for (Iterator it= StructuredContentAssistProcessor.this.getProposalCategories().iterator(); it.hasNext();) {
          CompletionProposalCategory cat= (CompletionProposalCategory) it.next();
          cat.sessionEnded();
        }
 
        fCategoryIteration= null;
        fRepetition= -1;
        fIterationGesture= null;
        if (event.assistant instanceof IContentAssistantExtension2) {
          IContentAssistantExtension2 extension= (IContentAssistantExtension2) event.assistant;
          extension.setShowEmptyList(false);
          extension.setRepeatedInvocationMode(false);
          extension.setStatusLineVisible(false);
          if (extension instanceof IContentAssistantExtension3) {
            IContentAssistantExtension3 ext3= (IContentAssistantExtension3) extension;
            ((ContentAssistant) ext3).setRepeatedInvocationTrigger(null);
          }
        }
      }
    }

    /**
     * @see org.eclipse.jface.text.contentassist.ICompletionListener#selectionChanged(org.eclipse.jface.text.contentassist.ICompletionProposal, boolean)
     */
    public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) {
      //ignore
    }

    /**
     * @see org.eclipse.jface.text.contentassist.ICompletionListenerExtension#assistSessionRestarted(org.eclipse.jface.text.contentassist.ContentAssistEvent)
     */
    public void assistSessionRestarted(ContentAssistEvent event) {
      fRepetition= 0;
    }
  }
 
  /**
   *
   */
  private class TextInputListener implements ITextInputListener {

    /**
     * <p>Set the content type based on the new document if it has not already been
     * set yet.</p>
     *
     * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
     */
    public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
      if(fContentTypeID == null) {
        if(newInput instanceof IStructuredDocument) {
          IStructuredModel model = null;
          try {
            model = StructuredModelManager.getModelManager().getModelForRead((IStructuredDocument)newInput);
            if(model != null) {
              fContentTypeID = model.getContentTypeIdentifier();
              if (fAutoActivation != null) {
                fAutoActivation.dispose();
              }
              fAutoActivation = CompletionProposalComputerRegistry.getDefault().getActivator(fContentTypeID, fPartitionTypeID);
            }
          } finally {
            if(model != null) {
              model.releaseFromRead();
            }
          }
        }
      }
    }
   
    /**
     * <p>Ignored</p>
     *
     * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument)
     */
    public void inputDocumentAboutToBeChanged(IDocument oldInput,
        IDocument newInput) {
      //ignore
    }
  }

  protected void setAutoActivationDelay(int delay) {
    fAssistant.setAutoActivationDelay(delay);
  }

}
TOP

Related Classes of org.eclipse.wst.sse.ui.contentassist.StructuredContentAssistProcessor$CompletionListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.