package ctagsinterface.main; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Vector; import javax.swing.BorderFactory; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListModel; import javax.swing.ImageIcon; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.JWindow; import javax.swing.Timer; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.gjt.sp.jedit.GUIUtilities; import org.gjt.sp.jedit.View; import ctagsinterface.index.TagIndex; import ctagsinterface.main.TagListFilterMenu.TagListModelHandler; @SuppressWarnings("serial") public class QuickSearchTagDialog extends JDialog { enum Mode { SUBSTRING, PREFIX }; private Mode mode; private JTextField name; private JList tags; private DefaultListModel model; private View view; private Timer filterTimer; private String query; private boolean showImmediately; private JCheckBox caseSensitive; private JCheckBox wholeWord; private TagListFilterMenu menu; private QuickSearchTagListModelHandler handler; /** This window will contains the scroll with the items. */ final JWindow window = new JWindow(this); public class QuickSearchTagListModelHandler implements TagListModelHandler { public void clear() { model.removeAllElements(); } public void add(Tag t) { model.addElement(new QuickSearchTag(t)); } public void done() { if (model.getSize() == 1) { jumpTo((QuickSearchTag) model.get(0)); model.removeAllElements(); } } } public QuickSearchTagDialog(View view, Mode mode) { this(view, mode, "Search tag", null, false); } public QuickSearchTagDialog(View view, Mode mode, String title, String query, boolean showImmediately) { super(view, title, false); this.view = view; this.mode = mode; this.query = query; this.showImmediately = showImmediately; JPanel p = new JPanel(); p.add(new JLabel("Type part of the tag name:")); name = new JTextField(30); p.add(name); ActionListener refilter = new ActionListener() { public void actionPerformed(ActionEvent e) { applyFilter(); } }; caseSensitive = new JCheckBox("Case-sensitive", false); p.add(caseSensitive); caseSensitive.addActionListener(refilter); wholeWord = new JCheckBox("Whole-word", false); p.add(wholeWord); wholeWord.addActionListener(refilter); add(p, BorderLayout.NORTH); JPanel listPanel = new JPanel(); listPanel.setLayout(new BorderLayout()); handler = new QuickSearchTagListModelHandler(); menu = new TagListFilterMenu(handler); listPanel.add(menu, BorderLayout.NORTH); listPanel.setBorder(BorderFactory.createEtchedBorder()); model = new DefaultListModel(); tags = new JList(model); tags.setCellRenderer(new TagListCellRenderer()); listPanel.add(new JScrollPane(tags), BorderLayout.CENTER); window.setContentPane(listPanel); name.getDocument().addDocumentListener(new DocumentListener() { public void changedUpdate(DocumentEvent e) { setFilter(); } public void insertUpdate(DocumentEvent e) { setFilter(); } public void removeUpdate(DocumentEvent e) { setFilter(); } }); name.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (handledByList(e)) { tags.dispatchEvent(e); } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { setVisible(false); } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { jumpToSelected(); } } } ); tags.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent arg0) { jumpToSelected(); } }); tags.addKeyListener(new KeyAdapter() { public void keyTyped(KeyEvent e) { name.dispatchEvent(e); } public void keyPressed(KeyEvent e) { if (!handledByList(e)) { name.dispatchEvent(e); } else if (e.getKeyCode() == KeyEvent.VK_ENTER) { e.consume(); jumpToSelected(); } } }); filterTimer = new Timer(500, new ActionListener() { public void actionPerformed(ActionEvent e) { applyFilter(); } }); filterTimer.setRepeats(false); pack(); setLocationRelativeTo(view); setVisible(true); if (showImmediately) applyFilter(); } private void jumpToSelected() { QuickSearchTag t = (QuickSearchTag) tags.getSelectedValue(); jumpTo(t); } private void jumpTo(QuickSearchTag t) { if (t != null) CtagsInterfacePlugin.jumpTo(view, t.file, t.line, true); setVisible(false); dispose(); } private void setFilter() { if (filterTimer.isRunning()) filterTimer.restart(); else filterTimer.start(); } private void applyFilter() { final String input = caseSensitive.isSelected() ? name.getText(): name.getText().toLowerCase(); if (showImmediately || (! input.isEmpty())) { String s = (query == null) ? "" : query; if (! input.isEmpty()) { if (s.length() > 0) s = s + " AND "; String field = caseSensitive.isSelected() ? TagIndex._NAME_FLD : TagIndex._NAME_LOWERCASE_FLD; String value = (wholeWord.isSelected()) ? input : (mode == Mode.SUBSTRING ? "*" : "") + input + "*"; s = s + field + ":" + value; } Vector<Tag> tags = CtagsInterfacePlugin.runScopedQuery(view, s); menu.setTags(tags); } if (model.isEmpty()) window.setVisible(false); else { tags.setVisibleRowCount(Math.min(10, model.size())); window.pack(); window.setVisible(true); } } private static boolean handledByList(KeyEvent e) { return e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_PAGE_DOWN || e.getKeyCode() == KeyEvent.VK_PAGE_UP; } public void setVisible(boolean b) { if (b) { Rectangle bounds = getBounds(); window.setLocation(bounds.x, bounds.y + bounds.height); GUIUtilities.requestFocus(this, name); } window.setVisible(false); // Initially hide the tag list window, even if b is true super.setVisible(b); } private static class QuickSearchTag { String file; int line; String name; String desc; String kind; public QuickSearchTag(Tag t) { StringBuffer text = new StringBuffer(); name = t.getName(); text.append(name); kind = t.getKind(); if (kind != null) text.append(" (" + kind + ")"); file = t.getFile(); line = t.getLine(); desc = text.toString(); if (isValid()) desc = desc + " [" + file + ":" + line + "]"; } public boolean isValid() { return (desc.length() > 0 && file != null && line >= 0); } public String toString() { return desc; } public ImageIcon getIcon() { return KindIconProvider.getIcon(kind); } } private class TagListCellRenderer extends DefaultListCellRenderer { @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JLabel l = (JLabel) super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus); if (value instanceof QuickSearchTag) { ImageIcon icon = ((QuickSearchTag)value).getIcon(); if (icon != null) l.setIcon(icon); } return l; } } }