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: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists