/*
* 12/02/2013
*
* Copyright (C) 2013 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is distributed under a modified BSD license. See the included
* RSTALanguageSupport.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
/**
* An <code>AutoCompletion</code> that adds the ability to cycle through a set
* of <code>CompletionProvider</code>s via the trigger key. This allows the
* application to logically "group together" completions of similar kinds;
* for example, Java code completions vs. template completions.<p>
*
* Usage:
* <pre>
* XPathDynamicCompletionProvider dynamicProvider = new XPathDynamicCompletionProvider();
* RoundRobinAutoCompletion ac = new RoundRobinAutoCompletion(dynamicProvider);
* XPathCompletionProvider staticProvider = new XPathCompletionProvider();
* ac.addCompletionProvider(staticProvider);
* ac.setXXX(..);
* ...
* ac.install(textArea);
* </pre>
*
* @author mschlegel
*/
public class RoundRobinAutoCompletion extends AutoCompletion {
/** The List of CompletionProviders to use */
private List<CompletionProvider> cycle = new ArrayList<CompletionProvider>();
/**
* Constructor.
*
* @param provider A single completion provider.
* @see #addCompletionProvider(CompletionProvider)
*/
public RoundRobinAutoCompletion(CompletionProvider provider) {
super(provider);
cycle.add(provider);
// principal requirement for round-robin
setHideOnCompletionProviderChange(false);
// this is required since otherwise, on empty list of completions for
// one of the CompletionProviders, round-robin completion would not
// work
setHideOnNoText(false);
// this is required to prevent single choice of 1st provider to choose
// the completion since the user may want the second provider to be
// chosen.
setAutoCompleteSingleChoices(false);
}
/**
* Adds an additional <code>CompletionProvider</code> to the list to
* cycle through.
*
* @param provider The new completion provider.
*/
public void addCompletionProvider(CompletionProvider provider) {
cycle.add(provider);
}
/**
* Moves to the next Provider internally. Needs refresh of the popup window
* to display the changes.
*
* @return true if the next provider was the default one (thus returned to
* the default view). May be used in case you like to hide the
* popup in this case.
*/
public boolean advanceProvider() {
CompletionProvider currentProvider = getCompletionProvider();
int i = (cycle.indexOf(currentProvider)+1) % cycle.size();
setCompletionProvider(cycle.get(i));
return i==0;
}
/**
* Overridden to provide our own implementation of the action.
*/
@Override
protected Action createAutoCompleteAction() {
return new CycleAutoCompleteAction();
}
/**
* Resets the cycle to use the default provider on next refresh.
*/
public void resetProvider() {
CompletionProvider currentProvider = getCompletionProvider();
CompletionProvider defaultProvider = cycle.get(0);
if (currentProvider != defaultProvider) {
setCompletionProvider(defaultProvider);
}
}
/**
* An implementation of the auto-complete action that ensures the proper
* <code>CompletionProvider</code> is displayed based on the context in
* which the user presses the trigger key.
*/
private class CycleAutoCompleteAction extends AutoCompleteAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isAutoCompleteEnabled()) {
if (isPopupVisible()) {
// The popup is already visible, and user pressed the
// trigger-key. In this case, move to next provider.
advanceProvider();
}
else {
// Be sure to start with the default provider
resetProvider();
}
//Check if there are completions from the current provider. If not, advance to the next provider and display that one.
//A completion provider can force displaying "his" empty completion pop-up by returning an empty BasicCompletion. This is useful when the user is typing backspace and you like to display the first provider always first.
for (int i=1; i<cycle.size(); i++) {
List<Completion> completions = getCompletionProvider().getCompletions(getTextComponent());
if (completions.size() > 0) {
//nothing to do, just let the current provider display
break;
}
else{
//search for non-empty completions
advanceProvider();
}
}
}
super.actionPerformed(e);
}
}
// TODO add label "Ctrl-Space for <next provider name>" to the popup window
}