Ján Srniček
Ján Srniček

Reputation: 525

Preventing TableCellEditor to show Class' descriptor

I'm getting a strange bug. I have my song's list on the right side of a JFrame. When I click once on any item it does what i want: set color to Gray. But on double click it shows the Class' descriptor of my Custom table cell.

When I click once (correct)

enter image description here

When I click twice (bug)

enter image description here

Heres my code

PanelItem class

package dierplayer;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;

/**
 * @author srnec
 */
public class PanelItem extends javax.swing.JPanel implements TableCellRenderer {

    private Thread focusThread;
    private int startPosX, startPosY;

    public PanelItem() {
        initComponents();
        this.setPreferredSize(new Dimension(20, 30));
        startPosX = songLabel.getX();
        startPosY = songLabel.getY();
    }

    /**
     * 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">                          
    private void initComponents() {

        songLabel = new javax.swing.JLabel();

        songLabel.setText("Song");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(songLabel)
                .addGap(0, 88, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(songLabel)
                .addGap(0, 45, Short.MAX_VALUE))
        );
    }// </editor-fold>                        


    // Variables declaration - do not modify                     
    private javax.swing.JLabel songLabel;
    // End of variables declaration                   

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

        this.setSelected(isSelected);

        return this;

    }

    public void setSelected(boolean isSelected) {
        if (isSelected) {
            this.setBackground(Color.GRAY);
        } else {
            this.setBackground(Color.WHITE);
        }
    }
}   

PanelItemEditor class

package dierplayer;

import java.awt.Component;
import java.util.EventObject;
import javax.swing.JTable;
import javax.swing.event.CellEditorListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

/**
 * @author srnec
 */
public class PanelItemEditor extends DefaultTableModel implements  TableCellEditor{


    @Override
    public boolean isCellEditable(int row,int column)
    {
        return false;
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public Object getCellEditorValue() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public boolean isCellEditable(EventObject anEvent) {
     return false;
    }

    @Override
    public boolean shouldSelectCell(EventObject anEvent) {
       return true;
    }

    @Override
    public boolean stopCellEditing() {
        return true;
    }

    @Override
    public void cancelCellEditing() {

    }

    @Override
    public void addCellEditorListener(CellEditorListener l) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void removeCellEditorListener(CellEditorListener l) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

}

MainContext class

package dierplayer;

import java.io.File;
import java.util.Vector;
import javax.swing.JFileChooser;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

/**
 * @author srnec
 */
public class MainContext extends javax.swing.JFrame 
{

    private DierPlayer mainPlayer;
    public MainContext() 
    {
        initComponents();
        mainPlayer=new DierPlayer();

        songList.setCellEditor(new PanelItemEditor());
        songList.getColumnModel().getColumn(0).setCellRenderer(new PanelItem());

        DefaultTableModel dtm = (DefaultTableModel) songList.getModel();

        Vector v = new Vector();
        v.add(new PanelItem());
        dtm.addRow(v);  
        v.clear();
        v.add(new PanelItem());
        dtm.addRow(v);
        v.clear();
        v.add(new PanelItem());
        dtm.addRow(v);

    }


    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        progressPanel = new javax.swing.JPanel();
        jScrollPane2 = new javax.swing.JScrollPane();
        songList = new javax.swing.JTable();
        MainMenuBar = new javax.swing.JMenuBar();
        jMenu1 = new javax.swing.JMenu();
        OpenMenuItem = new javax.swing.JMenuItem();
        jMenu2 = new javax.swing.JMenu();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        progressPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());

        javax.swing.GroupLayout progressPanelLayout = new javax.swing.GroupLayout(progressPanel);
        progressPanel.setLayout(progressPanelLayout);
        progressPanelLayout.setHorizontalGroup(
            progressPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 582, Short.MAX_VALUE)
        );
        progressPanelLayout.setVerticalGroup(
            progressPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 55, Short.MAX_VALUE)
        );

        songList.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {
                "Title 1"
            }
        ));
        jScrollPane2.setViewportView(songList);

        jMenu1.setText("File");

        OpenMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK));
        OpenMenuItem.setText("Open");
        OpenMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                OpenMenuItemActionPerformed(evt);
            }
        });
        jMenu1.add(OpenMenuItem);

        MainMenuBar.add(jMenu1);

        jMenu2.setText("Edit");
        MainMenuBar.add(jMenu2);

        setJMenuBar(MainMenuBar);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(progressPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 179, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(0, 0, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 488, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(progressPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        );

        pack();
    }// </editor-fold>                        

    private void OpenMenuItemActionPerformed(java.awt.event.ActionEvent evt) {                                             
        JFileChooser chooser = new JFileChooser();
        chooser.setMultiSelectionEnabled(true);
        chooser.showOpenDialog(this);
        File[] files = chooser.getSelectedFiles();

    }                                            

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(MainContext.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(MainContext.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(MainContext.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(MainContext.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new MainContext().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JMenuBar MainMenuBar;
    private javax.swing.JMenuItem OpenMenuItem;
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenu jMenu2;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JPanel progressPanel;
    private javax.swing.JTable songList;
    // End of variables declaration                   
}

Upvotes: 3

Views: 183

Answers (2)

dic19
dic19

Reputation: 17971

You have stated in a comment:

On the double click,the song that is represented by that specific table cell will be played,also on right click there will be some menu shown(remove,...).

I think you're mixing up the concepts of TableCellRenderer and TableCellEditor (see Concepts: Editors and Renderers). The cell renderer should display some text, let's say the song's title, while the cell editor should allow users to modify this value. Since cell editor is automatically called when user double-clicks (or press F2 key) on a cell then providing a cell editor is not what you want. To achieve your goal you would have to follow this hints:

Prevent editing the cells

Since you want to play a song when user double-clicks on a cell then you must prevent the cell be editable (so the cell editor will not be called):

    DefaultTableModel model = new DefaultTableModel(new Object[]{"Songs list"}, 0) {
        @Override
        public boolean isCellEditable(int row, int column) {
            return false;
        }
    };

Provide a cell renderer to display the song's name

You have done this step already. Note if the only thing it will do is change the background/selection colors then you can get rid of it and simply do as follows:

    table.setBackground(Color.WHITE);
    table.setSelectionBackground(Color.GRAY);

Attach a MouseListener to the table

In order to listen double-clicks and right-clicks from user you need to attach a MouseListener to the table:

    table.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
            if(e.isPopupTrigger()) {
                // show JPopupMenu here
            } else if(e.getClickCount() >= 2){
                // play the song here
            }
        }
    });

You might want to take a look to:


Example

Here's a little example you can use as a start point. Some notes:

  • IPlayableSong: this interface establishes a contract to create a self-playable song object. It's up to the implementation the way on how are these methods implemented (f.i.: library used, multithreading if needed, etc).

  • Song: it's a default implementation of previous interface. As you can see it doesn't really implement anything (throws UnsupportedOperationException) because I don't have any library to play MP3 files. But hope you get the idea.

  • Use of SwingWorker: you'll see I've used a SwingWorker to create the songs and add them to the table's model. This is because you want to get some ID3 tags from the MP3 files which implies a lot of IO operations. This kind of operations could be very time consuming and may block the Event Dispatch Thread (a.k.a. EDT) causing the GUI becomes unresponsive (freezing). So the swing worker performs these operations in a background thread and update the table model in the EDT. See more in Concurrency in Swing trial.

Code

import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;

public class Demo {

    private JTable table;
    private IPlayableSong currentPlayingSong;

    private void createAndShowGUI() {

        DefaultTableModel model = new DefaultTableModel(new Object[]{"Songs list"}, 0) {
            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };

        table = new JTable(model);
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                Color background = isSelected ? Color.GRAY : Color.WHITE;
                setBackground(background);

                if(value instanceof Song) {
                    Song song = (Song)value;
                    setText(song.getTitle());
                }
                return this;
            }
        });

        table.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if(e.isPopupTrigger()) {
                    // show JPopupMenu here
                } else if(e.getClickCount() >= 2){
                    int row = table.rowAtPoint(e.getPoint());
                    int column = table.columnAtPoint(e.getPoint());

                    if(currentPlayingSong != null) {
                        currentPlayingSong.stop();
                    }

                    IPlayableSong song = (IPlayableSong)table.getValueAt(row, column);
                    song.play();
                    currentPlayingSong = song;
                }
            }

        });

        JMenuItem addDir = new JMenuItem("Add dir...");
        addDir.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                addSongs();
            }
        });

        JMenu fileMenu = new JMenu("File");
        fileMenu.add(addDir);

        JMenuBar menuBar = new JMenuBar();
        menuBar.add(fileMenu);

        JFrame frame = new JFrame("Demo");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setJMenuBar(menuBar);
        frame.getContentPane().add(new JScrollPane(table));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void addSongs() {
        final List<File> fileList = new ArrayList<>();

        File file = new File(System.getProperty("user.dir"));
        JFileChooser fileChooser = new JFileChooser(file);
        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);        
        fileChooser.setMultiSelectionEnabled(true);
        fileChooser.setAcceptAllFileFilterUsed(false);
        fileChooser.setDialogTitle("Please select a directory");

        if(fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION && fileChooser.getSelectedFile().isDirectory()){
            File selectedDirectory = fileChooser.getSelectedFile();
            FilenameFilter filenameFilter = new FilenameFilter() {
                @Override
                public boolean accept(File dir, String name) {
                    return name.toUpperCase().endsWith("MP3");
                }
            };
            fileList.addAll(Arrays.asList(selectedDirectory.listFiles(filenameFilter)));

            DefaultTableModel model = (DefaultTableModel)table.getModel();
            model.setRowCount(0);

            SwingWorker<Void, Song> worker = new SwingWorker<Void, Song>() {
                @Override
                protected Void doInBackground() throws Exception {

                    for(File aFile : fileList) {
                        Song song = new Song(aFile, aFile.getName());
                        publish(song);
                    }
                    return null;
                }

                @Override
                protected void process(List<Song> chunks) {
                    DefaultTableModel model = (DefaultTableModel)table.getModel();
                    for(Song song: chunks) {
                        model.addRow(new Object[]{song});
                    }
                }
            };
            worker.execute();
        }

    }

    public interface IPlayableSong {

        public void play();

        public void stop();
    }

    public class Song implements IPlayableSong {

        private final String title;
        private final File file;

        public Song(File file, String title){
            this.file = file;
            this.title = title;
        }

        public String getTitle() {
            return title;
        }

        @Override
        public void play() {
            String message = String.format("Not supported yet! Trying to play %s", title);
            throw new UnsupportedOperationException(message);
        }

        @Override
        public void stop() {
            String message = String.format("Not supported yet! Trying to stop %s", title);
            throw new UnsupportedOperationException(message);
        }
    }

    public static void main(String[] args) {   
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {                
                new Demo().createAndShowGUI();
            }
        });
    }

}

Upvotes: 1

Aung Thet
Aung Thet

Reputation: 3640

I don't know exactly about your case but this is my suggest ...use that model in songList

songList.setModel(new model());

TableModel class "model"

class model extends DefaultTableModel{

model(){

super( new Object [][] {

     },
        new String [] {
            "Title 1"
        }
    );
}
    public boolean isCellEditable(int row,int column){
        return false;
    }
}

Upvotes: 0

Related Questions