I am building an editor application using GWT/GXT with gwt editor framework. For some cases I have to edit a list of dates. To do so I chose to use GridInlineEditing, it works fine, but I also have to make some format validation on my DateField inside the gridInlineEditing.
Basically, the default behavior of the editing is to "record" changes when CompleteEditEvent is fired regardless the result of the validation. I therefore tried to override the onCompleteEditHandler method like this (which obviously is the only way to do it according to GXT forum):
public class NameValueDTMEditorWidget extends GenericEditableListView<DTM, Date> implements Editor<NameValueDTM> {
private final static DTMProperties props = GWT.create(DTMProperties.class);
ListStoreEditor<DTM> values;
#Ignore
private DateField df = new DateField();
public NameValueDTMEditorWidget(String widgetTitle) {
super(widgetTitle, new ListStore<DTM>(props.key()), props.dtm());
DateTimeFormat dtf = DateTimeFormat.getFormat("yyyyMMddHHmmss");
df.setPropertyEditor(new DateTimePropertyEditor(dtf));
addEditorConfig(df); // parent class method basically doing: editing.addEditor(df), editing is GridInlineEditing
// Modifying grid cell render for a Date
Cell c = new DateCell(DateTimeFormat.getFormat("yyyy-MM-dd HH:mm:ss"));
getColumnModel().getColumn(0).setCell(c);
values = new ListStoreEditor<DTM>(getStore());
editing.addCompleteEditHandler(new CompleteEditEvent.CompleteEditHandler<DTM>() {
#Override
public void onCompleteEdit(CompleteEditEvent<DTM> event) {
df.validate(); // I force field validation
if (df.getValue() == null || !df.isValid()) { // if value's not valid
getStore().clear(); // clear the store
DTM e = GWT.create(DTM.class);
getStore().add(e); // add a new value
editing.startEditing(event.getEditCell()); // start editing new value
df.forceInvalid(); // force invalid to get invilid display on the field
}
}
});
}
It does almost what I want, it stays in edition when value is not valid and everything, but when I input a valid value after a wrong one it knows the value is valid but it does not exit edition mode. I have also tried to do the samething keeping the wrong inputed value instead of clearing my store and creating a new value and it behaves exactly the same way, except that with this method I also have a display problem.
Does anybody know how to do this? I also have the same problem with a list of String.
Try using Converter
editing.addEditor(columnConfig, new Converter<String, Date>() {
#Override
public String convertFieldValue(Date date) { /* called when you leave the cell */
GridCell cell = (GridCell) editing.getActiveCell();
ListStore<DTM> store = grid.getStore();
DTM dtm = store.get(cell.getRow());
/*
* here you have the dtm object belongs to related cell and the date as input.
* done with your validation here.
*/
return dtf.format(date);
}
#Override
public Date convertModelValue(String date) { /* called when you focus in the cell */
return dtf.parse(date);
}
}, df);
Try calling Field#clearInvalid before validating the field (I think this would be done before the df.validate(); line in your example).
Related
In my JavaFx project I have a scene with a few views. For the footer of the window I have a class extending from TableView:
public class FooterView extends TableView<Area> implements ViewTemplate {...}
This displays a Table with some data from a .csv-file.
When it comes to assigning the value of the specific presentationmodel property to the cells I do it like that:
TableColumn<Area,Double> powerColumn = new TableColumn<>("Power Overview");
powerColumn.setCellValueFactory(new PropertyValueFactory<>("powerPerArea"));
this.setItems(filePM.getAreas()); //filePm is my filehandler
this.getColumns().addAll(powerColumn, other_columns);
getAreas() looks like this:
public List<Area> readCantonsFromFile() {
try (Stream<String> stream = getStreamOfLines(AREA_FILE_NAME)) {
return stream.skip(1)
.map(l -> new Area(l.split(DELIMITER, 12)))
.collect(Collectors.toList());
}
}
In the constructor of Area i set the properties. One of the properties is the mentioned powerPerArea
private final DoubleProperty powerPerArea = new SimpleDoubleProperty();
...
public void setPowerPerCanton(double powerPerCanton) {
this.powerPerCanton.set(powerPerCanton);
}
My question: Is there a way to change the value in the FooterView before the value is displayed? I tried something like this:
powerColumn.setCellValueFactory(Math.round(new PropertyValueFactory<>("powerPerArea")));
But it seems that I mixed up DoubleProperty, Double and ObservableDouble. Can I even modify the value in here?
The problem: I can not round the value in the setter because I add a double value in a loop through this function:
public void addPowerPerArea(double power){
double sum = getPowerPerCanton() + power;
setPowerPerCanton(sum);
}
And rounding the value in here would give me a wrong result. (rounding not precise enough). I need to do it in the end when all sums are added
You should use the cellValueFactory to determine which data are displayed in the cells: in this case the data returned by your PropertyValueFactory is the actual double value returned from powerPerAreaProperty().get(), which is exactly what you want.
If you want to control how the data are displayed, you should use a cellFactory. So to display the data in a particular format, including limiting the number of decimal places, you can do:
powerColumn.setCellFactory(tc -> new TableCell<Area, Double>() {
#Override
protected void updateItem(Double power, boolean empty) {
super.updateItem(power, empty);
if (empty) {
setText(null);
} else {
setText(String.format("%.0f", power.doubleValue()));
}
}
});
The point here is that you should not modify the data based on how you want to display it; the purpose of having both cellValueFactory and cellFactory is to separate the display of the data from the actual data itself.
An alternative to returning custom cells from a cellFactory would be to use a custom cellValueFactory to return the property formatted as string:
TableColumn<Area, String> powerColumn = new TableColumn<>("Power Overview");
powerColumn.setCellValueFactory(cd -> cd.getValue().powerPerAreaProperty().asString(""%.0f""));
I see two ways to do this. You could:
use the setCellFactory method and in the updateItem method you format it. Should look something like this, haven't tested
powerColumn.setCellFactory(column -> {
return new TableCell<Area, Double>() {
#Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(Math.round(item), empty);
}
};
});
OR: You could make another property of your Area class that is bound to the existing powerOfArea property but returns the value rounded. I am sure that is somehow possible, you could just override some functions of a readOnlyDoubleProperty but there should be a better way. Maybe via DoubleBindings.
In my project I have a table. When the user double clicks on the row, an editing dialog opens. On the opening this dialog I set values to fields, few of which are ChoiceBoxes. The type of ChoiceBox's field is a custom object, not a string.
The code that creates the ChoiceBox follows:
trade_point.setConverter(new TradePointConverter());
trade_point.setItems(FXCollections.observableArrayList(tradePointsService.getTradePoints()));
TradePoint currentTradePoint = tradePointsService.getTradePoint(typeOfPriceByTradePoint.getTradePoint());
trade_point.setValue(currentTradePoint);
Where trade_point is a choice box of TradePoint type. currentTradePoint is not null, when I look at trade_point's value equals currentTradePoint's value but in the dialog I see no value. Items are set correctly. in other case with same choice box filling everything is correct and here is not.
UPD: TradePointConverter class:
class TradePointConverter extends StringConverter<TradePoint>{
public TradePoint fromString(String name){
try {
List<TradePoint> tradePoints = tradePointsService.getTradePoints();
for (TradePoint tradePoint1: tradePoints){
if (tradePoint1.getName().equals(name)){
return tradePoint1;
}
}
}catch (SQLException e){
}
return null;
}
public String toString(TradePoint tradePoint1){
return tradePoint1.getName();
}
}
I am not sure if this converter is correct as far as converting from string by name is totally incorrect. I do not understand how to make it correctly visible to the user (to see the name of an object) and how to store its id to get the definite object at the same time. I can convert to string by getting an id but in this case user won't understand which object to choose.
trade_point is a ChoiceBox:
private ChoiceBox<TradePoint> trade_point = new ChoiceBox<TradePoint>();
I am creating a DateField and adding listener to it.If certain conditions fails I need to reset the value of DateField with previous value which is there in that field.Below is my code
final DateField dateField1 = new DateField();
dateField1.getPropertyEditor().setFormat(DateTimeFormat.getFormat("dd/MMM/yyyy"));
dateField1.getDatePicker().addListener(Events.Select, new Listener<DatePickerEvent>() {
#Override
public void handleEvent(DatePickerEvent dpe) {
// Window.alert("Getting Roster Date here-->"+grid.getColumnModel().);
Window.alert("Getting RosterDate-->"+ caseStoreModule.getModifiedRecords().get(0).get("rosterDate"));
if(caseStoreModule.getModifiedRecords().get(0).get("rosterDate")!=null){
DateTimeFormat format = DateTimeFormat.getFormat("dd/MMM/yyyy");
rosterdate=format.parse(caseStoreModule.getModifiedRecords().get(0).get("rosterDate").toString());
nextdate.setTime(rosterdate.getTime()+(1000*60*60*24));
prevdate.setTime(rosterdate.getTime()-(1000*60*60*24));
}
checkInDate=(Date)(dateField1.getValue());
if(checkInDate.getTime()<rosterdate.getTime() || checkInDate.getTime()>nextdate.getTime()){
MsgBox.info("Enter valid Check In Date");
dateField1.setValue();//here i need to reset the value to the previous value.
return ;
}
}
});
If the if condition is true,then I need to put the previous value which is there in the Field instead of reseting it.Please suggest how to do this.
One solution is having a String variable that holds the field value before doing any manipulation. So if you need to reset the field just use it. Something like this
String tmpStringValue = dateField1.getValue();
...
if(something went wrong){
dateField1.setValue(tmpStringValue );
}
You can store values as an attribute of Element.
At first time this attribute oldValue will be blank.
Set this attribute every time if validation is successful.
For more information read inline comments.
Here is the sample code:
dateField1.getCell().getDatePicker().addValueChangeHandler(new ValueChangeHandler<Date>() {
#Override
public void onValueChange(ValueChangeEvent<Date> event) {
DateTimeFormat format = DateTimeFormat.getFormat("yyyy-dd-MM");
String oldValue = dateField1.getElement().getAttribute("oldValue");
if (oldValue == null || oldValue.length() == 0) {
// initially date field is empty
dateField1.getElement().setAttribute("oldValue",
format.format(event.getValue()));
} else {
Date oldDate = format.parse(oldValue);
Date newDate = event.getValue();
// you validation logic
if (oldDate.getTime() < newDate.getTime()) {
// revert back to last value if validation is failed
dateField1.getCell().getDatePicker().setValue(oldDate);
} else {
// set the old value with new one
dateField1.getElement().setAttribute("oldValue",
format.format(event.getValue()));
}
}
}
});
Call below line if value is initially populated by its default value just after setting the value.
dateField1.getElement().setAttribute("oldValue",
format.format(event.getValue()));
In GXT, every Field has a setOriginalValue(D originalValue) method which could help you in this matter too. You can always return to the original value by reset()ing the Field. Please keep in mind that reset() will clear the validation messages too!
How to use it in situations like this?
Set the original value when the entered value is validated fine
Call Field.reset(), if anything went wrong to reload the last working value in your field.
I have a JTable, and I want validate data in the first column. When the user type the entry in any cell in the first column and click in another cell (focus lost), I want show message that the entry is false, and focus again in the cell until the entry is valid.
First I thought that the cell is like the JTextFiled, so I have tried the method addFocusListener(...) but it doesn't work!
table.getValueAt(0, 0).addFocusListener(
new FocusListener() {
public void focusGained(FocusEvent e) {
}
public void focusLost(FocusEvent e) {
for (int n = 0; n <= table.getValueAt(0, 0).toString().length(); n++) {
if (Character.isDigit(table.getValueAt(0, 0).toString().charAt(n)) == false) {
JOptionPane.showMessageDialog(null,
"Error: code is a number !", "Error Message",
JOptionPane.ERROR_MESSAGE);
break;
} else
break;
}
}
}
}
});
creation of JTable:
String [][]data={ {"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"},
{"-","-","0","-"} };
String[] header = {"Code Projet", "Description", "Duree", "Taches anterieurs"};
table = new JTable(data, header);
The TableCellEditor that is added to a JTable will do any validation on the values that are entered into a JTable.
From the Java Table Tutorial:
The automatic checking of user-entered strings occurs when the default
editor attempts to create a new instance of the class associated with
the cell's column. The default editor creates this instance using a
constructor that takes a String as an argument. For example, in a
column whose cells have type Integer, when the user types in "123" the
default editor creates the corresponding Integer using code equivalent
to new Integer("123"). If the constructor throws an exception, the
cell's outline turns red and refuses to let focus move out of the
cell. If you implement a class used as a column data type, you can use
the default editor if your class supplies a constructor that takes a
single argument of type String.
So, if you want to just make sure the user enters in an Integer (which is what it looks like from your code), you can set the column type to a type of Integer .
table.setDefaultEditor(Integer.class,
new IntegerEditor(0, 100));
Now the DefaultCellEditor will do the checking for you, and throw an exception if the type is not correct. You will just have to make sure you catch the exception and deal with it properly.
I think tutorial I linked above on creating tables would be very helpful for you, especially the part I linked to which talks about validating user text.
I'm using the following
org.eclipse.jface.viewers.CheckboxCellEditor.CheckboxCellEditor(Composite parent)
I'm creating a table viewer with cellEditors and doing the following
CellEditor[] editors = new CellEditor[columnNames.length];
editors[7] = new CheckboxCellEditor(table);
I have a CellModifier that has the following
public Object getValue(Object element, String property) {
Object result = null;
...
result = Boolean.valueOf(task.isDfRequested());
return result;
}
public void modify(Object element, String property, Object value) {
item.isSelected(((Boolean)value).booleanValue());
}
Finally I have a LabelProvider that has the following
public String getColumnText(Object element, int columnIndex) {
String result = "";
try {
result = Boolean.toString(item.isSelected());
} catch (Exception ex) { }
break;
However, in my UI instead of having a check box I have the word true or false && clicking it results in switching state to false or true. Any ideas on why I don't have a checkbox??
I've searched in the source code of CheckboxCellEditor class and in the constructor the control associated to the CellEditor is created in the createControl(Composite parent) method. This method is abstract in CellEditor class and it's implemented like this in CheckboxCellEditor:
protected Control createControl(Composite parent) {
return null;
}
So a control is not created, that's why you don't see the checkbox. In the documentation of the Class you can read:
Note that this implementation simply
fakes it and does does not create any
new controls. The mere activation of
this editor means that the value of
the check box is being toggled by the
end users; the listener method
applyEditorValue is immediately called
to signal the change.
I solved this using a ComboBoxCellEditor with yes and no items.
Regards.
Well, I have no idea how SWT works or what component you are even talking about.
But I do know that when using Swing you can have custom editors for a column in a JTable. If you don't tell the table the class of data for the column then the toString() method of the data is invoked. But if you tell the table that Boolean data is displayed in the column then the table will use the check box editor.
Sounds like a similiar symptom, but I don't know your particular solution.
What I've decided to do is to just implement a dirty hack others have been using.
Create two images of check boxes, one checked the other not checked. Switch the state between the two based on the boolean.
It's not perfect, but for now it gets the job done