Check all CheckBoxTreeCells in a TreeView JavaFX - java

I have created a TreeView that contains many CheckBoxTreeCells. I also have a Button that I would like to check all of the CheckBoxTreeCells. I've been reading this tutorial, and I am a bit lost. Here is what I have so far:
Button Code:
public void fooMethod() {
/*selectAll is an instance variable*/
selectAll = new Button("Select All");
selectAll.setOnMouseClicked(e -> handleSelectAllButtonAction(e));
}
private void handlSelectAllButtonAction(MouseEvent e) {
/*Code goes here*/
}
TreeView Code:
public void fooMethod2() {
/*myTreeView is also an insance variable*/
myTreeView = new TreeView<String>();
CheckBoxTreeItem<String> root = new CheckBoxTreeItem<>();
CheckBoxTreeItem<String> branch1 = new CheckBoxTreeItem<>("Branch 1");
CheckBoxTreeItem<String> branch2 = new CheckBoxTreeItem<>("Branch 2");
CheckBoxTreeItem<String> branch3 = new CheckBoxTreeItem<>("Branch 3");
root.getChildren.add(branch1);
root.getChildren.add(branch2);
root.getChildren.add(branch3);
myTreeView.setCellFactory(CheckBoxTreeCell.forTreeView());
myTreeView.setRoot(root);
myTreeView.setShowRoot(false);
myTreeView.setEditable(true);
}
The example provided in the link is a bit more complex than what I need, and I think it is confusing me. How do I edit a CheckBoxTreeItems in a TreeView?

Unless there are independent CheckBoxTreeItems, its enough to select the root:
root.setSelected(true);
since this automatically selects the children.

Best way I could find was to do this:
private void handlSelectAllButtonAction(MouseEvent e) {
CheckBoxTreeItem<String> root = (CheckBoxTreeItem<String>)myTreeView.getRoot();
int numBranches = root.getChildren().size();
for(int i = 0; i < numBranches; i++) {
if(((CheckBoxTreeItem<String>)root.getChildren().get(i)).isSelected()) {
((CheckBoxTreeItem<String>)root.getChildren().get(i)).setSelected(false);
}
}
}

Related

Errors when sorting a tableview

Simply put, I have a tableview with a single column which I want to be sorted and stay sorted whenever I add a new item to the observable list. I've looked at several examples and similar questions online but nothing is working for me. If I try calling TableView.sort() from the list's change listener, nothing really seems to happen.
When I add the column to the table's sort order that's when things get problematic. Attempting to sort the table after that results in a hard crash of the application. The only error is "Exception in Application start method". I then moved the sort() call to Platform.runLater() and that prevents the app from crashing. But then a new problem arose. When calling sort I'll get an array index out of bounds error, which seems to happen at random though I think mostly when the new items exceeds the table's view and needs a scrollbar.
While writing this, I moved the sort out of the change listener and down below into the Task where I'm adding the actual data. No more errors. So I guess my new question is, why am I having so many issues when attempting to do things from the listener?
public class Temp extends Application{
private ObservableList<String> libraryList = FXCollections.observableArrayList();
public void start(Stage stage) {
Label statusLabel = new Label("stuff goes here");
TableView<String> table = new TableView<String>(libraryList);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
TableColumn<String, String> col = new TableColumn<String, String>("Stuff");
col.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue()));
table.getColumns().add(col);
table.getSortOrder().add(col);
libraryList.addListener(new ListChangeListener<String>() {
public void onChanged(Change change) {
Platform.runLater(()->{
table.sort();
statusLabel.setText(libraryList.size()+" entries");
});
}
});
// dummy stuff
libraryList.add("foo");
libraryList.add("bar");
Button b = new Button("Press Me");
b.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
FileTask task = new FileTask();
new Thread(task).start();
}
});
BorderPane mainBody = new BorderPane();
mainBody.setTop(statusLabel);
mainBody.setCenter(table);
mainBody.setBottom(b);
Scene scene = new Scene(mainBody);
stage.setScene(scene);
stage.show();
}
class FileTask extends Task<Boolean>{
public FileTask(){
}
protected Boolean call() throws Exception{
Random rand = new Random();
for(int i = 0; i < 3; i++) {
String s = ""+rand.nextInt(Integer.MAX_VALUE);
libraryList.add(s);
}
return true;
}
}
public static void main(String[] args) {
Application.launch(args);
}
}
I believe I understand the problem now. When I add an item to the list it calls onChanged(). When I sort the the table, it's also triggering the onChanged thus creating an infinite loop. I didn't realize the table sorts the data by actually changing the list itself.

Introspection (JavaFx)

after having made quite a lot of searches, I leave it up to you.
Here is in my application JavaFx, I use the introspection to generate a gridPane automatically (that I insert then into Dialog). Thus I have TableView, when the user doubles click above, it generates Dialog containing the columns of this TableView.
In this Dialog, thus there is TextFields which allow to modify the values of fields in TableView.
But well, I cannot get back the value of my attributes by means of the introspection, and how make get back the value of the textFields which were created thanks to the introspection?
There is my introspection method :
public static GridPane analyserChamp(Etudiant etu) {
List<String> list = new ArrayList<>();
Class<? extends Etudiant> classPixel = etu.getClass();
Field attribut[] = classPixel.getDeclaredFields();
GridPane gp = new GridPane();
int i=0;
for(Field p : attribut) {
list.add(p.getName());
Label lab = new Label();
if(!p.getName().equals("classe")) {
TextField l = new TextField();
lab.setText(p.getName());
gp.add(l, 1, i);
}else {
ComboBox<String> cb = new ComboBox<String>();
cb.getItems().addAll("1Bi","2Bi","3Bi");
gp.add(cb, 1, i);
}
gp.add(lab, 0, i);
i++;
}
return gp;
}
Here is the code where I call on to the method of introspection :
if(e.getClickCount() == 2) {
Dialog<Etudiant> dialog = new Dialog<>();
Etudiant test = tableViewEtudiant.getSelectionModel().getSelectedItems().get(0);
if(test!=null) {
dialog.setTitle("Editor");
dialog.setHeaderText("You can update your question");
dialog.getDialogPane().setContent(Analysateur.analyserChamp(test));
ButtonType buttonCancel = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
ButtonType buttonOk = new ButtonType("Ok", ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(buttonOk,buttonCancel);
//Confirmation of the edition
Optional<Etudiant> result = dialog.showAndWait();
//Edition of the question in the gson file
GridPane tmp = Analysateur.analyserChamp(test);
if(result.isPresent()) {
// Here ?????
}
}
Thank in advance ;)
There are many ways of solving this, for example, you can use the userData property to store the key of the attributes, so you can iterate later over the GridPane children and obtain each value in the Dialog result converter.
When you introspect the class Etudiant:
if(!p.getName().equals("classe")) {
TextField l = new TextField();
l.setUserData(p.getName()); //Store the attribute name in the TextField
lab.setText(p.getName());
gp.add(l, 1, i);
}else {
ComboBox<String> cb = new ComboBox<String>();
cb.setUserData(p.getName()); //Store the attribute name in the ComboBox
cb.getItems().addAll("1Bi","2Bi","3Bi");
gp.add(cb, 1, i);
}
When you creat the Dialog:
Dialog<Etudiant> dialog = new Dialog<>();
...
GridPane content = Analysateur.analyserChamp(test); //Keep the content accesible
...
dialog.getDialogPane().setContent(content);
...
dialog.setResultConverter(button -> { //Convert the result
Etudiant result = new Etudiant();
for (Node child : content.getChildren()) { //Iterate over the GridPane children
if (child instanceof TextField) {
String attribute = ((TextField)child).getUserData();
String value = ((TextField)child).getTest();
//Set the value in the result attribute via instrospection
}
if (child instanceof ComboBox) {
//Do the same with combos
}
}
});
Store a Supplier for getting the value of the input for a field in a Map<Field, Supplier<?>>. This way you could go through the entries of the map and retrieve the values for the assignments:
public class ReflectionDialog<T> extends Dialog<T> {
public ReflectionDialog(Class<T> type, Supplier<T> factory) throws IllegalAccessException {
GridPane gp = new GridPane();
Field[] fields = type.getDeclaredFields();
// stores getters for result value
final Map<Field, Supplier<?>> results = new HashMap<>();
int i = 0;
for (Field field : fields) {
if (String.class.equals(field.getType())) {
String name = field.getName();
Node input;
Supplier<?> getter;
if ("classe".equals(name)) {
ComboBox<String> cb = new ComboBox<>();
cb.getItems().addAll("1Bi", "2Bi", "3Bi");
getter = cb::getValue;
input = cb;
} else {
TextField l = new TextField();
getter = l::getText;
input = l;
}
results.put(field, getter);
gp.addRow(i, new Label(name), input);
i++;
}
}
getDialogPane().setContent(gp);
getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
setResultConverter(buttonType -> {
if (buttonType == ButtonType.OK) {
// create & initialize new object
final T object = factory.get();
results.forEach((k, v) -> {
try {
k.set(object, v.get());
} catch (IllegalAccessException ex) {
throw new IllegalStateException(ex);
}
});
return object;
} else {
return null;
}
});
}
}
public class A {
String classe;
String value;
#Override
public String toString() {
return "A{" + "classe=" + classe + ", value=" + value + '}';
}
}
ReflectionDialog<A> dialog = new ReflectionDialog<>(A.class, A::new);
A result = dialog.showAndWait().orElse(null);
System.out.println(result);

Java - Select all item in the menu

I'm using the javax.swing library, and I try to solve this problem:
I have a MenuBar in which I created JMenu, this menu has JCheckBoxMenuItem items. Like this:
//creating objects:
jMenuBar = new javax.swing.JMenuBar();
jMenuAlgorithms = new javax.swing.JMenu();
jCheckBoxSPEA = new javax.swing.JCheckBoxMenuItem();
jCheckBoxNSGAII = new javax.swing.JCheckBoxMenuItem();
jSeparator1 = new javax.swing.JPopupMenu.Separator();
jCheckBoxMenuEnableAll = new javax.swing.JCheckBoxMenuItem();
jCheckBoxMenuDisableAll = new javax.swing.JCheckBoxMenuItem();
//settings and putting them together:
jCheckBoxSPEA.setSelected(true);
jCheckBoxSPEA.setText("SPEA");
jMenuAlgorithms.add(jCheckBoxSPEA);
jCheckBoxNSGAII.setSelected(true);
jCheckBoxNSGAII.setText("NSGAII");
jMenuAlgorithms.add(jCheckBoxNSGAII);
jMenuAlgorithms.add(jSeparator1);
jCheckBoxMenuEnableAll.setSelected(true);
jCheckBoxMenuEnableAll.setText("Enable all");
jCheckBoxMenuEnableAll.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
jCheckBoxMenuEnableAllMouseClicked(evt);
}
});
jMenuAlgorithms.add(jCheckBoxMenuEnableAll);
jCheckBoxMenuDisableAll.setText("Disable all");
jMenuAlgorithms.add(jCheckBoxMenuDisableAll);
jMenuBar.add(jMenuAlgorithms);
If the user selects jCheckBoxMenuEnableAll item, I would like to select all the items above the separator. If he selects jCheckBoxMenuDisableAll, I would like to deselect all the items above the separator.
As you can see, I've added mouseClicked action to the jCheckBoxMenuEnableAll item. Now, I would like to do something like this:
private void jCheckBoxMenuEnableAllMouseClicked(java.awt.event.MouseEvent evt) {
for(JCheckBoxMenuItem item : jMenuAlgorithms){
item.setSelected(true);
}
//deselect then jCheckBoxMenuDisableAll, it's not essential for instance
}
But apparently, I can't do the for loop like this, as the menu item isn't an array or Iterable.
Well, just for testing, I had done something very stupid (code below) - I pass all the items in the menu, and if the item is a check box, I make its copy, set ist value to "true" (selected) and then replace the original item by its copy. Very stupid, I know and I absolutely don't want to do like this, however, I didn't find another way to do it. I just wanted to see if this would work. I supposed it should, but it stoll doesn't work. What am I doing wrong? Thank you very, very much for your time.
private void jCheckBoxMenuEnableAllMouseClicked(java.awt.event.MouseEvent evt) {
if(jCheckBoxMenuEnableAll.isSelected()){
for(int i =0; i< jMenuAlgorithms.getItemCount(); i++){ //for all items in the menu, separators included
if(jMenuAlgorithms.getItem(i) instanceof JCheckBoxMenuItem){
JCheckBoxMenuItem item = ((JCheckBoxMenuItem)jMenuAlgorithms.getItem(i));
item.setSelected(true);
jMenuAlgorithms.insert(item, i);
}
}
}
}
I think JPopupMenu#getSubElements() is what you are looking for.
see also: JMenu#getSubElements()
Returns an array of MenuElements containing the submenu for this menu
component. If popup menu is null returns an empty array. This method
is required to conform to the MenuElement interface. Note that since
JSeparators do not conform to the MenuElement interface, this array
will only contain JMenuItems.
import java.awt.*;
import java.util.Arrays;
import java.util.stream.Stream;
import javax.swing.*;
public class MenuSubElementsTest {
public JComponent makeUI() {
JMenu jMenuAlgorithms = new JMenu("MenuAlgorithms");
JMenuItem jCheckBoxMenuEnableAll = new JMenuItem("Enable all");
jCheckBoxMenuEnableAll.addActionListener(e -> {
for (MenuElement me: jMenuAlgorithms.getPopupMenu().getSubElements()) {
System.out.println("debug1: " + me.getClass().getName());
if (me instanceof JCheckBoxMenuItem) {
((JCheckBoxMenuItem) me).setSelected(true);
}
}
//or: getJCheckBoxMenuItem(jMenuAlgorithms).forEach(r -> r.setSelected(true));
});
JMenuItem jCheckBoxMenuDisableAll = new JMenuItem("Disable all");
jCheckBoxMenuDisableAll.addActionListener(e -> {
getJCheckBoxMenuItem(jMenuAlgorithms).forEach(r -> r.setSelected(false));
});
jMenuAlgorithms.add(new JCheckBoxMenuItem("SPEA", true));
jMenuAlgorithms.add(new JCheckBoxMenuItem("NSGAII", true));
jMenuAlgorithms.addSeparator();
jMenuAlgorithms.add(jCheckBoxMenuEnableAll);
jMenuAlgorithms.add(jCheckBoxMenuDisableAll);
JMenuBar jMenuBar = new JMenuBar();
jMenuBar.add(jMenuAlgorithms);
JPanel p = new JPanel(new BorderLayout());
p.add(jMenuBar, BorderLayout.NORTH);
return p;
}
private static Stream<JCheckBoxMenuItem> getJCheckBoxMenuItem(MenuElement p) {
Class<JCheckBoxMenuItem> clz = JCheckBoxMenuItem.class;
return stream(p).filter(clz::isInstance).map(clz::cast);
}
// public static Stream<MenuElement> stream(MenuElement p) {
// return Arrays.stream(p.getSubElements())
// .map(MenuSubElementsTest::stream).reduce(Stream.of(p), Stream::concat);
// }
public static Stream<MenuElement> stream(MenuElement p) {
return Stream.concat(Stream.of(p), Arrays.stream(p.getSubElements())
.peek(me -> System.out.println("debug2: " + me.getClass().getName()))
.flatMap(MenuSubElementsTest::stream));
}
public static void main(String... args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new MenuSubElementsTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}

How would I go about Streamlining this code?

I have been following an online tutorial on how to use JavaFX to create Java Gui programs.
This is one of the programs I made following using the tutorials. I decided to add a counter to the command line which would display the number of orders as well as the customers choices, although I was able to get this working the counter feature looks inefficient.
I feel adding another class to just add the orderNumber variable was wrong.
Is there any advice on streamlining this code?
Also what could I do to output the orderNumber after the Order?
public class Main extends Application {
private class Order {
public int orderNumber = 0;
}
Stage window;
Button button;
public static void main(String[] args) {
launch(args);
}
#Override
public void start (Stage primaryStage) throws Exception {
Order Number = new Order();
window = primaryStage;
window.setTitle("Luke's Sandwich's");
//Check Box's
CheckBox box1 = new CheckBox("cheese");
CheckBox box2 = new CheckBox("Bacon");
CheckBox box3 = new CheckBox("Tuna");
CheckBox box4 = new CheckBox("Tomatoes");
box1.setSelected(true);
//button
button = new Button("Order Now");
button.setOnAction(e -> {
Number.orderNumber++;
System.out.println("order: " + Number.orderNumber);
handleOptions(box1,box2,box3,box4);
});
VBox layout = new VBox(10);
layout.setPadding(new Insets(20));
layout.getChildren().addAll(box1,box2,box3,box4,button);
Scene scene = new Scene(layout,300,250);
window.setScene(scene);
window.show();
}
//Handle checkbox options
public void handleOptions (CheckBox box1,CheckBox box2,CheckBox box3,CheckBox box4){
String message = "user order:\n";
if(box1.isSelected())
message += "cheese\n";
if(box2.isSelected())
message += "bacon\n";
if(box3.isSelected())
message += "tuna\n";
if(box4.isSelected())
message += "tomatoes\n";
System.out.println(message);
}
}

TreeView nodes names bugging

I'm having a lot of trouble dealing with TreeView, more specificcaly with the text shown per node.
Starting with an initializer for the Tree, where I expected a single node with the text, it might make the program more intuitive for the users:
private void defineEmpityTree(){
cbt.setModel(new DefaultTreeModel(
new DefaultMutableTreeNode("Relat\u00F3rios Individuais") {
{
}
}
));
}
and I add initialize the Tree:
cbt = new JCheckBoxTree();
defineEmpityTree();
scrollPane.setViewportView(cbt);
"Relatórios Individuais" aren't shown, Tree Ok, another bug
I ignored this problem and continued with the actual filling of the nodes, the user specifies his search and press "ok", thats when we get the Tree filled, 2nd image.
But then then another strange problem comes, if "ok" is pressed again, some texts go strange, 3rd image.
Heres the part of the cade where the nodes are created. For an array of files nodes are created for each type of report and each report lot, its not really important.
private void defineNewTree(ArrayList<File> files){
DefaultMutableTreeNode dmtRoot = new DefaultMutableTreeNode("Relat\u00F3rios Individuais");
DefaultMutableTreeNode dmtSubFolder = null;
DefaultMutableTreeNode dmtLotFolder = null;
int childsRoot = 0;
int childsSub = 0;
for(File f : files){
String subFolder = f.getName().substring(17,f.getName().length()-4);
String name = f.getName();
String lot = f.getName().substring(0, 3);
childsRoot = dmtRoot.getChildCount();
boolean subFoldExists = false;
boolean foldLotExists = false;
//creatingo folder reports:
for(int i = 0;i<childsRoot;i++){
if(dmtRoot.getChildAt(i).toString().equals(subFolder)){
dmtSubFolder = (DefaultMutableTreeNode) dmtRoot.getChildAt(i);
subFoldExists = true;
i=childsRoot;
}
}
if(!subFoldExists){
dmtSubFolder = new DefaultMutableTreeNode(subFolder);
dmtRoot.add(dmtSubFolder);
}
for(int j = 0;j<childsSub;j++){
if(dmtSubFolder.getChildAt(j).toString().equals(lot)){
dmtLotFolder = (DefaultMutableTreeNode) dmtSubFolder.getChildAt(j);
foldLotExists = true;
j=childsSub;
}
}
if(!foldLotExists){
dmtLotFolder = new DefaultMutableTreeNode("lote "+lot);
dmtSubFolder.add(dmtLotFolder);
}
dmtLotFolder.add(new DefaultMutableTreeNode(name));
}
DefaultTreeModel myTree = new DefaultTreeModel(dmtRoot);
cbt.setModel(myTree);
}
I think the real problem is that:
cbt.setModel(myTree);
Is that the correct way to define the Tree contents?
Edit.:
Button OK:
...
JButton btnOk = new JButton("OK");
btnOk.setBounds(161, 37, 49, 23);
btnOk.setActionCommand("ok");
btnOk.addActionListener(buttonListener);
panel.add(btnOk);
...
class ButtonListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent a) {
switch(a.getActionCommand()){
case "ok":
search();
self.requestFocusInWindow();
break;
case "cancel":
dispose();
break;
case "print":
TreePath[] tp = cbt.getCheckedPaths();
for(TreePath t : tp){
System.out.println(t.getLastPathComponent().toString());
}
self.requestFocusInWindow();
break;
default:
break;
}
}
}
private void search(){
FileSeeker fs = new FileSeeker(textField.getText());
ArrayList<File> files = fs.getFiles();
defineNewTree(files);
}
Edit.:
CheckBoxCellRenderer:
private class CheckBoxCellRenderer extends JPanel implements TreeCellRenderer {
private static final long serialVersionUID = -7341833835878991719L;
JCheckBox checkBox;
public CheckBoxCellRenderer() {
super();
this.setLayout(new BorderLayout());
checkBox = new JCheckBox();
add(checkBox, BorderLayout.CENTER);
setOpaque(false);
}
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
Object obj = node.getUserObject();
TreePath tp = new TreePath(node.getPath());
CheckedNode cn = nodesCheckingState.get(tp);
if (cn == null) {
return this;
}
checkBox.setSelected(cn.isSelected);
checkBox.setText(obj.toString());
checkBox.setOpaque(cn.isSelected && cn.hasChildren && ! cn.allChildrenSelected);
return this;
}
}
So, for the first problem the solution is:
private void defineEmpityTree(){
DefaultMutableTreeNode dmtn = new DefaultMutableTreeNode("Relat\u00F3rios Individuais");
cbt.setModel(new DefaultTreeModel(dmtn));
((DefaultTreeModel) cbt.getModel()).nodeChanged(dmtn);
}
I simply had to notify that the node had changed.
For the truncation problem,
cbt.setLargeModel(true);
did the trick.

Categories