/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*
* Contributor(s):
*
* Portions Copyrighted 2011 Sun Microsystems, Inc.
*/
package com.tvl.spi.editor.completion;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.util.Collections;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.editor.BaseDocument;
import org.netbeans.editor.Utilities;
import org.openide.util.Parameters;
/**
*
* @author Sam Harwell
*/
public class BaseCompletionController implements CompletionController {
private final JTextComponent component;
private final int queryType;
/**
*
* @param component The active component.
* @param queryType The query type.
*
* @throws NullPointerException if the parameter value is <code>null</code>.
*/
public BaseCompletionController(@NonNull JTextComponent component,
int queryType) {
Parameters.notNull("component", component);
this.component = component;
this.queryType = queryType;
}
/**
* Gets the active {@link JTextComponent}.
*
* @return The active component.
*/
public @NonNull JTextComponent getComponent() {
return component;
}
/**
* Gets the active {@link Document}.
*
* @return The active document.
*/
public Document getDocument() {
return getComponent().getDocument();
}
@Override
public void sortItems(List<? extends CompletionItem> items, int sortType) {
Collections.sort(items, CompletionItemComparator.get(sortType));
}
@Override
public Selection getSelection(List<? extends CompletionItem> items, List<? extends CompletionItem> declarationItems) {
String prefix = getCompletionPrefix();
if (prefix != null && prefix.length() > 0) {
for (int idx = 0; idx < items.size(); idx++) {
CompletionItem item = items.get(idx);
CharSequence text = item.getInsertPrefix();
if (text != null && text.toString().startsWith(prefix)) {
boolean selected = isSelected(items, declarationItems, prefix, idx);
boolean unique = isUnique(items, declarationItems, prefix, idx);
return new Selection(idx, selected, unique);
}
}
}
/* This follows existing behavior, which is slightly different from
* Selection.DEFAULT because this method marks the item as selected.
*/
return new Selection(0);
}
@Override
public void defaultAction(CompletionItem bestMatch, boolean isSelected) {
if (isSelected) {
bestMatch.defaultAction(component);
}
}
@Override
public void processKeyEvent(KeyEvent evt, CompletionItem bestMatch, boolean isSelected) {
bestMatch.processKeyEvent(evt);
}
@Override
public void render(Graphics g, Font defaultFont, Color foregroundColor,
Color backgroundColor, Color selectedForegroundColor,
Color selectedBackgroundColor, int width, int height, CompletionItem item, boolean isBestMatch, boolean isSelected) {
if (isBestMatch) {
// Clear the background
g.setColor(selectedBackgroundColor);
g.fillRect(0, 0, width, height);
g.setColor(selectedForegroundColor);
item.render(g, defaultFont, selectedForegroundColor, selectedBackgroundColor, width, height, isBestMatch);
} else {
// Clear the background
g.setColor(backgroundColor);
g.fillRect(0, 0, width, height);
g.setColor(foregroundColor);
item.render(g, defaultFont, foregroundColor, backgroundColor, width, height, isBestMatch);
}
}
@Override
public boolean instantSubstitution(CompletionItem uniqueMatch) {
return uniqueMatch.instantSubstitution(component);
}
/**
* Gets the current completion prefix. The default implementation returns the
* text of the current identifier (per {@link Utilities#getIdentifierBlock})
* up to the location of the caret.
*
* @return The completion prefix.
*/
protected @CheckForNull String getCompletionPrefix() {
if(getDocument() instanceof BaseDocument) {
BaseDocument doc = (BaseDocument)getDocument();
int caretOffset = getComponent().getSelectionStart();
try {
int[] block = Utilities.getIdentifierBlock(doc, caretOffset);
if (block != null) {
block[1] = caretOffset;
return doc.getText(block);
}
} catch (BadLocationException ble) {
}
}
return null;
}
/**
*
* @param items
* @param declarationItems
* @param prefix
* @param index
*
* @return <code>true</code> if the item is selected, otherwise <code>false</code>.
*/
protected boolean isSelected(@NonNull List<? extends CompletionItem> items,
@NonNull List<? extends CompletionItem> declarationItems,
@NullAllowed String prefix,
int index) {
return declarationItems.isEmpty();
}
/**
*
* @param items
* @param declarationItems
* @param prefix
* @param index
*
* @return <code>true</code> if the selection is unique, otherwise <code>false</code>.
*/
protected boolean isUnique(@NonNull List<? extends CompletionItem> items,
@NonNull List<? extends CompletionItem> declarationItems,
@NullAllowed String prefix,
int index) {
if (items.size() != 1) {
return false;
}
try {
int caretOffset = getComponent().getSelectionStart();
int[] block = Utilities.getIdentifierBlock(getComponent(),
getComponent().getSelectionStart());
return block == null || block[1] == caretOffset;
} catch (BadLocationException ble) {
return false;
}
}
}