241 lines
8.8 KiB
Java
241 lines
8.8 KiB
Java
/*
|
|
* Copyright (C) 2015 The 8-Bit Bunch. Licensed under the Apache License, Version 1.1
|
|
* (the "License"); you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at <http://www.apache.org/licenses/LICENSE-1.1>.
|
|
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
* ANY KIND, either express or implied. See the License for the specific language
|
|
* governing permissions and limitations under the License.
|
|
*/
|
|
|
|
package org.badvision.outlaweditor.ui;
|
|
|
|
import static org.badvision.outlaweditor.data.DataUtilities.uppercaseFirst;
|
|
|
|
import java.beans.BeanInfo;
|
|
import java.beans.IntrospectionException;
|
|
import java.beans.Introspector;
|
|
import java.beans.PropertyDescriptor;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
import javafx.application.Platform;
|
|
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
|
|
import javafx.beans.value.ObservableValue;
|
|
import javafx.collections.FXCollections;
|
|
import javafx.collections.ObservableList;
|
|
import javafx.geometry.Insets;
|
|
import javafx.scene.Node;
|
|
import javafx.scene.control.Button;
|
|
import javafx.scene.control.ButtonType;
|
|
import javafx.scene.control.Control;
|
|
import javafx.scene.control.Dialog;
|
|
import javafx.scene.control.Label;
|
|
import javafx.scene.control.TableCell;
|
|
import javafx.scene.control.TableColumn;
|
|
import javafx.scene.control.TableView;
|
|
import javafx.scene.control.TextField;
|
|
import javafx.scene.layout.GridPane;
|
|
import javafx.scene.layout.HBox;
|
|
import javafx.scene.layout.VBox;
|
|
import javafx.util.Callback;
|
|
|
|
/**
|
|
*
|
|
* @author blurry
|
|
*/
|
|
public class ModalEditor {
|
|
Dialog dialog;
|
|
|
|
public static interface EditControl<V> {
|
|
|
|
public Node getControl();
|
|
|
|
public V getValue();
|
|
|
|
public void setValue(V value);
|
|
}
|
|
|
|
public static class TextControl implements EditControl<String> {
|
|
TextField control = new TextField();
|
|
|
|
@Override
|
|
public Control getControl() {
|
|
return control;
|
|
}
|
|
|
|
@Override
|
|
public String getValue() {
|
|
return control.getText();
|
|
}
|
|
|
|
@Override
|
|
public void setValue(String value) {
|
|
control.setText(value);
|
|
}
|
|
}
|
|
|
|
static class TableControl<T, S> implements EditControl<List<T>> {
|
|
|
|
Map<String, Callback<TableColumn<T, S>, TableCell<T, S>>> cols;
|
|
Class<T> rowType;
|
|
ObservableList<T> rows;
|
|
TableView<T> table;
|
|
VBox control;
|
|
|
|
public TableControl(Map<String, Callback<TableColumn<T, S>, TableCell<T, S>>> cols, Class<T> rowType) {
|
|
this.cols = cols;
|
|
this.rowType = rowType;
|
|
rows = FXCollections.observableArrayList();
|
|
|
|
table = new TableView(rows);
|
|
cols.forEach((String colName, Callback<TableColumn<T, S>, TableCell<T, S>> factory) -> {
|
|
TableColumn<T, S> col = new TableColumn<>(uppercaseFirst(colName));
|
|
col.setCellValueFactory((TableColumn.CellDataFeatures<T, S> param) -> {
|
|
try {
|
|
return (ObservableValue<S>) JavaBeanStringPropertyBuilder.create().bean(param.getValue()).name(colName).build();
|
|
} catch (NoSuchMethodException ex) {
|
|
Logger.getLogger(ModalEditor.class.getName()).log(Level.SEVERE, null, ex);
|
|
throw new RuntimeException(ex);
|
|
}
|
|
});
|
|
col.setCellFactory(factory);
|
|
col.setPrefWidth(150);
|
|
table.getColumns().add(col);
|
|
});
|
|
table.setPlaceholder(new Label("Click + to add one or more attributes"));
|
|
table.setEditable(true);
|
|
table.setPrefWidth(cols.size() * 150 + 5);
|
|
|
|
Button addButton = new Button("+");
|
|
addButton.setOnAction((event) -> {
|
|
try {
|
|
rows.add(rowType.newInstance());
|
|
Platform.runLater(() -> {
|
|
try {
|
|
Thread.sleep(100);
|
|
} catch (InterruptedException ex) {
|
|
Logger.getLogger(ModalEditor.class.getName()).log(Level.SEVERE, null, ex);
|
|
}
|
|
table.edit(rows.size() - 1, table.getColumns().get(0));
|
|
});
|
|
} catch (InstantiationException | IllegalAccessException ex) {
|
|
Logger.getLogger(ModalEditor.class.getName()).log(Level.SEVERE, null, ex);
|
|
}
|
|
});
|
|
Button removeButton = new Button("-");
|
|
removeButton.setOnAction((event) -> {
|
|
rows.removeAll(table.getSelectionModel().getSelectedItems());
|
|
});
|
|
addButton.setPrefWidth(25);
|
|
removeButton.setPrefWidth(25);
|
|
control = new VBox(
|
|
table,
|
|
new HBox(
|
|
10,
|
|
addButton,
|
|
removeButton
|
|
)
|
|
);
|
|
}
|
|
|
|
@Override
|
|
public Node getControl() {
|
|
return control;
|
|
}
|
|
|
|
@Override
|
|
public List<T> getValue() {
|
|
return rows;
|
|
}
|
|
|
|
@Override
|
|
public void setValue(List<T> value) {
|
|
rows.setAll(value);
|
|
}
|
|
}
|
|
|
|
public PropertyDescriptor getPropertyDescriptor(PropertyDescriptor[] descriptors, String propertyName) {
|
|
for (PropertyDescriptor descriptor : descriptors) {
|
|
if (descriptor.getName().equalsIgnoreCase(propertyName)) {
|
|
return descriptor;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public <T> Optional<T> editObject(T sourceObject, Map<String, EditControl> obj, Class<T> clazz, String title, String header) throws IntrospectionException {
|
|
BeanInfo info = Introspector.getBeanInfo(clazz);
|
|
|
|
dialog = new Dialog();
|
|
dialog.setTitle(title);
|
|
dialog.setHeaderText(header);
|
|
|
|
// dialog.setGraphic(...);
|
|
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
|
|
|
|
GridPane grid = new GridPane();
|
|
grid.setHgap(10);
|
|
grid.setVgap(10);
|
|
grid.setPadding(new Insets(20, 50, 10, 10));
|
|
|
|
final AtomicInteger row = new AtomicInteger(0);
|
|
obj.forEach((String property, EditControl control) -> {
|
|
PropertyDescriptor descriptor = getPropertyDescriptor(info.getPropertyDescriptors(), property);
|
|
if (row.get() == 0) {
|
|
Platform.runLater(() -> control.getControl().requestFocus());
|
|
}
|
|
grid.add(new Label(uppercaseFirst(descriptor.getDisplayName())), 0, row.get());
|
|
grid.add(control.getControl(), 1, row.getAndAdd(1));
|
|
try {
|
|
control.setValue(descriptor.getReadMethod().invoke(sourceObject));
|
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
|
Logger.getLogger(ModalEditor.class.getName()).log(Level.SEVERE, null, ex);
|
|
}
|
|
});
|
|
|
|
dialog.getDialogPane().setContent(grid);
|
|
|
|
dialog.setResultConverter(dialogButton -> {
|
|
if (dialogButton == ButtonType.OK) {
|
|
obj.forEach((String property, EditControl control) -> {
|
|
PropertyDescriptor descriptor = getPropertyDescriptor(info.getPropertyDescriptors(), property);
|
|
try {
|
|
if (descriptor.getWriteMethod() != null) {
|
|
descriptor.getWriteMethod().invoke(sourceObject, control.getValue());
|
|
} else {
|
|
Object val = descriptor.getReadMethod().invoke(sourceObject);
|
|
if (val instanceof List sourceList) {
|
|
sourceList.clear();
|
|
sourceList.addAll((Collection) control.getValue());
|
|
}
|
|
}
|
|
} catch (NullPointerException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
|
Logger.getLogger(ModalEditor.class.getName()).log(Level.SEVERE, "Error updating property " + property, ex);
|
|
}
|
|
});
|
|
return sourceObject;
|
|
}
|
|
return null;
|
|
});
|
|
|
|
return dialog.showAndWait();
|
|
}
|
|
|
|
public boolean isOpen() {
|
|
return dialog != null && dialog.isShowing();
|
|
}
|
|
|
|
public void close() {
|
|
if (isOpen()) {
|
|
dialog.close();
|
|
}
|
|
}
|
|
}
|