Hi, Can anyone please tell me what I’m doing wrong below?
I need to use legacy JTables in our application because we have very customized renderers and dynamic column add/remove features and for the time being I have to keep what we have. Besides, from my limited research it seems the TableView and OutlineView are limited in their customization of renderers and being able to dynamically add/remove custom columns, etc? At least out of the box.. So, I thought I could basically make an adapter/bridge between the JTable selection and the ExplorerManager API so other components (ie. Properties, selection handling) can work elsewhere. But, for some reason I’m not getting my node published. I see the debug statements saying things SHOULD be published, but the Properties window isn’t showing anything. This is my TopComponent as a test: Thanks! — CODE —— /* * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license * Click nbfs://nbhost/SystemFileSystem/Templates/NetBeansModuleDevelopment-files/templateTopComponent637.java to edit this template */ package org.mullet.tables; import java.awt.BorderLayout; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.List; import javax.swing.JButton; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.table.TableRowSorter; import org.netbeans.api.settings.ConvertAsProperties; import org.openide.awt.ActionID; import org.openide.awt.ActionReference; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.windows.TopComponent; import org.openide.util.NbBundle.Messages; /** * Top component which displays something. */ @ConvertAsProperties( dtd = "-//org.mullet.tables//Tims//EN", autostore = false ) @TopComponent.Description( preferredID = "TimsTopComponent", //iconBase="SET/PATH/TO/ICON/HERE", persistenceType = TopComponent.PERSISTENCE_ALWAYS ) @TopComponent.Registration(mode = "explorer", openAtStartup = false) @ActionID(category = "Window", id = "org.mullet.tables.TimsTopComponent") @ActionReference(path = "Menu/Window" /*, position = 333 */) @TopComponent.OpenActionRegistration( displayName = "#CTL_TimsAction", preferredID = "TimsTopComponent" ) @Messages({ "CTL_TimsAction=Tims", "CTL_TimsTopComponent=Tims Window", "HINT_TimsTopComponent=This is a Tims window" }) public final class TimsTopComponent extends TopComponent implements ExplorerManager.Provider { private final ExplorerManager em = new ExplorerManager(); private final JTable table; private final PersonTableModel model; public TimsTopComponent() { // initComponents(); setLayout(new BorderLayout()); setName(Bundle.CTL_TimsTopComponent()); setToolTipText(Bundle.HINT_TimsTopComponent()); // Create a JTableModel List<Person> peopleList = List.of( new Person("Alice", 25), new Person("Bob", 30), new Person("Carol", 22) ); // Add the model to the JTable model = new PersonTableModel(peopleList); table = new JTable(model); // Set selection and sorting table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); TableRowSorter<PersonTableModel> sorter = new TableRowSorter<>(model); table.setRowSorter(sorter); // Hookup the JTable selection with explorer manager syncSelectionWithExplorer(); // Right-click menu on header for "Add Column..." JPopupMenu headerMenu = new JPopupMenu(); JMenuItem addColumnItem = new JMenuItem("Add Column…"); addColumnItem.addActionListener(e -> showAddColumnDialog()); headerMenu.add(addColumnItem); table.getTableHeader().addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if (SwingUtilities.isRightMouseButton(e)) { headerMenu.show(table.getTableHeader(), e.getX(), e.getY()); } } }); // Clicking the button doesn't do anything // I don't see anything in the Properties window JButton debugButton = new JButton("Force Node"); debugButton.addActionListener(e -> { Person p = new Person("Zed", 99); PersonNode node = new PersonNode(p); try { em.setSelectedNodes(new Node[]{node}); } catch (Exception ex) { ex.printStackTrace(); } }); add(debugButton, BorderLayout.SOUTH); // Hookup the explorer manager associateLookup(ExplorerUtils.createLookup(em, getActionMap())); // Set the nodes em.setRootContext(new AbstractNode(new PersonChildren(peopleList))); // Add the table add(new JScrollPane(table), BorderLayout.CENTER); } /** * Sync the JTable selection to publish using the explorer manager * so we can use built in components/selection management * * NOTE: I see all the debug come out but nothing shows up in the Properties window */ private void syncSelectionWithExplorer() { table.getSelectionModel().addListSelectionListener((ListSelectionEvent e) -> { if (!e.getValueIsAdjusting()) { int viewRow = table.getSelectedRow(); if (viewRow >= 0) { int modelRow = table.convertRowIndexToModel(viewRow); Person selected = model.getPersonAt(modelRow); System.out.println("[DEBUG] Table row selected: " + selected.getName()); Node selectedNode = new PersonNode(selected); System.out.println("[DEBUG] Created node: " + selectedNode.getDisplayName()); System.out.println("[DEBUG] Node properties:"); for (Node.PropertySet ps : selectedNode.getPropertySets()) { for (Node.Property<?> prop : ps.getProperties()) { try { System.out.println(" - " + prop.getName() + " = " + prop.getValue()); } catch (Exception ex) { System.out.println(" - " + prop.getName() + " [ERROR getting value]"); } } } try { em.setSelectedNodes(new Node[] { selectedNode }); System.out.println("[DEBUG] ExplorerManager setSelectedNodes() called"); } catch (Exception ex) { ex.printStackTrace(); } } else { System.out.println("[DEBUG] Table row deselected"); try { em.setSelectedNodes(new Node[0]); } catch (Exception ex) { ex.printStackTrace(); } } } }); } private void showAddColumnDialog() { String name = JOptionPane.showInputDialog(this, "Enter column name:"); if (name != null && !name.isBlank()) { model.addComputedColumn(name.trim(), p -> { System.out.println("[DEBUG] Computing value for: " + p.getName() + " with column: " + name); return "[" + p.getName().toUpperCase() + ":" + name.length() + "]"; }); } } @Override public ExplorerManager getExplorerManager() { return em; } /** * 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. */ // <editor-fold defaultstate="collapsed" desc="Generated Code"> private void initComponents() { javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 400, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 300, Short.MAX_VALUE) ); }// </editor-fold> // Variables declaration - do not modify // End of variables declaration @Override public void componentOpened() { // TODO add custom code on component opening } @Override public void componentClosed() { // TODO add custom code on component closing } void writeProperties(java.util.Properties p) { // better to version settings since initial version as advocated at // http://wiki.apidesign.org/wiki/PropertyFiles p.setProperty("version", "1.0"); // TODO store your settings } void readProperties(java.util.Properties p) { String version = p.getProperty("version"); // TODO read your settings according to their version } } — PersonNode package org.mullet.tables; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Sheet; import org.openide.util.lookup.Lookups; import org.openide.nodes.PropertySupport; public class PersonNode extends AbstractNode { private final Person person; public PersonNode(Person person) { // Explorer views rely on the Node to be lookup-aware (for Properties window) super(Children.LEAF, Lookups.singleton(person)); this.person = person; setDisplayName(person.getName()); // Optional: name shown in tree column } @Override protected Sheet createSheet() { Sheet sheet = Sheet.createDefault(); // creates root Sheet object Sheet.Set set = Sheet.createPropertiesSet(); // default "properties" set try { // These must match getName() / getAge() in your Person class set.put(new PropertySupport.Reflection<>(person, String.class, "getName", null)); set.put(new PropertySupport.Reflection<>(person, int.class, "getAge",null)); } catch (NoSuchMethodException e) { throw new RuntimeException("Failed to reflect on Person bean", e); } sheet.put(set); return sheet; } } — PersonChildren package org.mullet.tables; import java.util.List; import org.openide.nodes.Children; import org.openide.nodes.Node; public class PersonChildren extends Children.Keys<Person> { private final List<Person> data; public PersonChildren(List<Person> people) { this.data = people; } @Override protected void addNotify() { setKeys(data); } @Override protected Node[] createNodes(Person person) { System.out.println("DEBUG => Creating a person Node"); return new Node[] { new PersonNode(person) }; } } --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@netbeans.apache.org For additional commands, e-mail: users-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists