/*
* 12/11/2010
*
* ParameterizedCompletionChoicesWindow.java - A list of likely choices for a
* parameter.
*
* This library is distributed under a modified BSD license. See the included
* RSyntaxTextArea.License.txt file for details.
*/
package org.fife.ui.autocomplete;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JWindow;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/**
* A small popup window offering a list of likely choices for a parameter
* when the user has code-completed a parameterized completion. For example,
* if they have just code-completed the C function "<code>fprintf</code>",
* when entering the file name, this popup might display all local variables
* of type "<code>char *</code>".
*
* @author Robert Futrell
* @version 1.0
*/
public class ParameterizedCompletionChoicesWindow extends JWindow {
/**
* The parent AutoCompletion instance.
*/
private AutoCompletion ac;
/**
* The list of completion choices.
*/
private JList list;
/**
* The currently displayed completion choices.
*/
private DefaultListModel model;
/**
* A list of lists of choices for each parameter.
*/
private List<List<Completion>> choicesListList;
/**
* The scroll pane containing the list.
*/
private JScrollPane sp;
/**
* Comparator used to sort completions by their relevance before sorting
* them lexicographically.
*/
private static final Comparator<Completion> sortByRelevanceComparator =
new SortByRelevanceComparator();
/**
* Constructor.
*
* @param parent The parent window (hosting the text component).
* @param ac The auto-completion instance.
* @param context The completion context.
*/
public ParameterizedCompletionChoicesWindow(Window parent,
AutoCompletion ac,
final ParameterizedCompletionContext context) {
super(parent);
this.ac = ac;
ComponentOrientation o = ac.getTextComponentOrientation();
model = new DefaultListModel();
list = new JList(model);
if (ac.getParamChoicesRenderer()!=null) {
list.setCellRenderer(ac.getParamChoicesRenderer());
}
list.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount()==2) {
context.insertSelectedChoice();
}
}
});
sp = new JScrollPane(list);
setContentPane(sp);
applyComponentOrientation(o);
setFocusableWindowState(false);
// Give apps a chance to decorate us with drop shadows, etc.
PopupWindowDecorator decorator = PopupWindowDecorator.get();
if (decorator!=null) {
decorator.decorate(this);
}
}
/**
* Returns the selected value.
*
* @return The selected value, or <code>null</code> if nothing is
* selected.
*/
public String getSelectedChoice() {
Completion c = (Completion)list.getSelectedValue();
return c==null ? null : c.toString();
}
/**
* Changes the selected index.
*
* @param amount The amount by which to change the selected index.
*/
public void incSelection(int amount) {
int selection = list.getSelectedIndex();
selection += amount;
if (selection<0) {
// Account for nothing selected yet
selection = model.getSize()-1;//+= model.getSize();
}
else {
selection %= model.getSize();
}
list.setSelectedIndex(selection);
list.ensureIndexIsVisible(selection);
}
/**
* Initializes this window to offer suggestions for the parameters of
* a specific completion.
*
* @param pc The completion whose parameters we should offer suggestions
* for.
*/
public void initialize(ParameterizedCompletion pc) {
CompletionProvider provider = pc.getProvider();
ParameterChoicesProvider pcp = provider.getParameterChoicesProvider();
if (pcp==null) {
choicesListList = null;
return;
}
int paramCount = pc.getParamCount();
choicesListList = new ArrayList<List<Completion>>(paramCount);
JTextComponent tc = ac.getTextComponent();
for (int i=0; i<paramCount; i++) {
ParameterizedCompletion.Parameter param = pc.getParam(i);
List<Completion> choices = pcp.getParameterChoices(tc, param);
choicesListList.add(choices);
}
}
/**
* Sets the location of this window relative to the given rectangle.
*
* @param r The visual position of the caret (in screen coordinates).
*/
public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and
// description window, if applicable) both fit in the same window in
// a multi-monitor environment. To do this, we decide which monitor
// the rectangle "r" is in, and use that one (just pick top-left corner
// as the defining point).
Rectangle screenBounds = Util.getScreenBoundsForPoint(r.x, r.y);
//Dimension screenSize = tooltip.getToolkit().getScreenSize();
// Try putting our stuff "below" the caret first.
int y = r.y + r.height + 5;
// Get x-coordinate of completions. Try to align left edge with the
// caret first.
int x = r.x;
if (x<screenBounds.x) {
x = screenBounds.x;
}
else if (x+getWidth()>screenBounds.x+screenBounds.width) { // completions don't fit
x = screenBounds.x + screenBounds.width - getWidth();
}
setLocation(x, y);
}
/**
* Displays the choices for the specified parameter matching the given
* text. This will display or hide this popup window as necessary.
*
* @param param The index of the parameter the caret is currently in.
* This may be <code>-1</code> if not in a parameter (i.e., on
* the comma between parameters).
* @param prefix Text in the parameter before the dot. This may
* be <code>null</code> to represent the empty string.
*/
public void setParameter(int param, String prefix) {
model.clear();
List<Completion> temp = new ArrayList<Completion>();
if (choicesListList!=null && param>=0 && param<choicesListList.size()) {
List<Completion> choices = choicesListList.get(param);
if (choices!=null) {
for (Completion c : choices) {
String choice = c.getReplacementText();
if (prefix==null || Util.startsWithIgnoreCase(choice, prefix)) {
temp.add(c);
}
}
}
// Sort completions appropriately.
Comparator<Completion> c = null;
if (/*sortByRelevance*/true) {
c = sortByRelevanceComparator;
}
Collections.sort(temp, c);
for (int i=0; i<temp.size(); i++) {
model.addElement(temp.get(i));
}
int visibleRowCount = Math.min(model.size(), 10);
list.setVisibleRowCount(visibleRowCount);
// Toggle visibility, if necessary.
if (visibleRowCount==0 && isVisible()) {
setVisible(false);
}
else if (visibleRowCount>0) {
Dimension size = getPreferredSize();
if (size.width<150) {
setSize(150, size.height);
}
else {
pack();
}
// Make sure nothing is ever obscured by vertical scroll bar.
if (sp.getVerticalScrollBar()!=null &&
sp.getVerticalScrollBar().isVisible()) {
size = getSize();
int w = size.width + sp.getVerticalScrollBar().getWidth()+5;
setSize(w, size.height);
}
list.setSelectedIndex(0);
list.ensureIndexIsVisible(0);
if (!isVisible()) {
setVisible(true);
}
}
}
else {
setVisible(false);
}
}
/**
* Toggles the visibility of this popup window.
*
* @param visible Whether this window should be visible.
*/
@Override
public void setVisible(boolean visible) {
if (visible!=isVisible()) {
// i.e. if no possibilities matched what's been typed
if (visible && model.size()==0) {//list.getVisibleRowCount()==0) {
return;
}
super.setVisible(visible);
}
}
/**
* Updates the <tt>LookAndFeel</tt> of this window.
*/
public void updateUI() {
SwingUtilities.updateComponentTreeUI(this);
}
}