Google Maps API v3 - Buttons and TextBoxes inside InfoWindow? - gwt

I'm using the new maps v3 API from gwt-google-apis.
Is it possible to capture events from GWT widgets that are inside InfoWindow? Am I missing something?
Tried code above (button.addClickHandler) and it doesn't show the alert:
Marker m = Marker.create();
m.setIcon(MarkerImage.create(icone));
m.setPosition(LatLng.create(posicao.lat(), posicao.lng()));
m.setMap(map);
m.addClickHandler(new ClickHandler() {
#Override
public void handle(MouseEvent event) {
InfoWindow info = InfoWindow.create();
Button button = new Button("Desativar");
button.addClickHandler(new com.google.gwt.event.dom.client.ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Window.alert("click");
}
});
final HTMLPanel html = new HTMLPanel(("<div id='button'></div>"));
html.add(button, "button");
info.setContent(html.getElement());
info.setPosition(posicao);
info.open(map);
}
});
Thanks.

The problem is result of a broken hierarchy between the widgets, the normal way to do it is by attach / detach widget. You do it by setting of the widget's element. This is also matter of Google Maps API.
This can be resolved by using fake panel which will be part of the InfoWindow, so when you make setContent(Widget widget) the fake panel will be updated and the element of the widget will be set to the content (as previous).
Please take a look at this class:
public class MyInfoWindow extends InfoWindow {
static class FakePanel extends ComplexPanel {
public FakePanel(Widget w) {
w.removeFromParent();
getChildren().add(w);
adopt(w);
}
#Override
public boolean isAttached() {
return true;
}
public void detachWidget() {
this.remove(0);
}
}
private IsWidget widgetContent = null;
FakePanel widgetAttacher;
public MyInfoWindow() {
super(InfoWindowImpl.impl.construct());
}
private void detachWidget() {
if (this.widgetAttacher != null) {
this.widgetAttacher.detachWidget();
this.widgetAttacher = null;
}
}
public void close() {
super.close();
detachWidget();
}
public void setContent(String content) {
this.widgetContent = null;
this.detachWidget();
super.setContent(content);
}
/** */
public void setContent(Widget value) {
this.widgetContent = value;
setContent(value.getElement());
if (this.widgetAttacher == null) {
addListener(getJso(), "closeclick", new Runnable() {
#Override
public void run() {
detachWidget();
}
});
this.widgetAttacher = new FakePanel(value);
} else if (this.widgetAttacher.getWidget(0) != value) {
this.widgetAttacher.detachWidget();
this.widgetAttacher = new FakePanel(value);
}
}
private void setContent(Element element) {
InfoWindowImpl.impl.setContent(getJso(), element);
}
public IsWidget getContentWidget() {
return widgetContent;
}
public final native void addListener(JavaScriptObject jso, String whichEvent, Runnable handler)
/*-{
var that = jso;
$wnd.google.maps.event.addListener(jso, whichEvent, function() {
handler.#java.lang.Runnable::run()();
});
}-*/;
}

I had to build a wrapper over InfoWindow to make it work.
public class NXInfoWindow {
static class FakePanel extends ComplexPanel {
public FakePanel(Widget w) {
w.removeFromParent();
getChildren().add(w);
adopt(w);
}
#Override
public boolean isAttached() {
return true;
}
public void detachWidget() {
this.remove(0);
}
}
private InfoWindow info;
private IsWidget widgetContent = null;
private Long id;
FakePanel widgetAttacher;
public static NXInfoWindow create(Long id){
NXInfoWindow myInfo = new NXInfoWindow();
myInfo.info = InfoWindow.create();
myInfo.id = id;
return myInfo;
};
private void detachWidget() {
if (this.widgetAttacher != null) {
this.widgetAttacher.detachWidget();
this.widgetAttacher = null;
}
}
public void close() {
info.close();
detachWidget();
}
public void setPosition(LatLng posicao) {
info.setPosition(posicao);
}
public void open(GoogleMap map) {
info.open(map);
}
public void setContent(Widget value) {
this.widgetContent = value;
info.setContent(value.getElement());
if (this.widgetAttacher == null) {
addListener(info, "closeclick", new Runnable() {
#Override
public void run() {
detachWidget();
}
});
this.widgetAttacher = new FakePanel(value);
} else if (this.widgetAttacher.getWidget(0) != value) {
this.widgetAttacher.detachWidget();
this.widgetAttacher = new FakePanel(value);
}
}
private void setContent(Element element) {
this.setContent(element);
}
public IsWidget getContentWidget() {
return widgetContent;
}
public final native void addListener(JavaScriptObject jso, String whichEvent, Runnable handler)
/*-{
var that = jso;
$wnd.google.maps.event.addListener(jso, whichEvent, function() {
handler.#java.lang.Runnable::run()();
});
}-*/;
}

Related

Flutter Plugin - cannot resolve method 'startActivityForResult'

Recently I am working on a Flutter plugin for my project. My plugin requires startActivityForResult but can't figure out how to use it according to Flutter plugin development. I have given my code below.
public class MyPlugin implements FlutterPlugin, MethodCallHandler {
private static final int REQUEST_CODE = 1;
Result result;
#Override
public void onAttachedToEngine(#NonNull FlutterPluginBinding flutterPluginBinding) {
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "my_plugin");
channel.setMethodCallHandler(this);
}
#Override
public void onDetachedFromEngine(#NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
}
#Override
public void onMethodCall(#NonNull MethodCall call, #NonNull Result result) {
this.result = result;
if (call.method.equals("myMethod")) {
myMethod();
} else {
result.notImplemented();
}
}
private void myMethod() {
// my intent instance will be here
startActivityForResult(intent, REQUEST_CODE); // Cannot resolve method 'startActivityForResult'
}
#Override
public boolean onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
try {
if (requestCode == REQUEST_CODE) {
result.success("done");
} else {
result.error("Something went wrong", null, null);
}
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.toString());
}
return false;
}
}
Try this solution to get the Context. After, call the intent using the Context:
private void myMethod() {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}

Java: How do I get the text of two JLabels when copying JLabel's text with TransferHandler?

How do I get the text of two JLabels when copying JLabel's text with TransferHandler?
Label1111111 How can I keep the text of both JLabels when copied to Label2222222.
For this reason, I will take control of two Jlabel's texts. This shape can only get the text of JLabel, which was first held. Thank you in advance for your help.
public class deneme2 extends JFrame {
private static final int COPY = 0;
private static final int NONE = 0;
private JPanel contentPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
deneme2 frame = new deneme2();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public deneme2() {
JPanel panel= new JPanel();
MouseListener listener = new DragMouseAdapter();
JLabel label1 = new JLabel("Label1111111", JLabel.CENTER);
handlerLabel(label1);
label1.addMouseListener(listener);
panel.add(label1);
JLabel label2 = new JLabel("Label2222222", JLabel.CENTER);
handlerLabel(label2);
label2.addMouseListener(listener);
panel.add(label2);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
contentPane.add(panel);
setContentPane(contentPane);
}
private void handlerLabel (JLabel lbl)
{
lbl.setTransferHandler(new TransferHandler("text") {
#Override
protected void exportDone(JComponent source, Transferable data, int action) {
if (action == COPY){
((JLabel)lbl.getDropTarget().getDropTargetContext().getComponent()).getText();
//((JLabel) source).setText("LabelEmpty");
}
}
});
}
private class DragMouseAdapter extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
JComponent comp = (JComponent)e.getSource();
TransferHandler handler = comp.getTransferHandler();
handler.exportAsDrag(comp, e, TransferHandler.COPY);
}
}
}
Maybe in this way. I added inner class MyLabel and console output in your handlerLabel. Ctrl+v this under your main. It will print to console the original String property of each MyLabels.
public class MyLabel extends JLabel {
String original;
public MyLabel (String text)
{
super(text);
this.original=text;
}
public String getOriginal() {
return original;
}
public void setOriginal(String original) {
this.original = original;
}
}
public deneme2() {
JPanel panel= new JPanel();
MouseListener listener = (MouseListener) new DragMouseAdapter();
MyLabel label1 = new MyLabel("Label1111111");
handlerLabel(label1);
label1.addMouseListener(listener);
panel.add(label1);
MyLabel label2 = new MyLabel("Label2222222");
handlerLabel(label2);
label2.addMouseListener(listener);
panel.add(label2);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
contentPane.add(panel);
setContentPane(contentPane);
}
private void handlerLabel(MyLabel lbl) {
lbl.setTransferHandler(new TransferHandler("text") {
#Override
protected void exportDone(JComponent source, Transferable data, int action) {
if (action == COPY) {
System.out.println(((MyLabel) lbl.getDropTarget().getDropTargetContext().getComponent()).getOriginal());
}
}
});
}
Edit: check this out. Ctrl+V this under your main. It will print to console text of source label and text of target label, also the original property. Class MyLabel implements DropTargetListener , which is then registered by new DropTarget(this, this). Then we define what will happen in overriden drop method.
public class MyLabel extends JLabel implements DropTargetListener {
String original;
public MyLabel(String text) {
super(text);
this.original = text;
new DropTarget(this, this);
this.setTransferHandler(new TransferHandler("text"));
final MouseListener listener = new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent me) {
final MyLabel label = (MyLabel) me.getSource();
final TransferHandler handler = label.getTransferHandler();
handler.exportAsDrag(label, me, TransferHandler.COPY);
}
};
this.addMouseListener(listener);
}
public String getOriginal() {
return original;
}
public void setOriginal(String original) {
this.original = original;
}
#Override
public void dragEnter(DropTargetDragEvent dtde) {
}
#Override
public void dragOver(DropTargetDragEvent dtde) {
}
#Override
public void dropActionChanged(DropTargetDragEvent dtde) {
}
#Override
public void dragExit(DropTargetEvent dte) {
}
#Override
public void drop(DropTargetDropEvent dtde) {
try {
final String sourceString = (String) dtde.getTransferable().getTransferData(new DataFlavor("application/x-java-jvm-local-objectref; class=java.lang.String"));
System.out.println("Source: " + sourceString + " target: " + this.getText());
this.setText(sourceString);
System.out.println("Original target: "+this.getOriginal());
} catch (final UnsupportedFlavorException | IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public deneme2() {
JPanel panel = new JPanel();
MyLabel label1 = new MyLabel("Label1111111a");
panel.add(label1);
MyLabel label2 = new MyLabel("Label2222222b");
panel.add(label2);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
contentPane.add(panel);
setContentPane(contentPane);
}
}
EDIT2: Following code generates this result see result at the end of this answer:
or
It also prints original property to console. Those could be organised differently (MyLabel, MyLabelTransferable, MyLabelDropTargetListener, MyLabelTransferHandler classes, just to give an idea for future refactoring) but it works in this way too. Consider it a quickfix for your use case.
So main class is this:
public class Deneme2 extends JFrame {
private static final int COPY = 0;
private static final int NONE = 0;
public static void main(String[] args) {
Deneme2 mainFrame = new Deneme2();
SwingUtilities.invokeLater(() -> {//let's get that frame on EDT rollin lambda style:)
mainFrame.setVisible(true);
});
}
public Deneme2() {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
MyLabel label1 = new MyLabel("Label1111111a");
panel.add(label1, BorderLayout.WEST);
MyLabel label2 = new MyLabel("Label2222222b");
panel.add(label2, BorderLayout.EAST);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setBounds(100, 100, 450, 300);
panel.setBorder(new EmptyBorder(5, 5, 5, 5));
this.add(panel);
}
}
Then MyLabel.java :
public class MyLabel extends JLabel implements DropTargetListener, Transferable {
String original;
protected static final DataFlavor MYLABEL_DATA_FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + "; class=\"" + MyLabel.class.getCanonicalName() + "\"",
"MyLabel label");
protected static final DataFlavor[] SUPPORTED_FLAVORS = {MYLABEL_DATA_FLAVOR};
public MyLabel(String text) {
super(text);
this.original = text;
new DropTarget(this, this);
this.setTransferHandler(new MyLabelTransferHandler()); //here we use our custom TransferHandler
final MouseListener listener = new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent me) {
final MyLabel label = (MyLabel) me.getSource();
final TransferHandler handler = label.getTransferHandler();
handler.exportAsDrag(label, me, TransferHandler.COPY);
}
};
this.addMouseListener(listener);
}
public String getOriginal() {
return original;
}
public void setOriginal(String original) {
this.original = original;
}
#Override
public void dragEnter(DropTargetDragEvent dtde) {
if (dtde.getTransferable().isDataFlavorSupported(MyLabel.MYLABEL_DATA_FLAVOR)) {
System.out.println("Drop accept - MyLabel");
dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
} else {
dtde.rejectDrag();
}
}
#Override
public void dragOver(DropTargetDragEvent dtde) {
//System.out.println("Drag over");
}
#Override
public void dropActionChanged(DropTargetDragEvent dtde) {
System.out.println("Action changed");
}
#Override
public void dragExit(DropTargetEvent dte) {
System.out.println("Exited");
}
#Override
public void drop(DropTargetDropEvent dtde) {
System.out.println("Drop detected");
if (dtde.getTransferable().isDataFlavorSupported(MyLabel.MYLABEL_DATA_FLAVOR)) {
Transferable t = dtde.getTransferable();
if (t.isDataFlavorSupported(MyLabel.MYLABEL_DATA_FLAVOR)) {
try {
Object transferData = t.getTransferData(MyLabel.MYLABEL_DATA_FLAVOR);
if (transferData instanceof MyLabel) {
MyLabel mySourceLabel = (MyLabel) transferData;
if (!(mySourceLabel.equals(this))) {
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
this.setText(mySourceLabel.getText());
mySourceLabel.setText("Empty");
System.out.println(mySourceLabel.getOriginal() + " " + this.getOriginal());
} else {
dtde.rejectDrop();
System.out.println("Drop rejected - the same MyLabel");
}
} else {
dtde.rejectDrop();
}
} catch (UnsupportedFlavorException | IOException ex) {
dtde.rejectDrop();
}
} else {
dtde.rejectDrop();
}
}
}
#Override
public DataFlavor[] getTransferDataFlavors() {
return SUPPORTED_FLAVORS;
}
#Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(MYLABEL_DATA_FLAVOR) || flavor.equals(DataFlavor.stringFlavor);
}
#Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
if (flavor.equals(MYLABEL_DATA_FLAVOR)) {
return this;
} else if (flavor.equals(DataFlavor.stringFlavor)) {
return this.getText();
} else {
throw new UnsupportedFlavorException(flavor);
}
}
}
And then MyLabelTransferHandler.java :
public class MyLabelTransferHandler extends TransferHandler {
#Override
public boolean canImport(TransferHandler.TransferSupport support) {
return (support.getComponent() instanceof MyLabel) && support.isDataFlavorSupported(MyLabel.MYLABEL_DATA_FLAVOR);
}
#Override
public boolean importData(JComponent src, Transferable transferable) {
return src instanceof MyLabel;
}
#Override
public int getSourceActions(JComponent c) {
return DnDConstants.ACTION_COPY;
}
#Override
protected Transferable createTransferable(JComponent c) {
Transferable t = (MyLabel)c;
return t;
}
#Override
protected void exportDone(JComponent source, Transferable data, int action) {
System.out.println("Export done.");
}
}
After final edit result looks like this:
Little clarification:
protected static final DataFlavor MYLABEL_DATA_FLAVOR = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + "; class=\"" + MyLabel.class.getCanonicalName() + "\"",
"MyLabel label");
in MyLabel. That's important line. This will make getTransferData() return the same instance of MyLabel. If you would do it like:
protected static final DataFlavor MYLABEL_DATA_FLAVOR = new DataFlavor(MyLabel.class, "MyLabel label");
you won't be able to change mySourceLabel text to "Empty" in overridden drop() method, since you would be given a copy of that object.
Also Why shouldn't you extend JFrame and other components? . And you can provide checking for "Empty" text (if getText() returns "Empty", then don't change text in target MyLabel).

JavaFX TableView with Custom Cell Datepicker "OnEditCommit" is not invoked

Hi everyone !!
I'm working with JavaFX and the JDK 8.
I got a TableView filled by a database with the use of a JavaFX POJO Class "Inputs". I've implemented TexField cells, ComboBoxes cells and Checkbox cells succesfully.
But I am not able to make it working with DatePicker Control: the event on the "setOnEditComit" is never invoked...
Hre is my code:
Declarations for TableColumn:
#FXML
private TableColumn<Inputs,LocalDate> colDate = new TableColumn<Inputs,LocalDate>();
[...]
colDate.setCellValueFactory(
new PropertyValueFactory<Inputs,LocalDate>("localdate"));
Callback<TableColumn<Inputs, LocalDate>, TableCell<Inputs, LocalDate>> dateCellFactory =
new Callback<TableColumn<Inputs, LocalDate>, TableCell<Inputs, LocalDate>>() {
public TableCell call(TableColumn p) {
return new EditingDatePickerCell();
}
};
[...]
colDate.setOnEditCommit(
new EventHandler<CellEditEvent<Inputs, LocalDate>>() {
#Override
public void handle(CellEditEvent<Inputs, LocalDate> event) {
System.out.println("EVENT!!!!");
((Inputs) event.getTableView().getItems().get(
event.getTablePosition().getRow())
).setDatePicker(event.getNewValue());
}
}
);
With these JavaFX POJO Objects declarations:
public ObjectProperty<LocalDate> localdate;
[...]
this.localdate = new SimpleObjectProperty<LocalDate>(localdate);
[...]
public LocalDate getDatePicker() {
return localdate.getValue();
}
[...]
public void setDatePicker(LocalDate value) {
System.out.println("EVENT!!!!");
localdate.setValue(value);
}
[...]
public ObjectProperty<LocalDate> localdateProperty() {
return localdate;
}
EditingDatePickerCell class:
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import com.desktop.newapp.utils.Inputs;
public class EditingDatePickerCell <Inputs, LocalDate> extends TableCell<Inputs, LocalDate> {
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createDatePicker();
setText(null);
setGraphic(datePicker);
datePicker.requestFocus();
}
}
#Override
public void cancelEdit() {
super.cancelEdit();
datePicker.setValue((java.time.LocalDate) getItem());
setGraphic(null);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
private DatePicker datePicker;
public EditingDatePickerCell() {
if (datePicker == null) {
createDatePicker();
}
setGraphic(datePicker);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
Platform.runLater(new Runnable() {
#Override
public void run() {
datePicker.requestFocus();
}
});
}
#Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (datePicker != null && item != null) {
datePicker.setValue((java.time.LocalDate) getLocalDate());
commitEdit(getLocalDate());
}
setGraphic(datePicker);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
private void createDatePicker() {
datePicker = new DatePicker();
datePicker.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
setGraphic(datePicker);
datePicker.addEventFilter(KeyEvent.KEY_PRESSED,new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(getLocalDate());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
} else if (t.getCode() == KeyCode.TAB) {
commitEdit(getLocalDate());;
}
}
});
setAlignment(Pos.CENTER);
}
private LocalDate getLocalDate() {
return getItem();
///return datePicker.getValue() != null ? datePicker.getValue() : getItem();
}
}
I've been Googling a lot and I didn't find any correct implementation of the Java8 DatePicker Editing capabilities in a JavaFX TableView. Any help would be very appreciate !!
Without Stackoverflow I'll never could done the quarter of the whole job so long life to Stackoverflow !!
Regards.
You need to put the table in editing mode (for the current row) when the user interacts with the picker. That can be achieved with an onShowingHandler like this (example is also with a color picker):
this.colorPicker.setOnShowing(event -> {
final TableView<T> tableView = getTableView();
tableView.getSelectionModel().select(getTableRow().getIndex());
tableView.edit(tableView.getSelectionModel().getSelectedIndex(), column);
});
then watch the valueProperty of the picker like this:
this.colorPicker.valueProperty().addListener((observable, oldValue, newValue) -> {
if(isEditing()) {
commitEdit(newValue);
}
});
For more explanation have a look at my blog post: Custom editor components in JavaFX TableCells.
Full Class:
public class ColorTableCell<T> extends TableCell<T, Color> {
private final ColorPicker colorPicker;
public ColorTableCell(TableColumn<T, Color> column) {
this.colorPicker = new ColorPicker();
this.colorPicker.editableProperty().bind(column.editableProperty());
this.colorPicker.disableProperty().bind(column.editableProperty().not());
this.colorPicker.setOnShowing(event -> {
final TableView<T> tableView = getTableView();
tableView.getSelectionModel().select(getTableRow().getIndex());
tableView.edit(tableView.getSelectionModel().getSelectedIndex(), column);
});
this.colorPicker.valueProperty().addListener((observable, oldValue, newValue) -> {
if(isEditing()) {
commitEdit(newValue);
}
});
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
protected void updateItem(Color item, boolean empty) {
super.updateItem(item, empty);
setText(null);
if(empty) {
setGraphic(null);
} else {
this.colorPicker.setValue(item);
this.setGraphic(this.colorPicker);
}
}
}
I was having the same issues but i ended up solving it with this post. JavaFX8 – Render a DatePicker Cell in a TableView.
The outcome is illustrated in the image below.
Your Model object is a bit strange. The setDatePicker and getDatePicker methods should be called setLocaldate and getLocaldate, to be consistent with the localdateProperty() method and (possibly importantly) the parameter passed to the PropertyValueFactory.
I don't think that would cause the problem you observe, though, and I can't see what else is wrong.
This example works for me.
Have a look and see if you can see what I did that's different...
here is a datapicker cell edit when pressing tab key and go to the next cell
datePicker.setOnKeyPressed /EventHandler doesnot work with tab
datePicker.EventFilter get called twice
to work arround this you need to add a listner to tableview cell and get date text as string and cast it to localdate
addEventFilter to TableCell deduction object is a sample
=> addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler() {...}
public class DatePickerCell extends TableCell<Deduction, LocalDate> {
private DatePicker datePicker;
private TableColumn column;
// private TableCell cell;
public DatePickerCell(TableColumn column) { //
super();
this.column = column;
this.datePicker = new DatePicker(getDate());
}
#Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createDatePicker();
setText(null);
setGraphic(datePicker);
Platform.runLater(new Runnable() {
#Override
public void run() {
datePicker.requestFocus();
}
});
//Platform.runLater(datePicker::requestFocus);
}
}
#Override
public void updateItem(LocalDate item, boolean empty) {
super.updateItem(item, empty);
// SimpleDateFormat smp = new SimpleDateFormat("dd/MM/yyyy");
/* if (null == this.datePicker) {
System.out.println("datePicker is NULL");
}*/ //TO be reviewed
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (datePicker != null) {
datePicker.setValue(getItem());
}
setText(null);
setGraphic(datePicker);
} else {
setText(getItem().toString());
setGraphic(null);
}
}
}
private LocalDate getDate() {
return getItem() == null ? LocalDate.now() : getItem();
}
private void createDatePicker() {
this.datePicker = new DatePicker(getDate());
datePicker.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
this.datePicker.editableProperty().bind(column.editableProperty());
this.datePicker.disableProperty().bind(column.editableProperty().not());
this.datePicker.setOnShowing(event -> {
final TableView tableView = getTableView();
tableView.getSelectionModel().select(getTableRow().getIndex());
tableView.edit(tableView.getSelectionModel().getSelectedIndex(), column);
});
datePicker.setOnAction((e) -> {
commitEdit(datePicker.getValue());
TableColumn nextColumn = getNextColumn(true);
if (nextColumn != null) {
getTableView().edit(getTableRow().getIndex(),
nextColumn);
}
});
datePicker.focusedProperty().addListener(
new ChangeListener<Boolean>() {
#Override
public void changed(
ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) {
if (!arg2) {
commitEdit(datePicker.getValue());
}
}
});
datePicker.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
// System.out.println("setOnKeyPressed datapicker get value ...." + t.getCode());
/* if (t.getCode() == KeyCode.ENTER) {
commitEdit(datePicker.getValue());
System.out.println("Enter datapicker get value " + datePicker.getValue());
} else */if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
} else if (t.getCode() == KeyCode.ENTER){
commitEdit(datePicker.getValue());
// System.out.println("Tab datapicker get value " + datePicker.getValue());
TableColumn nextColumn = getNextColumn(!t.isShiftDown());
// System.out.println("nextColumn datapicker get value " + nextColumn.getId());
if (nextColumn != null) {
getTableView().edit(getTableRow().getIndex(),
nextColumn);
}
}
}
});
addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.TAB) {
commitEdit(LocalDate.parse(datePicker.getEditor().getText(), DateTimeFormatter.ofPattern("dd/MM/yyyy")));
TableColumn nextColumn = getNextColumn(true);
if (nextColumn != null) {
getTableView().edit(getTableRow().getIndex(),
nextColumn);
}
}
}
});
//
// // setAlignment(Pos.CENTER);
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(getItem().toString());
setGraphic(null);
}
public DatePicker getDatePicker() {
return datePicker;
}
public void setDatePicker(DatePicker datePicker) {
this.datePicker = datePicker;
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
private TableColumn<Deduction, ?> getNextColumn(boolean forward) {
List<TableColumn<Deduction, ?>> columns = new ArrayList<>();
for (TableColumn<Deduction, ?> column : getTableView().getColumns()) {
columns.addAll(getLeaves(column));
}
// There is no other column that supports editing.
if (columns.size() < 2) {
return null;
}
int currentIndex = columns.indexOf(getTableColumn());
int nextIndex = currentIndex;
if (forward) {
nextIndex++;
if (nextIndex > columns.size() - 1) {
nextIndex = 0;
}
} else {
nextIndex--;
if (nextIndex < 0) {
nextIndex = columns.size() - 1;
}
}
return columns.get(nextIndex);
}
private List<TableColumn<Deduction, ?>> getLeaves(
TableColumn<Deduction, ?> root) {
List<TableColumn<Deduction, ?>> columns = new ArrayList<>();
if (root.getColumns().isEmpty()) {
// We only want the leaves that are editable.
if (root.isEditable()) {
columns.add(root);
}
return columns;
} else {
for (TableColumn<Deduction, ?> column : root.getColumns()) {
columns.addAll(getLeaves(column));
}
return columns;
}
}
}
in your class with the tableview
Callback cellFactoryDate
= new Callback<TableColumn, TableCell>() {
public TableCell call(TableColumn p) {
return new DatePickerCell(p);//DatePickerTest(p) //DatePickerTest2 //EditingCellDate
}
};
dateColumn.setCellValueFactory(new PropertyValueFactory<>("date"));
// dateColumn.setCellFactory(cellFactoryTest);//cellFactoryDate
dateColumn.setCellFactory(cellFactoryDate);
dateColumn.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<Deduction, LocalDate>>() {
#Override
public void handle(TableColumn.CellEditEvent<Deduction, LocalDate> t) {
((Deduction) t.getTableView().getItems().get(
t.getTablePosition().getRow())).setDate(t.getNewValue());
}
}
);
Here is a solution acceptable for all custom controls; this example use a ColorPicker control:
So finnaly I deal it like that: on the events of the controls I get back the POJO objects in use by the "((Inputs)getTableView().getItems().get(getTableRow().getIndex()" and I update similary like is it done in the OnEditCommit method...
So for me it's look like this (update the color):
((Inputs) getTableView().getItems().get(
getTableRow().getIndex())
).setColor(cp.getValue());
Here is example with ColorPickerCell
:
public class ColorPickerTableCell<Inputs> extends TableCell<Inputs, Color>{
private ColorPicker cp;
public ColorPickerTableCell(){
cp = new ColorPicker();
cp.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
commitEdit(cp.getValue());
updateItem(cp.getValue(), isEmpty());
((Inputs) getTableView().getItems().get(
getTableRow().getIndex())
).setColor(cp.getValue());
}
});
setGraphic(cp);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setEditable(true);
}
#Override
protected void updateItem(Color item, boolean empty) {
super.updateItem(item, empty);
cp.setVisible(!empty);
this.setItem(item);
cp.setValue(item);
}
}
With this simple JavaFX's POJO:
public ObjectProperty<Color> color = new SimpleObjectProperty<Color>();
this.color = new SimpleObjectProperty(color);
public ObjectProperty<Color> colorProperty() {
return color;
}
public void setColor(Color color2) {
color.set(color2);
}
I do not know if it's a good way to achive that but it worked for me... Note that the JavaFX's POJO is only accessible within an "ActionEvent" request (combobox, datepicker, colorpicker, etc..)
Regards,

Jenkins plugin development - persistence

I'm still learning plugin dev. This is my first one.
I would like to persist the configuration of my plugin, but it won't work.
Could you please tell me, what am I doing wrong?
I have tried debuging the process, starting from the addition of the plugin to the job 'til the saving of the job config.
I have found, that inside the load() method of the descriptor, no xml file is found!
The path it is looking for is something like: c:\users\Peter\workspace\r-script.\work\whatEverDir\xy.xml
I don't think that the .\ part is causing the config file not to be found, but since it is a Jenkins class generating this path, I would not bet on it. Although the system might have tried to create it here at the first place.
Thanks in advance!
Scriptrunner.jelly
<f:block>
<f:entry title="RScript" field="command">
<f:textarea style="width:99%" />
</f:entry>
</f:block>
<f:entry field="installedPackages" title="Installed packages">
<f:select style="width:40%" />
</f:entry>
<f:entry field="mirrors" title="Choose a mirror">
<f:select style="width:40%" />
</f:entry>
<f:entry>
<f:repeatableProperty field="availablePackages" minimum="1"/>
</f:entry>
AvailablePackage.jelly
<f:entry field="availablePackages">
<f:select style="width:40%" />
<f:repeatableDeleteButton />
</f:entry>
ScriptRunner.java
public class ScriptRunner extends Builder {
private static final String fileExtension = ".R";
private ArrayList<AvailablePackage> availablePackages;
private String command;
private String chosenMirror;
private List<String> mirrorList = new ArrayList<String>();
#DataBoundConstructor
public ScriptRunner(String command, ArrayList<String> installedPackages, ArrayList<String> mirrors, ArrayList<AvailablePackage> availablePackages) {
this.chosenMirror = mirrors.get(0);
this.availablePackages = availablePackages;
this.command = command;
}
public final String getCommand() {
return command;
}
#Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
BuildListener listener) throws InterruptedException {
return perform(build, launcher, (TaskListener) listener);
}
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
TaskListener listener) throws InterruptedException {
FilePath workSpace = build.getWorkspace();
FilePath rScript = null;
if (workSpace == null) {
try {
throw new NoSuchFileException("Workspace could not be set!");
} catch (NoSuchFileException e) {
e.printStackTrace();
}
}
try {
try {
String fullScript;
if (command.contains("options(repos=structure(")) {
fullScript = PackagesManager.singleton().createFullScript(availablePackages, "", command);
} else {
fullScript = PackagesManager.singleton().createFullScript(availablePackages, chosenMirror, command);
}
rScript = workSpace.createTextTempFile("RScriptTemp",
getFileExtension(), fullScript, false);
} catch (IOException e) {
Util.displayIOException(e, listener);
e.printStackTrace(listener.fatalError(Messages
.CommandInterpreter_UnableToProduceScript()));
return false;
}
boolean successfullyRan = false;
try {
EnvVars envVars = build.getEnvironment(listener);
for (Map.Entry<String, String> e : build.getBuildVariables()
.entrySet()) {
envVars.put(e.getKey(), e.getValue());
}
if (launcher.launch().cmds(buildCommandLine(rScript))
.envs(envVars).stdout(listener).pwd(workSpace).join() == 1) {
successfullyRan = true;
}
} catch (IOException e) {
Util.displayIOException(e, listener);
e.printStackTrace(listener.fatalError(Messages
.CommandInterpreter_CommandFailed()));
}
return successfullyRan;
} finally {
try {
if (rScript != null) {
rScript.delete();
}
} catch (IOException e) {
Util.displayIOException(e, listener);
e.printStackTrace(listener.fatalError(Messages
.CommandInterpreter_UnableToDelete(rScript)));
} catch (Exception e) {
e.printStackTrace(listener.fatalError(Messages
.CommandInterpreter_UnableToDelete(rScript)));
}
}
}
public String[] buildCommandLine(FilePath script) {
return new String[] { "Rscript", script.getRemote() };
}
protected String getFileExtension() {
return fileExtension;
}
public List<String> getMirrorList() {
return mirrorList;
}
public void setMirrorList(List<String> mirrorList) {
this.mirrorList = mirrorList;
}
public String getChosenMirror() {
return chosenMirror;
}
public void setChosenMirror(String chosenMirror) {
this.chosenMirror = chosenMirror;
}
public ArrayList<AvailablePackage> getAvailablePackages() {
return availablePackages;
}
#Override
public ScriptBuildStepDescriptorImplementation getDescriptor() {
return (ScriptBuildStepDescriptorImplementation)super.getDescriptor();
}
#Extension
public static class ScriptBuildStepDescriptorImplementation extends
BuildStepDescriptor<Builder> {
private boolean showInstalled;
private String command;
private String chosenMirror;
private ArrayList<AvailablePackage> availablePackages;
public ScriptBuildStepDescriptorImplementation() {
load();
}
#Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
req.bindJSON(this, formData);
save();
return super.configure(req,formData);
}
#Override
public String getDisplayName() {
return "Advanced R script runner";
}
#Override
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
return true;
}
public ListBoxModel doFillInstalledPackagesItems() {
ListBoxModel mirrors = new ListBoxModel();
Set<String> mirrorsList = PackagesManager.singleton()
.getInstalledPackages();
for (String entry : mirrorsList) {
mirrors.add(entry);
}
return mirrors;
}
public ListBoxModel doFillAvailablePackagesItems() {
ListBoxModel packages = new ListBoxModel();
List<String> packageList = PackagesManager.singleton().getAvailablePackages();
Set<String> alreadyInstalled = PackagesManager.singleton().getInstalledPackages();
for (String entry : packageList) {
if (!alreadyInstalled.contains(entry)) {
packages.add(entry);
}
}
return packages;
}
public ListBoxModel doFillMirrorsItems() {
ListBoxModel mirrors = new ListBoxModel();
String[] mirrorsList = MirrorManager.singleton().getMirrors();
int selected = 34;
for (int i = 0; i < mirrorsList.length; i++) {
String[] splitCurrent = mirrorsList[i].split(" - ");
if (chosenMirror != null && chosenMirror.equals(splitCurrent[1])) {
selected = i;
}
mirrors.add(splitCurrent[1], splitCurrent[0]);
}
mirrors.get(selected).selected = true;
return mirrors;
}
public boolean getShowInstalled() {
return showInstalled;
}
public void setShowInstalled(boolean showInstalled) {
this.showInstalled = showInstalled;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public String getChosenMirror() {
return chosenMirror;
}
public void setChosenMirror(String chosenMirror) {
this.chosenMirror = chosenMirror;
}
}
}
AvailablePackage.java
public class AvailablePackage extends AbstractDescribableImpl<AvailablePackage> {
private String name;
#DataBoundConstructor
public AvailablePackage(String availablePackages) {
this.name = availablePackages;
}
public String getName() {
return name;
}
#Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl)super.getDescriptor();
}
#Extension
public static class DescriptorImpl extends Descriptor<AvailablePackage> {
private String name;
public DescriptorImpl() {
load();
}
#Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
req.bindJSON(this, formData);
save();
return super.configure(req,formData);
}
public ListBoxModel doFillAvailablePackagesItems() {
return PackagesManager.singleton().getAvailablePackagesAsListBoxModel(name);
}
#Override
public String getDisplayName() {
return "";
}
public String getName() {
return name;
}
}
}
Sorry for the code formatting! First timer at stackoverflow code posting.
I think you may need to comment this line out
#Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
req.bindJSON(this, formData);
save();
//return super.configure(req,formData);
return true;
}
as it will then save again but with no fields.
A good place to locate Jenkins plugin examples is in https://github.com/jenkinsci

GWT - Add and remove nodes in celltree

Here I have an complete and very easy example to dynamically add/remove
nodes to an celltree. My example is not working very well. It seems there
is an refresh problem. Only closing/expanding the nodes will show the correct
result. I also did not found any answer in this forum which fits to this problem.
Maybe somebody can try my example and tell me where the problem is.
Any other hint is also very appreciated.
Greetings, Marco
import java.util.ArrayList;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.CellTree;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SingleSelectionModel;
import com.google.gwt.view.client.TreeViewModel;
public class MyCelltreeTest implements EntryPoint {
private AbsolutePanel absolutePanel;
private CellTree cellTree;
private Button btnAdd;
private Button btnRemove;
private MyTreeModel myTreeModel;
private SingleSelectionModel<MyNode> selectionModelCellTree = null;
#Override
public void onModuleLoad() {
RootPanel rootPanel = RootPanel.get();
rootPanel.add(getAbsolutePanel(), 0, 0);
}
private AbsolutePanel getAbsolutePanel() {
if (absolutePanel == null) {
absolutePanel = new AbsolutePanel();
absolutePanel.setSize("612px", "482px");
absolutePanel.add(getCellTree(), 0, 0);
absolutePanel.add(getBtnAdd(), 265, 428);
absolutePanel.add(getBtnRemove(), 336, 428);
}
return absolutePanel;
}
private CellTree getCellTree() {
if (cellTree == null) {
myTreeModel = new MyTreeModel();
cellTree = new CellTree(myTreeModel, null);
cellTree.setSize("285px", "401px");
}
return cellTree;
}
private Button getBtnAdd() {
if (btnAdd == null) {
btnAdd = new Button("Add");
btnAdd.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
MyNode node = selectionModelCellTree.getSelectedObject();
if(node != null)
myTreeModel.addNew(node, "Bla");
}
});
}
return btnAdd;
}
private Button getBtnRemove() {
if (btnRemove == null) {
btnRemove = new Button("Remove");
btnRemove.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
MyNode node = selectionModelCellTree.getSelectedObject();
if(node != null)
myTreeModel.remove(node);
}
});
}
return btnRemove;
}
public class MyNode {
private String name;
private ArrayList<MyNode> childs; //nodes childrens
private MyNode parent; //track internal parent
private MyCell cell; //for refresh - reference to visual component
public MyNode(String name) {
super();
parent = null;
this.name = name;
childs = new ArrayList<MyNode>();
}
public void addSubMenu(MyNode m) {
m.parent = this;
childs.add(m);
}
public void removeMenu(MyNode m) {
m.getParent().childs.remove(m);
}
public boolean hasChildrens() {
return childs.size()>0;
}
public ArrayList<MyNode> getList() {
return childs;
}
public MyNode getParent() {
return parent;
}
public void setCell(MyCell cell) {
this.cell = cell;
}
public void refresh() {
if(parent!=null) {
parent.refresh();
}
if (cell!=null) {
cell.refresh(); //refresh tree
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class MyTreeModel implements TreeViewModel {
private MyNode officialRoot; //default not dynamic
private MyNode studentRoot; //default not dynamic
private MyNode testRoot; //default not dynamic
private MyNode root;
public MyNode getRoot() { // to set CellTree root
return root;
}
public MyTreeModel() {
selectionModelCellTree = new SingleSelectionModel<MyNode>();
root = new MyNode("root");
// Default items
officialRoot = new MyNode("Cat"); //some basic static data
studentRoot = new MyNode("Dog");
testRoot = new MyNode("Fish");
root.addSubMenu(officialRoot);
root.addSubMenu(studentRoot);
root.addSubMenu(testRoot);
}
//example of add add logic
public void addNew(MyNode myparent, String name) {
myparent.addSubMenu(new MyNode(name));
myparent.refresh(); //HERE refresh tree
}
public void remove(MyNode objToRemove) {
objToRemove.removeMenu(objToRemove);
objToRemove.refresh();
}
#Override
public <T> NodeInfo<?> getNodeInfo(T value) {
ListDataProvider<MyNode> dataProvider;
MyNode myValue = null;
if (value == null) { // root is not set
dataProvider = new ListDataProvider<MyNode>(root.getList());
} else {
myValue = (MyNode) value;
dataProvider = new ListDataProvider<MyNode>(myValue.getList());
}
MyCell cell = new MyCell(dataProvider); //HERE Add reference
if (myValue != null)
myValue.setCell(cell);
return new DefaultNodeInfo<MyNode>(dataProvider, cell, selectionModelCellTree, null);
}
#Override
public boolean isLeaf(Object value) {
if (value instanceof MyNode) {
MyNode t = (MyNode) value;
if (!t.hasChildrens())
return true;
return false;
}
return false;
}
}
public class MyCell extends AbstractCell<MyNode> {
ListDataProvider<MyNode> dataProvider; //for refresh
public MyCell(ListDataProvider<MyNode> dataProvider) {
super();
this.dataProvider = dataProvider;
}
public void refresh() {
dataProvider.refresh();
}
#Override
public void render(Context context, MyNode value, SafeHtmlBuilder sb) {
if (value == null) {
return;
}
sb.appendEscaped(value.getName());
}
}
}
Thanks Ümit for your explanation.
I tried the close-reopen version.
I have replaced my refresh method with the methods below.
Adding is working but removing not.
Very strange the whole topic. I'm very suprised that these basic
functions are not really supported by GWT.
Can somebody give me more help to run a real working example.
public void refresh() {
closeReopenTreeNodes(cellTree.getRootTreeNode());
}
private void closeReopenTreeNodes(TreeNode node) {
if(node == null) {
return;
}
for(int i = 0; i < node.getChildCount(); i++) {
if(node.getChildValue(i).equals(this)){
if(node.getParent() != null){
node.getParent().setChildOpen(i, false);
//node.getParent().setChildOpen(i, true);
}
node.setChildOpen(i, false);
node.setChildOpen(i, true);
}
TreeNode child = node.setChildOpen(i, node.isChildOpen(i));
closeReopenTreeNodes(child);
}
}
Here my third try:
This way is recommended by gwt-commiter.
Please see following issue:
http://code.google.com/p/google-web-toolkit/issues/detail?id=7160
Current status:
* Adding is possible
* Removing is possible if not last child!
So, last open point, refresh the tree if last open child!
package com.test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.CellTree;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SingleSelectionModel;
import com.google.gwt.view.client.TreeViewModel;
public class MyCelltreeTest2 implements EntryPoint {
private AbsolutePanel absolutePanel;
private CellTree cellTree;
private Button btnAdd;
private Button btnRemove;
private MyTreeModel myTreeModel;
private SingleSelectionModel<MyNode> selectionModelCellTree = null;
private Map<MyNode, ListDataProvider<MyNode>> mapDataProviders = null;
private ListDataProvider<MyNode> rootDataProvider = null;
public void onModuleLoad() {
RootPanel rootPanel = RootPanel.get();
rootPanel.add(getAbsolutePanel(), 0, 0);
}
private AbsolutePanel getAbsolutePanel() {
if (absolutePanel == null) {
absolutePanel = new AbsolutePanel();
absolutePanel.setSize("612px", "482px");
absolutePanel.add(getCellTree(), 0, 0);
absolutePanel.add(getBtnAdd(), 265, 428);
absolutePanel.add(getBtnRemove(), 336, 428);
}
return absolutePanel;
}
private CellTree getCellTree() {
if (cellTree == null) {
myTreeModel = new MyTreeModel();
cellTree = new CellTree(myTreeModel, null);
cellTree.setSize("285px", "401px");
}
return cellTree;
}
private Button getBtnAdd() {
if (btnAdd == null) {
btnAdd = new Button("Add");
btnAdd.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
MyNode node = selectionModelCellTree.getSelectedObject();
myTreeModel.add(node, "Bla");
}
});
}
return btnAdd;
}
private Button getBtnRemove() {
if (btnRemove == null) {
btnRemove = new Button("Remove");
btnRemove.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
MyNode node = selectionModelCellTree.getSelectedObject();
myTreeModel.remove(node);
}
});
}
return btnRemove;
}
public class MyNode {
private String name;
private ArrayList<MyNode> childs; //nodes childrens
private MyNode parent; //track internal parent
public MyNode(String name) {
super();
parent = null;
this.name = name;
childs = new ArrayList<MyNode>();
}
public boolean hasChildrens() {
return childs.size()>0;
}
public ArrayList<MyNode> getList() {
return childs;
}
public MyNode getParent() {
return parent;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class MyTreeModel implements TreeViewModel {
public MyTreeModel() {
selectionModelCellTree = new SingleSelectionModel<MyNode>();
mapDataProviders = new HashMap<MyCelltreeTest2.MyNode, ListDataProvider<MyNode>>();
}
public void add(MyNode myparent, String name) {
MyNode child = new MyNode(name);
//root-node
if(myparent == null){
rootDataProvider.getList().add(child);
mapDataProviders.put(child, rootDataProvider);
}
else{
ListDataProvider<MyNode> dataprovider = mapDataProviders.get(myparent);
myparent.childs.add(child);
child.parent = myparent;
dataprovider.refresh();
}
}
public void remove(MyNode objToRemove) {
ListDataProvider<MyNode> dataprovider = mapDataProviders.get(objToRemove);
dataprovider.getList().remove(objToRemove);
// mapDataProviders.remove(objToRemove);
dataprovider.refresh();
dataprovider.flush();
if(objToRemove.parent != null){
ListDataProvider<MyNode> dataproviderParent = mapDataProviders.get(objToRemove.parent);
objToRemove.parent.childs.remove(objToRemove);
dataproviderParent.refresh();
dataproviderParent.flush();
}
else{
rootDataProvider.refresh();
rootDataProvider.flush();
}
}
#Override
public <T> NodeInfo<?> getNodeInfo(T value) {
if (value == null) { // root is not set
rootDataProvider = new ListDataProvider<MyNode>(new ArrayList<MyNode>());
MyCell cell = new MyCell();
return new DefaultNodeInfo<MyNode>(rootDataProvider, cell,
selectionModelCellTree, null);
} else {
MyNode myValue = (MyNode) value;
ListDataProvider<MyNode> dataProvider =
new ListDataProvider<MyNode>(myValue.childs);
MyCell cell = new MyCell();
for(MyNode currentNode : myValue.childs){
mapDataProviders.put(currentNode, dataProvider);
}
return new DefaultNodeInfo<MyNode>(dataProvider, cell,
selectionModelCellTree, null);
}
}
#Override
public boolean isLeaf(Object value) {
if (value instanceof MyNode) {
MyNode t = (MyNode) value;
if (!t.hasChildrens())
return true;
return false;
}
return false;
}
}
public class MyCell extends AbstractCell<MyNode> {
public MyCell() {
super();
}
#Override
public void render(Context context, MyNode value, SafeHtmlBuilder sb) {
if (value == null) {
return;
}
sb.appendEscaped(value.getName());
}
}
}
This is somehow a known problem with CellTree.
The reason for the refresh problem is that in the getNodeInfo() function you create a new ListDataProvider instance for each CellTree level.
The CellTree only updates/refreshes itself if you update the items in that ListDataProvider.
I believe that your removeMenu() and addSubMenu() functions add and remove items from the original list stored in your MyNode class but won't update the list in the corresponding ListDataProviders (you can try to check that in debug mode).
The reason why the CellTree is refreshed when you close and re
-open the nodes is because when you re-open the nodes the getNodeInfo() is called again and the whole CellTree structure will be constructed again (including the new menu or without the removed one respectively).
There are two possible solutions:
Keep a reference for each of the ListDataProviders somewhere and call remove/add on that list (although you do that I assume that the items are not really added/removed there).
Programatically close all nodes and re-open it.
Both are somehow a PITA to implement. Unfortunately there is no easy way around it.
I just clear the array of objects maintained in my data provider. I do this in onRangeChanged(final HasData<?> display). I guess I don't use a ListDataProvider here, I use something extending AbstractDataProvider<T> instead.
To add the node add it to your list within the onRangeChanged() method and then call updateRowData(). You can do this for a delete too.
I think I may have licked the problem...
Essentially I've extended and subclasses many parts of CellTree and have obtained an almost perfect working example. Too complex to document here, but suffice to say the solution involved using a node class where I stored the data provider within each node.
https://code.google.com/p/updatable-cell-tree/