AutoCompletion defect

This is the forum for JIDE Common Layer which is open sourced at https://github.com/jidesoft/jide-oss. Please note, JIDE technical support doesn't monitor this forum as often as other forums. Please consider subscribe for technical support for JIDE Common Layer so that you can use customer only forum to get a timely response.

Moderator: JIDE Support

Forum rules
Community driven forum for open source JIDE Common Layer. JIDE technical support doesn't monitor this forum as often as other forums. If you only use JIDE Common Layer, please consider subscribing for technical support for JIDE Common Layer so that you can use customer only forum to get a timely response.

AutoCompletion defect

Postby keyevent » Wed Sep 01, 2010 2:10 am

AutoCompletion and AutoCompletionComboBox are having trouble when dealing with jtable. I made this testcase to reproduce the scene.
Image displays the dropdown combobox.
now I double click to select the item "football" and press del and then enter, I now got an empty cell
Image
now I click the cell again and type "f", I am expecting it display the autocomplete for "Football", but it only display the char and cell lost it's focus immediately
Image


Code: Select all
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import com.jidesoft.swing.AutoCompletion;
import com.jidesoft.swing.ComboBoxSearchable;

 :shock: public class TableRenderDemo extends JPanel
{
    private boolean DEBUG = false;
    private ComboEditor editor = new ComboEditor();

    public TableRenderDemo()
    {
        super(new GridLayout(1, 0));
        JTable table = new JTable(new MyTableModel());
        table.setPreferredScrollableViewportSize(new Dimension(500, 70));
        table.setFillsViewportHeight(true);
        // Create the scroll pane and add the table to it.
        JScrollPane scrollPane = new JScrollPane(table);
        // Set up column sizes.
        initColumnSizes(table);
        // Fiddle with the Sport column's cell editors/renderers.
        setUpSportColumn(table, table.getColumnModel().getColumn(2));
        table.getColumnModel().getColumn(4).setCellEditor(new CheckboxEditor(editor));
        // Add the scroll pane to this panel.
        add(scrollPane);
    }

    /*
     * This method picks good column sizes. If all column heads are wider than
     * the column's cells' contents, then you can just use
     * column.sizeWidthToFit().
     */
    private void initColumnSizes(JTable table)
    {
        MyTableModel model = (MyTableModel) table.getModel();
        TableColumn column = null;
        Component comp = null;
        int headerWidth = 0;
        int cellWidth = 0;
        Object[] longValues = model.longValues;
        TableCellRenderer headerRenderer = table.getTableHeader().getDefaultRenderer();
        for (int i = 0; i < 5; i++)
        {
            column = table.getColumnModel().getColumn(i);
            comp = headerRenderer.getTableCellRendererComponent(null, column.getHeaderValue(), false, false, 0, 0);
            headerWidth = comp.getPreferredSize().width;
            comp = table.getDefaultRenderer(model.getColumnClass(i)).getTableCellRendererComponent(table,
                    longValues[i], false, false, 0, i);
            cellWidth = comp.getPreferredSize().width;
            if (DEBUG)
            {
                System.out
                        .println("Initializing width of column " + i + ". " + "headerWidth = " + headerWidth + "; cellWidth = " + cellWidth);
            }
            column.setPreferredWidth(Math.max(headerWidth, cellWidth));
        }
    }

    public void setUpSportColumn(JTable table, TableColumn sportColumn)
    {
        // Set up the editor for the sport cells.
        sportColumn.setCellEditor(editor);
        // Set up tool tips for the sport cells.
        DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
        renderer.setToolTipText("Click for combo box");
        sportColumn.setCellRenderer(renderer);
    }

    /**
     * Custom editor that changes the combo choices based on the "Vegetarian"
     * column value
     */
    class ComboEditor extends DefaultCellEditor
    {
        DefaultComboBoxModel model;
        Map<String, List<String>> choicesMap;

        public ComboEditor()
        {
            super(new JComboBox());
            JComboBox combo = (JComboBox) getComponent();
            AutoCompletion complete = new AutoCompletion(combo, new ComboBoxSearchable(combo));
            complete.setStrict(true);
            model = (DefaultComboBoxModel) combo.getModel();
            ((JComboBox) getComponent()).setEditable(true);
            ((JComboBox) getComponent()).addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                   
                }
            });
            buildComboMap();
        }

        private void buildComboMap()
        {
            choicesMap = new HashMap<String, List<String>>();
            // for "true"
            List<String> choices = new ArrayList<String>();
            choices.add("Knitting");
            choices.add("Interior Decoration");
            choicesMap.put("true", choices);
            // for "false"
            choices = new ArrayList<String>();
            choices.add("Football");
            choices.add("Hockey");
            choices.add("Kickboxing");
            choicesMap.put("false", choices);
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
        {
            model.removeAllElements();
            String keyColumnValue = table.getValueAt(row, table.getColumnModel().getColumnIndex("Vegetarian"))
                    .toString();
            for (String item : choicesMap.get(keyColumnValue))
            {
                model.addElement(item);
                // ((AbstractTableModel)
                // table.getModel()).fireTableCellUpdated(row, 2);
                // ((AbstractTableModel)
                // table.getModel()).fireTableDataChanged();
            }
            return super.getTableCellEditorComponent(table, value, isSelected, row, column);
        }

        @Override
        public boolean stopCellEditing()
        {
            System.out.println(this.getCellEditorValue());
            return super.stopCellEditing();
        }

        public boolean shouldSelectCell(EventObject anEvent)
        {
            return delegate.shouldSelectCell(anEvent);
        }
    }
    class CheckboxEditor extends DefaultCellEditor
    {
        DefaultComboBoxModel model;
        Map<String, List<String>> choicesMap;
        ComboEditor editor;

        public CheckboxEditor(ComboEditor editor)
        {
            super(new JCheckBox());
            //
            this.editor = editor;
            buildComboMap();
        }

        private void buildComboMap()
        {
            choicesMap = new HashMap<String, List<String>>();
            // for "true"
            List<String> choices = new ArrayList<String>();
            choices.add("Knitting");
            choices.add("Interior Decoration");
            choicesMap.put("true", choices);
            // for "false"
            choices = new ArrayList<String>();
            choices.add("Football");
            choices.add("Hockey");
            choices.add("Kickboxing");
            choicesMap.put("false", choices);
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
        {
            Boolean flag = (Boolean) value;
            System.out.println(value);
            JComboBox sportsCombo = (JComboBox) editor.getTableCellEditorComponent(table, value, isSelected, row, 2);
            model = (DefaultComboBoxModel) (sportsCombo).getModel();
            model.removeAllElements();
            for (String item : choicesMap.get(flag.toString()))
            {
                model.addElement(item);
                sportsCombo.setSelectedIndex(0);
            }
            ((AbstractTableModel) table.getModel()).setValueAt(model.getElementAt(0), row, 2);
            ((AbstractTableModel) table.getModel()).fireTableCellUpdated(row, 2);
            ((AbstractTableModel) table.getModel()).fireTableDataChanged();
            return super.getTableCellEditorComponent(table, value, isSelected, row, column);
        }
    }
    class MyTableModel extends AbstractTableModel
    {
        private String[] columnNames =
        { "First Name", "Last Name", "Sport", "# of Years", "Vegetarian" };
        private Object[][] data =
        {
        { "Mary", "Campione", "Football", new Integer(5), new Boolean(false) },
        { "Alison", "Huml", "Interior Decoration", new Integer(3), new Boolean(true) },
        { "Kathy", "Walrath", "Hockey", new Integer(2), new Boolean(false) },
        { "Sharon", "Zakhour", "Knitting", new Integer(20), new Boolean(true) },
        { "Philip", "Milne", "Kickboxing", new Integer(10), new Boolean(false) } };
        public final Object[] longValues =
        { "Sharon", "Campione", "None of the above", new Integer(20), Boolean.TRUE };

        public int getColumnCount()
        {
            return columnNames.length;
        }

        public int getRowCount()
        {
            return data.length;
        }

        public String getColumnName(int col)
        {
            return columnNames[col];
        }

        public Object getValueAt(int row, int col)
        {
            return data[row][col];
        }

        /*
         * JTable uses this method to determine the default renderer/ editor for
         * each cell. If we didn't implement this method, then the last column
         * would contain text ("true"/"false"), rather than a check box.
         */
        public Class getColumnClass(int c)
        {
            return getValueAt(0, c).getClass();
        }

        /*
         * Don't need to implement this method unless your table's editable.
         */
        public boolean isCellEditable(int row, int col)
        {
            // Note that the data/cell address is constant,
            // no matter where the cell appears onscreen.
            if (col < 2)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /*
         * Don't need to implement this method unless your table's data can
         * change.
         */
        public void setValueAt(Object value, int row, int col)
        {
            if (DEBUG)
            {
                System.out.println("Setting value at " + row + "," + col + " to " + value + " (an instance of " + value
                        .getClass() + ")");
            }
            data[row][col] = value;
            fireTableCellUpdated(row, col);
            if (DEBUG)
            {
                System.out.println("New value of data:");
                printDebugData();
            }
        }

        private void printDebugData()
        {
            int numRows = getRowCount();
            int numCols = getColumnCount();
            for (int i = 0; i < numRows; i++)
            {
                System.out.print("    row " + i + ":");
                for (int j = 0; j < numCols; j++)
                {
                    System.out.print("  " + data[i][j]);
                }
                System.out.println();
            }
            System.out.println("--------------------------");
        }
    }

    /**
     * Create the GUI and show it. For thread safety, this method should be
     * invoked from the event-dispatching thread.
     */
    private static void createAndShowGUI()
    {
        // Create and set up the window.
        JFrame frame = new JFrame("TableRenderDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // Create and set up the content pane.
        TableRenderDemo newContentPane = new TableRenderDemo();
        newContentPane.setOpaque(true); // content panes must be opaque
        frame.setContentPane(newContentPane);
        // Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args)
    {
        // Schedule a job for the event-dispatching thread:
        // creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
    }
}
Attachments
incorrect.JPG
incorrect.JPG (3.19 KiB) Viewed 14497 times
empty_dropdown.JPG
empty_dropdown.JPG (2.61 KiB) Viewed 14497 times
dropdown.JPG
dropdown.JPG (18.31 KiB) Viewed 14497 times
keyevent
 
Posts: 2
Joined: Tue Aug 31, 2010 10:38 pm

Re: AutoCompletion defect

Postby JIDE Support » Wed Sep 01, 2010 10:33 am

The reason is that, AutoCompletion has to invoke JComboBox#setSelectedIndex() which will fire action event then trigger stop editing. Please try the following code to make it work.
Code: Select all
            AutoCompletion complete = new AutoCompletion(combo, new ComboBoxSearchable(combo) {
                @Override
                protected void setSelectedIndex(int index, boolean incremental) {
                    Object element = combo.getModel().getElementAt(index);
                    ((JTextComponent) combo.getEditor().getEditorComponent()).setText("" + element);
                }
            });

Thanks,
JIDE Software Technical Support Team
JIDE Support
Site Admin
 
Posts: 37219
Joined: Sun Sep 14, 2003 10:49 am

Re: AutoCompletion defect

Postby keyevent » Thu Sep 02, 2010 7:36 pm

thank you for the solution, it is working. I might have to look into source code and understand why. one more question on this, AutoCompletionComboBox is having the same problem, what should be done if this component is used?
keyevent
 
Posts: 2
Joined: Tue Aug 31, 2010 10:38 pm

Re: AutoCompletion defect

Postby johanrydahl » Wed Jul 06, 2011 12:58 am

Do anyone know why after I used a checkbox with auto completion the editor does not work unless click on the table cell.
I downloaded the code in previous question and saw that the autocompletion works after I click on the cell, but not if I tab into it.
The users of the system I am working are heavy keyboard users.

Johan
johanrydahl
 
Posts: 1
Joined: Tue Jul 05, 2011 8:49 am

Re: AutoCompletion defect

Postby JIDE Support » Wed Jul 06, 2011 3:37 pm

Please try to invoke JideTable#setAlwaysRequestFocusForEditor(true) to make it work.

Thanks,
JIDE Software Technical Support Team
JIDE Support
Site Admin
 
Posts: 37219
Joined: Sun Sep 14, 2003 10:49 am


Return to JIDE Common Layer Open Source Project Discussion (Community Driven)

Who is online

Users browsing this forum: No registered users and 7 guests

cron