0

I need to highlight the text and move the entire screen/view to the searched text. for example: If view is holding 1 to 1000 numbers(rows), if i enter search text as 55 and click on Search button, first it has to move the screen to text 55 (first occurrence) and then should highlight only 55, not the other occurrences of 55. As i keep pressing on Search button, search should proceed further and highlight 155 and then 255, 355, 455, 555, etc. At once only one 55 can be highlighted.

It should have same search experience as Notepad.

Is it a valid use case ? I am new to Swings, trying to build a small application as part of learning. Have written a sample code (referred other post), but this is highlighting all the occurrences at once and not sure how to move the view to the first occurrence. Please suggest.

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.BadLocationException;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Form extends JFrame {

    private String textForSearch = "";
    private JTable t;

    public Form() {

        t = new JTable();

        String[] headers = new String[]{"first", "second", "third"};
        DefaultTableModel model = new DefaultTableModel(0, 0);
        model.setColumnIdentifiers(headers);

        for(int i =0;i<1000;i++){
            model.addRow(new Object[]{i});
            model.addRow(new Object[]{i});
        }

        t.setModel(model);

        for(int i =0;i<t.getColumnCount();i++){
            t.getColumnModel().getColumn(0).setCellRenderer(getRenderer());
        }

        JScrollPane jsp = new JScrollPane(t);
        final RightPanel right = new RightPanel();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(jsp, BorderLayout.CENTER);
        add(right, BorderLayout.EAST);
        pack();
        setLocationRelativeTo(null);
    }

    private TableCellRenderer getRenderer() {
        return new TableCellRenderer() {
            JTextField f = new JTextField(5);

            @Override
            public Component getTableCellRendererComponent(JTable arg0, Object arg1, boolean arg2, boolean arg3, int arg4, int arg5) {
                if(arg1 != null){
                    f.setText(arg1.toString());
                    String string = arg1.toString();
                    if(string.contains(textForSearch)){
                        int indexOf = string.indexOf(textForSearch);
                        try {
                            f.getHighlighter().addHighlight(indexOf,indexOf+textForSearch.length(),new javax.swing.text.DefaultHighlighter.DefaultHighlightPainter(Color.RED));
                        } catch (BadLocationException e) {
                            e.printStackTrace();
                        }
                    }
                } else {
                    f.setText("");
                    f.getHighlighter().removeAllHighlights();
                }
                return f;
            }
        };
    }

    class RightPanel extends JPanel{

        public RightPanel(){
            setLayout(new GridBagLayout());
            GridBagConstraints c = new GridBagConstraints();
            c.insets = new Insets(5, 5, 5, 5);
            c.gridy = 0;
            final JTextField f = new JTextField();
            add(f,c);
            JButton b = new JButton("search");
            b.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent arg0) {
                    textForSearch = f.getText();
                    t.repaint();
                }
            });
            c.gridy++;
            add(b,c);
        }
    }

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                new Form().setVisible(true);
            }
        });
    }
}

Edited: From other posts, found solution to move the pointer/screen to a particular row, but not sure how to get the rowIndex value of a searched text:

public static void scrollToVisible(JTable table, int rowIndex, int vColIndex) {
    if (!(table.getParent() instanceof JViewport)) return;
    JViewport viewport = (JViewport) table.getParent();
    Rectangle rect = table.getCellRect(rowIndex, vColIndex, true);
    Point pt = viewport.getViewPosition();
    rect.setLocation(rect.x - pt.x, rect.y - pt.y);
    viewport.scrollRectToVisible(rect);
}

Added for others reference :

to show the selected row in middle of the screen: http://www.java2s.com/Code/Java/Swing-JFC/ScrollingaCelltotheCenterofaJTableComponent.htm

Molay
  • 1,154
  • 2
  • 19
  • 42

1 Answers1

2

I think i have managed to do what u wanted, like you said Notepad++ search like. Dont use new JTextField() for a renderer, just extend JTextField and implement TableCellRenderer. Then in your action listener, find the index of the cell that has this value, and let the renderer highlight only this cell.

✓ 1 search each time (furthermore only 1 highlight).

✓ Scrolls the value that is found.

✓ Beeps when there is no search.

✓ Beeps when it starts from the start.

✓ Doesn't loose selection after a search (you can change that)

Preview: enter image description here

Some extra comments inside the code.

package test;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.BadLocationException;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Form extends JFrame {

    private String textForSearch = "";
    private JTable t;
    private static int rowIndex = -1;
    public Form() {

        t = new JTable();

        String[] headers = new String[]{"first", "second", "third"};
        DefaultTableModel model = new DefaultTableModel(0, 0);
        model.setColumnIdentifiers(headers);

        for(int i =0;i<1555;i++){
            model.addRow(new Object[]{i});
        }

        t.setModel(model);

        for(int i =0;i<t.getColumnCount();i++){
            t.getColumnModel().getColumn(0).setCellRenderer(new MyTextFieldRenderer()); //Add JTextField as renderer
        }
        JScrollPane jsp = new JScrollPane(t);
        final RightPanel right = new RightPanel();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(jsp, BorderLayout.CENTER);
        add(right, BorderLayout.EAST);
        pack();
        setLocationRelativeTo(null);
    }

    private class MyTextFieldRenderer extends JTextField implements TableCellRenderer{

        @Override
        public Component getTableCellRendererComponent(JTable arg0, Object arg1, boolean arg2, boolean arg3, int row,
                int col) {
            String string = String.valueOf(arg1);
            this.setText(string);
            if(row == rowIndex) //Check if the row of the found text matches this value
            {
                int indexOf = getText().indexOf(textForSearch);
                try {
                    getHighlighter().addHighlight(indexOf, indexOf + textForSearch.length(),
                            new javax.swing.text.DefaultHighlighter.DefaultHighlightPainter(Color.RED));
                } catch (BadLocationException e) {
                    e.printStackTrace();
                }
            }
            return this;
        }

    }
    class RightPanel extends JPanel{

        public RightPanel(){
            setLayout(new GridBagLayout());
            GridBagConstraints c = new GridBagConstraints();
            c.insets = new Insets(5, 5, 5, 5);
            c.gridy = 0;
            final JTextField f = new JTextField(11);
            add(f,c);
            JButton b = new JButton("search");
            b.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent arg0) {
                    //Start all over again
                    int tempRow = rowIndex;
                    if (rowIndex + 1 >= t.getRowCount())
                    {
                        rowIndex = -1;
                        Toolkit.getDefaultToolkit().beep(); //Notepad++ beeps i think
                    }
                    for (int row = rowIndex+1; row < t.getRowCount(); row++) { //Start checking all rows
                        Component cc = t.prepareRenderer(t.getCellRenderer(row, 0), row, 0);
                        if (cc instanceof MyTextFieldRenderer) {
                            MyTextFieldRenderer textField = (MyTextFieldRenderer) cc; //Grab the text of the renderer
                            if (textField.getText().contains(f.getText())) { //We got a match!
                                textForSearch = f.getText();
                                rowIndex = row;
                                int selRow = t.getSelectedRow();
                                //Selection code taken from here:
                                //https://stackoverflow.com/questions/853020/jtable-scrolling-to-a-specified-row-index/6229040#6229040
                                t.getSelectionModel().setSelectionInterval(row, row); //Scroll to the value
                                t.scrollRectToVisible(new Rectangle(t.getCellRect(row, 0, true)));
                                if (selRow!=-1) //Something was selected
                                    t.setRowSelectionInterval(selRow, selRow); //Restore selection
                                break;
                            }
                        }
                    }
                    if (rowIndex == tempRow) //Index didn't change, means there is no value for this search
                    {
                        Toolkit.getDefaultToolkit().beep();
                    }
                    t.repaint();
                }
            });
            c.gridy++;
            add(b,c);
        }
    }
    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                new Form().setVisible(true);
            }
        });
    }
}
George Z.
  • 6,643
  • 4
  • 27
  • 47
  • Selection not happening for me, i am using mac. – Molay May 14 '18 at 10:08
  • @Prajitha What do you mean not hapenning? When u have selected row 4, but you search something that is found in row 100, selection stays in row 4, but you see row 100. – George Z. May 14 '18 at 10:10
  • @Prajitha if you want to select the searched value, delete selRow variable, and the line t.setRowSelectionInterval(selRow, selRow); Selection will stay on the searched text. – George Z. May 14 '18 at 10:14
  • Sorry, my mistake, it's working fine. quick question, how can i add 1000 more records dynamically once it reaches to the bottom of the table and continue searching on it ? just trying to replicate the pagination here... – Molay May 14 '18 at 10:19
  • @Prajitha I can't understand what exactly you are asking. I added 500 more records to table's model and search continued where it was. – George Z. May 14 '18 at 10:25
  • 1555 you loaded as a initial records. Assume i have 1000 records as initial records on screen, once the search reaches 955, it's gonna start search over again from beginning, instead of this, i need to add 1000 more records to the existing table (now total 2000) and continue searching on it. – Molay May 14 '18 at 10:29
  • @Prajitha i added some dynamically with a button and it continues where it stopped. – George Z. May 14 '18 at 10:31
  • final question, how to remove those boxes (borders of JTextField) – Molay May 14 '18 at 10:37
  • @Prajitha you could find this your self with one min search in Google. Anyway just use in renderer this.setBorder (Borderfactory.createEmptyBorder ()); – George Z. May 14 '18 at 10:42
  • Thank you for your help. – Molay May 14 '18 at 10:48