/* * Copyright 2008 Ayman Al-Sairafi ayman.alsairafi@gmail.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License * at http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jsyntaxpane.actions.gui; import java.awt.Font; import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.KeyEvent; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent; import jsyntaxpane.actions.ActionUtils; import jsyntaxpane.util.ReflectUtils; import jsyntaxpane.util.StringUtils; import jsyntaxpane.util.SwingUtils; /** * * @author Ayman Al-Sairafi */ public class ReflectCompletionDialog extends javax.swing.JDialog implements EscapeListener { /** * The class we are displaying its members: */ private Class theClass; /** * The current filter, to avoid refiltering the items */ public String escapeChars = ";(= \t\n"; public List<Member> items; private final JTextComponent target; /** * Creates new form ReflectCompletionDialog * @param target Text component for this dialog */ public ReflectCompletionDialog(JTextComponent target) { super(SwingUtilities.getWindowAncestor(target), ModalityType.APPLICATION_MODAL); initComponents(); this.target = target; jTxtItem.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { refilterList(); } @Override public void removeUpdate(DocumentEvent e) { refilterList(); } @Override public void changedUpdate(DocumentEvent e) { refilterList(); } }); // This will allow the textfield to receive TAB keys jTxtItem.setFocusTraversalKeysEnabled(false); // Add action so we automatically filter on comboBox Enter Key jCmbClassName.getEditor().addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateItems(); } }); SwingUtils.addEscapeListener(this); } public void setFonts(Font font) { jTxtItem.setFont(font); jLstItems.setFont(font); doLayout(); } private String getSelection() { String result; if (jLstItems.getSelectedIndex() >= 0) { Object selected = jLstItems.getSelectedValue(); if (selected instanceof Method) { result = ReflectUtils.getJavaCallString((Method) selected); } else if (selected instanceof Constructor) { result = ReflectUtils.getJavaCallString((Constructor) selected); } else if (selected instanceof Field) { result = ((Field) selected).getName(); } else { result = selected.toString(); } } else { result = jTxtItem.getText(); } return result; } private void refilterList() { String prefix = jTxtItem.getText(); Vector<Member> filtered = new Vector<Member>(); Object selected = jLstItems.getSelectedValue(); for (Member m : items) { if (StringUtils.camelCaseMatch(m.getName(), prefix)) { filtered.add(m); } } jLstItems.setListData(filtered); if (selected != null && filtered.contains(selected)) { jLstItems.setSelectedValue(selected, true); } else { jLstItems.setSelectedIndex(0); } } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jTxtItem = new javax.swing.JTextField(); jScrollPane1 = new javax.swing.JScrollPane(); jLstItems = new javax.swing.JList(); jCmbClassName = new javax.swing.JComboBox(); setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setName("CompletionDialog"); // NOI18N setResizable(false); setUndecorated(true); jTxtItem.setBorder(null); jTxtItem.addKeyListener(new java.awt.event.KeyAdapter() { public void keyPressed(java.awt.event.KeyEvent evt) { jTxtItemKeyPressed(evt); } }); jLstItems.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); jLstItems.setCellRenderer(new jsyntaxpane.actions.gui.MembersListRenderer(this)); jLstItems.setFocusable(false); jLstItems.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseClicked(java.awt.event.MouseEvent evt) { jLstItemsMouseClicked(evt); } }); jScrollPane1.setViewportView(jLstItems); jCmbClassName.setEditable(true); jCmbClassName.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Object", "String" })); jCmbClassName.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { jCmbClassNameItemStateChanged(evt); } }); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jTxtItem, javax.swing.GroupLayout.DEFAULT_SIZE, 437, Short.MAX_VALUE) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 437, Short.MAX_VALUE) .addComponent(jCmbClassName, 0, 437, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jTxtItem, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(0, 0, 0) .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 156, Short.MAX_VALUE) .addGap(0, 0, 0) .addComponent(jCmbClassName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); pack(); }// </editor-fold>//GEN-END:initComponents private void jTxtItemKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_jTxtItemKeyPressed int i = jLstItems.getSelectedIndex(); switch (evt.getKeyCode()) { case KeyEvent.VK_ESCAPE: target.setCaretPosition(target.getSelectionEnd()); setVisible(false); return; case KeyEvent.VK_DOWN: i++; break; case KeyEvent.VK_UP: i--; break; case KeyEvent.VK_HOME: i = 0; break; case KeyEvent.VK_END: i = jLstItems.getModel().getSize() - 1; break; case KeyEvent.VK_PAGE_DOWN: i += jLstItems.getVisibleRowCount(); break; case KeyEvent.VK_PAGE_UP: i -= jLstItems.getVisibleRowCount(); break; } if (escapeChars.indexOf(evt.getKeyChar()) >= 0) { String result = getSelection(); char pressed = evt.getKeyChar(); // we need to just accept ENTER, and replace the tab with a single // space if (pressed != '\n') { result += (pressed == '\t') ? ' ' : pressed; } target.replaceSelection(result); setVisible(false); } else { // perform bounds checks for i if (i >= jLstItems.getModel().getSize()) { i = jLstItems.getModel().getSize() - 1; } if (i < 0) { i = 0; } jLstItems.setSelectedIndex(i); jLstItems.ensureIndexIsVisible(i); } }//GEN-LAST:event_jTxtItemKeyPressed private void jCmbClassNameItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_jCmbClassNameItemStateChanged if (evt.getStateChange() == ItemEvent.SELECTED) { updateItems(); } }//GEN-LAST:event_jCmbClassNameItemStateChanged private void jLstItemsMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLstItemsMouseClicked if (evt.getClickCount() == 2) { String selected = getSelection(); target.replaceSelection(selected); setVisible(false); } }//GEN-LAST:event_jLstItemsMouseClicked private void updateItems() { String className = jCmbClassName.getEditor().getItem().toString(); if (items == null) { items = new ArrayList<Member>(); } else { items.clear(); } // we must have the class in the Combo: Class aClass = ReflectUtils.findClass(className, ReflectUtils.DEFAULT_PACKAGES); if (aClass != null) { // for now, add everything: theClass = aClass; ReflectUtils.addConstrcutors(aClass, items); ReflectUtils.addMethods(aClass, items); ReflectUtils.addFields(aClass, items); ActionUtils.insertIntoCombo(jCmbClassName, className); jTxtItem.requestFocusInWindow(); } refilterList(); } public Class getTheClass() { return theClass; } /** * Set the items to display * @param items */ public void setItems(List<Member> items) { this.items = items; } /** * Display the dialog. * @param target text component (its Window will be the parent) */ public void displayFor(JTextComponent target) { try { int dot = target.getSelectionStart(); Window window = SwingUtilities.getWindowAncestor(target); Rectangle rt = target.modelToView(dot); Point loc = new Point(rt.x, rt.y); // convert the location from Text Componet coordinates to // Frame coordinates... loc = SwingUtilities.convertPoint(target, loc, window); // and then to Screen coordinates SwingUtilities.convertPointToScreen(loc, window); setLocationRelativeTo(window); setLocation(loc); } catch (BadLocationException ex) { Logger.getLogger(ReflectCompletionDialog.class.getName()).log(Level.SEVERE, null, ex); } finally { setFonts(target.getFont()); updateItems(); jTxtItem.setText(target.getSelectedText()); setVisible(true); } } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JComboBox jCmbClassName; private javax.swing.JList jLstItems; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JTextField jTxtItem; // End of variables declaration//GEN-END:variables @Override public void escapePressed() { target.setCaretPosition(target.getSelectionEnd()); setVisible(false); } }