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

Reply via email to