Link to code: AutocompleteGUI.java
/*************************************************************************
* @author Matthew Drabick, adapted by Austin Lu for COMPSCI 201 Autocomplete,
*
* Interactive GUI used to demonstrate the Autocomplete data type.
*
* * Reads a list of terms and weights from a file, specified as a command-line argument.
*
* * As the user types in a text box, display the top-k terms that start with the text that
* the user types.
*
* * Displays the result in a browser if the user selects a term (by pressing enter or
* clicking a selection).
*
* BUG: Selections don't autoupdate if user enter character into text box without typing it
* (e.g., by selecting it from Mac OS X Character Viewer). BUG: Completion list disappears
* when user clicks to browse.
*
* * (06/01/2015) - GUI is no longer case-sensitive, but results are
* still displayed in their original case.
*
*
*************************************************************************/
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import javax.swing.*;
import java.util.*;
@SuppressWarnings("serial")
public class AutocompleteGUI extends JFrame {
private static int DEF_WIDTH = 600;
private static int DEF_HEIGHT = 400;
private static String searchURL = "https://www.google.com/search?q=";
private HashMap<String, String> casingMap;
public static final String CHARSET = "UTF-8";
public static final Locale LOCALE = Locale.US;
// display top k results
private final int k;
private final String autocompletorClassName;
public AutocompleteGUI(String fileName, int k, String className) {
this.k = k;
this.autocompletorClassName = className;
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Autocomplete");
setLocationRelativeTo(null);
Container content = getContentPane();
GroupLayout layout = new GroupLayout(content);
content.setLayout(layout);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
final AutocompletePanel ap = new AutocompletePanel(fileName);
JButton searchButton = new JButton("Search Google");
// searchButton.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
searchButton.addMouseListener(new MouseListener() {
// Need a bunch of unimplemented reports
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
@Override
public void mouseClicked(MouseEvent e) {
searchOnline(ap.getSelectedText());
}
});
JLabel textLabel = new JLabel("Type text:");
textLabel.setBorder(BorderFactory.createEmptyBorder(1, 4, 0, 0));
layout.setHorizontalGroup(layout.createSequentialGroup()
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(textLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE)
.addComponent(ap, 0, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE)
.addComponent(searchButton, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE));
layout.setVerticalGroup(
layout.createSequentialGroup().addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(textLabel).addComponent(ap).addComponent(searchButton)));
setPreferredSize(new Dimension(DEF_WIDTH, DEF_HEIGHT));
pack();
}
private class AutocompletePanel extends JPanel {
private final JTextField searchText;
private Autocompletor auto;
private String[] results = new String[k];
private JList<String> suggestions;
// keep these two values in sync! - used to keep the listbox the same
// width as the textfield
private final int DEF_COLUMNS = 30;
private final String suggListLen = "<b>Harry Potter and the Deathly Hallows: Part 1 (2010)</b>";
public AutocompletePanel(String filename) {
super();
// read in the data
Scanner in;
try {
in = new Scanner(new File(filename), CHARSET);
in.useLocale(LOCALE);
int N = Integer.parseInt(in.nextLine());
String[] terms = new String[N];
double[] weights = new double[N];
casingMap = new HashMap<String, String>();
for (int i = 0; i < N; i++) {
String line = in.nextLine();
int tab = line.indexOf('\t');
weights[i] = Double.parseDouble(line.substring(0, tab).trim());
casingMap.put(line.substring(tab + 1).toLowerCase(), line.substring(tab + 1));
terms[i] = line.substring(tab + 1).toLowerCase();
// create the autocomplete object
}
auto = (Autocompletor) Class.forName(autocompletorClassName)
.getDeclaredConstructor(String[].class, double[].class).newInstance(terms, weights);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e1) {
e1.printStackTrace();
System.exit(1);
} catch (FileNotFoundException e2) {
System.out.println("Cannot read file " + filename);
System.exit(1);
}
GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
searchText = new JTextField(DEF_COLUMNS);
searchText.setMaximumSize(
new Dimension(searchText.getMaximumSize().width, searchText.getPreferredSize().height));
searchText.getInputMap().put(KeyStroke.getKeyStroke("UP"), "none");
searchText.getInputMap().put(KeyStroke.getKeyStroke("DOWN"), "none");
searchText.addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
int pos = searchText.getText().length();
searchText.setCaretPosition(pos);
}
public void focusLost(FocusEvent e) {
}
});
JPanel searchTextPanel = new JPanel();
searchTextPanel.add(searchText);
searchTextPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
searchTextPanel.setLayout(new GridLayout(1, 1));
suggestions = new JList<String>(results);
suggestions.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1));
suggestions.setVisible(false);
suggestions.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
suggestions.setMaximumSize(
new Dimension(searchText.getMaximumSize().width, suggestions.getPreferredSize().height));
suggestions.setPrototypeCellValue(suggListLen); // set to make equal
// to the width of
// the
// textfield
suggestions.setFont(suggestions.getFont().deriveFont(Font.PLAIN, 13));
Action makeSelection = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (!suggestions.isSelectionEmpty()) {
String selection = (String) suggestions.getSelectedValue();
selection = selection.replaceAll("\\<.*?>", "");
searchText.setText(selection);
getSuggestions(selection);
}
searchOnline(searchText.getText());
}
};
Action moveSelectionUp = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
if (suggestions.getSelectedIndex() >= 0) {
suggestions.requestFocusInWindow();
suggestions.setSelectedIndex(suggestions.getSelectedIndex() - 1);
}
}
};
Action moveSelectionDown = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (suggestions.getSelectedIndex() != results.length) {
suggestions.requestFocusInWindow();
suggestions.setSelectedIndex(suggestions.getSelectedIndex() + 1);
}
}
};
Action moveSelectionUpFocused = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (suggestions.getSelectedIndex() == 0) {
suggestions.clearSelection();
searchText.requestFocusInWindow();
// int pos = searchText.getText().length();
// searchText.select(pos, pos);
searchText.setSelectionEnd(0);
} else if (suggestions.getSelectedIndex() >= 0) {
suggestions.setSelectedIndex(suggestions.getSelectedIndex() - 1);
}
}
};
suggestions.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("UP"),
"moveSelectionUp");
suggestions.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("DOWN"),
"moveSelectionDown");
suggestions.getActionMap().put("moveSelectionUp", moveSelectionUp);
suggestions.getActionMap().put("moveSelectionDown", moveSelectionDown);
suggestions.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke("ENTER"), "makeSelection");
suggestions.getInputMap().put(KeyStroke.getKeyStroke("UP"), "moveSelectionUpFocused");
suggestions.getActionMap().put("moveSelectionUpFocused", moveSelectionUpFocused);
suggestions.getActionMap().put("makeSelection", makeSelection);
JPanel suggestionsPanel = new JPanel();
suggestionsPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0));
suggestionsPanel.add(suggestions);
suggestionsPanel.setLayout(new GridLayout(1, 1));
this.setMaximumSize(new Dimension(searchText.getMaximumSize().width, this.getPreferredSize().height));
suggestions.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent mouseEvent) {
JList<?> theList = (JList<?>) mouseEvent.getSource();
if (mouseEvent.getClickCount() >= 1) {
int index = theList.locationToIndex(mouseEvent.getPoint());
if (index >= 0) {
String selection = getSelectedText();
searchText.setText(selection);
String text = searchText.getText();
getSuggestions(text);
searchOnline(searchText.getText());
}
}
}
});
searchText.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
JTextField txtSrc = (JTextField) e.getSource();
String text = txtSrc.getText();
getSuggestions(text);
}
@Override
public void keyTyped(KeyEvent e) {
}
});
searchText.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String selection = getSelectedText();
searchText.setText(selection);
getSuggestions(selection);
searchOnline(searchText.getText());
}
});
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(searchTextPanel, 0, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(suggestionsPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE))
);
layout.setVerticalGroup(
layout.createSequentialGroup().addComponent(searchTextPanel).addComponent(suggestionsPanel));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
}
/**
* Makes a call to the implementation of Autocomplete to get suggestions
* for the currently entered text.
*
* @param text
* string to search for
*/
public void getSuggestions(String text) {
// text = text.trim();
if (text.equals("")) {
suggestions.clearSelection();
suggestions.setVisible(false);
} else {
int textLen = text.length();
Queue<String> resultQ = new LinkedList<String>();
for (String term : auto.topKMatches(text.toLowerCase(), k)) {
resultQ.add(casingMap.get(term));
}
if (!resultQ.isEmpty()) {
results = new String[resultQ.size()];
for (int i = 0; i < results.length; i++) {
results[i] = resultQ.remove();
results[i] = "<html>" + results[i].substring(0, textLen) + "<b>" + results[i].substring(textLen)
+ "</b></html>";
}
suggestions.setListData(results);
suggestions.setVisible(true);
// suggestions.setSelectedIndex(0); // Pressing enter
// automatically selects the first one
// if nothing has been
} else {
// No suggestions
suggestions.setVisible(false);
suggestions.clearSelection();
}
}
}
public String getSelectedText() {
if (!suggestions.isSelectionEmpty()) {
String selection = (String) suggestions.getSelectedValue();
selection = selection.replaceAll("\\<.*?>", "");
return selection;
} else
return getSearchText();
}
public String getSearchText() {
return searchText.getText();
}
}
/**
* Creates a URI from the user-defined string and searches the web with the
* selected search engine Opens the default web browser (or a new tab if it
* is already open)
*
* @param s
* string to search online for
*/
private void searchOnline(String s) {
URI searchAddress = null;
try {
URI tempAddress = new URI(searchURL + s.trim().replace(' ', '+'));
searchAddress = new URI(tempAddress.toASCIIString()); // hack to
// handle
// Unicode
} catch (URISyntaxException e2) {
e2.printStackTrace();
return;
}
try {
Desktop.getDesktop().browse(searchAddress);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}