Given two CheckboxCell's checkboxA and checkboxB, how can I programmatically uncheck checkboxB if checkboxA is unchecked? From what I've tried, I'm able to set the value of checkboxB = false when checkboxA = false, but this change is not being reflected in the UI.
My table with the checkboxes -
public void buildTable() {
myDataProvider.addDataDisplay(myTable);
// other columns
Column<Note, Boolean> checkboxA = new Column<Note, Boolean>(new CheckboxCell()){
#Override
public Boolean getValue(Note object) {
return object.isAChecked();
}
};
checkboxA.setFieldUpdater(new FieldUpdater<Note, Boolean>() {
#Override
public void update(int index, Note object, Boolean value) {
performCheckboxAOperation(object, value);
}
});
Column<Note, Boolean> checkboxB = new Column<Note, Boolean>(new CheckboxCell()){
#Override
public Boolean getValue(Note object) {
return object.isBChecked();
}
};
checkboxB.setFieldUpdater(new FieldUpdater<Note, Boolean>() {
#Override
public void update(int index, Note object, Boolean value) {
performCheckboxBOperation(object, value);
}
});
myTable.addColumn(checkboxA, "BOX A");
myTable.addColumn(checkboxB, "BOX B");
}
Method to store checkbox A's value in the DB, and if checkboxA == false, also set checkboxB = false in the DB -
public void performCheckboxAOperation(Note note, Boolean bool) {
this.presenter.performBoxOperation(note, bool, new AsyncCallback<CNote>() {
#Override
public void onFailure(Throwable caught) {
// error logic
}
#Override
public void onSuccess(CompanyNote result) {
cleanupDataProvider.flush();
if (result.isAChecked() == false) {
if (result.isBChecked() == true) {
unsetB(result);
}
}
}
});
}
This is my method to unset checkboxB which is changing the value in the DB but not in the UI -
private void unsetB(Note note) {
this.presenter.performBoxOperation(note, false, new AsyncCallback<Note>() {
#Override
public void onFailure(Throwable caught) {
// error logic
}
#Override
public void onSuccess(Note result) {
myDataProvider.flush();
myTable.redraw();
}
});
}
From what I understand, ListDataProvider needs to be flushed for the changes to be reflected in the UI. This has worked very well for all the other fields in the table, but it is not working with CheckboxCells.
I've already tried to redraw() the table as can be seen above but without any luck.
So what should I be doing to trigger a UI change in another CheckboxCell when one CheckboxCell is clicked?
I think that you forgot to actually change the object's value.
private void unsetB(final Note note) {
this.presenter.performBoxOperation(note, false, new AsyncCallback<Note>() {
// ...
#Override
public void onSuccess(Note result) {
note.setB(false); // set B value or copy `result` to `note`
// (`note` must be final)
myDataProvider.flush();
myTable.redraw();
}
});
}
Also, in performCheckboxAOperation you call unsetB(result); - it should be unsetB(note);
You need to understand that in onSuccess you get a copy of Note. The result object is not in the DataProvider, note is. And you should change note's value.
Related
I have a code which goes like this
List insert;
List update;
List delete
for(SomeObject someObj : someObjects){
if(isNew){
insert.add()
}
else if(isUpdate){
update.add();
}
if(isDelete){
delete.add()
}
}
//call update insert delete functions
The problem is this code is untestable because the update insert delete are all void methods.
My question is , should I consider iterating over the loop three times and then get the lists to test if the logic to filter each type of results is working? The cost is not that much since I am expecting <100 elements in the list.
You can check
(sum of lists sizes after) == (sum of lists sizes before) + someObjects.size();
The key here is to be able to control the dependencies and access the state that this method acts upon.
Here is an example that illustrates that:
interface SomeDao {
void add(SomeObject object);
void update(SomeObject object);
void delete(SomeObject object);
}
class SomeDaoStub implements SomeDao {
#Override public void add(SomeObject object) {}
#Override public void update(SomeObject object) {}
#Override public void delete(SomeObject object) {}
}
class SomeObject {
private final boolean isNew;
private final boolean isUpdated;
private final boolean isDeleted;
SomeObject(boolean isNew, boolean isUpdated, boolean isDeleted) {
this.isNew = isNew;
this.isUpdated = isUpdated;
this.isDeleted = isDeleted;
}
public boolean isNew() {
return isNew;
}
public boolean isUpdated() {
return isUpdated;
}
public boolean isDeleted() {
return isDeleted;
}
}
public void doSomethingComplicatedWithListsInAForLoop(Iterable<SomeObject> someObjects, SomeDao dao) {
for (SomeObject someObject : someObjects) {
if (someObject.isNew()) {
dao.add(someObject);
} else if (someObject.isUpdated()) {
dao.update(someObject);
} else if (someObject.isDeleted()) {
dao.delete(someObject);
}
}
}
#Test
public void itDeletesObjectsMarkedToBeDeleted() {
final List<SomeObject> actualDeletedObjects = new ArrayList<>();
List<SomeObject> expectedDeletedObjects = Arrays.asList(
new SomeObject(false, false, true),
new SomeObject(false, false, true),
new SomeObject(false, false, true)
);
SomeDao theDao = new SomeDaoStub() {
#Override
public void delete(SomeObject object) {
actualDeletedObjects.add(object);
}
};
doSomethingComplicatedWithListsInAForLoop(expectedDeletedObjects, theDao);
assertEquals(expectedDeletedObjects, actualDeletedObjects);
}
The only reason that I can figure out what doSomethingComplicatedWithListsInAForLoop manipulated is because I can control its dependencies, namely, in this example, SomeDao.
It is likely that you are finding difficulty testing your method because it makes calls to state which you cannot inject.
I have the following code for editing a cell in a ListView:
listView.setCellFactory(new Callback<ListView<TextModule>, ListCell<TextModule>>() {
#Override public ListCell<TextModule> call(ListView<TextModule> param) {
TextFieldListCell<TextModule> textCell = new TextFieldListCell<TextModule>() {
#Override public void updateItem(TextModule item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText( item.getSummary());
}
else {
setText(null);
}
}
};
return textCell;
}
});
Now the problem is, that if I enter any cell within the ListView with double click, I can edit the cell, but the property (text which is displayed) is changed to the class definition like com.test.tools.tbm.model.TextModule#179326d. Normally it displays a text like "Hello World" or something else.
If you don't provide a proper string converter for TextFieldListCell it will use the default implementation (from CellUtils):
private static <T> String getItemText(Cell<T> cell, StringConverter<T> converter) {
return cell.getItem().toString();
}
showing in your case com.test.tools.tbm.model.TextModule#179326d, as cell.getItem() returns an instance of TextModule.
So you need to override toString() in your TextModule class.
class TextModule {
private final String summary;
public TextModule(String summary){
this.summary=summary;
}
public String getSummary(){ return summary; }
#Override
public String toString(){
return summary;
}
}
Or alternatively you could provide your own StringConverter:
listView.setCellFactory(TextFieldListCell.forListView(new StringConverter<TextModule>(){
#Override
public String toString(TextModule item) {
return item.getSummary();
}
#Override
public TextModule fromString(String string) {
return new TextModule(string);
}
}));
Using GWT 2.6.1, UiBinder, DataGrid.
Also using SingleSelectionModel to select a single row:
final SingleSelectionModel<User> selectionModel = new SingleSelectionModel<>(keyProvider);
Checkboxes column:
// checkboxes
Column<User, Boolean> checkBoxColumn = new Column<User, Boolean>(
new CheckboxCell(false, false)) {
#Override
public Boolean getValue(User user) {
return user.isChecked();
}
};
checkBoxColumn.setFieldUpdater(new FieldUpdater<User, Boolean>() {
#Override
public void update(int index, User user, Boolean value) {
user.setChecked(value);
}
});
So i store "checked" user state as a boolean field in the User entity class, without
using a SelectionModel at all.
Now I need to implement custom header checkbox to select/deselect all checkboxes in the column.
public class CheckboxHeader extends Header<Boolean> {
public CheckboxHeader(CheckboxCell cell) {
super(cell);
}
#Override
public Boolean getValue() {
return null;
}
}
Have no ideas how to implement properly this header class to add column in the DataGrid:
dataGrid.addColumn(checkBoxColumn, new CheckboxHeader(new CheckboxCell(false, false)));
Another trouble is to enable/disable all those checkboxes by checking other checkbox that
isn't in the DataGrid.
How can i retrieve all checkboxes from the column/selectionmodel/etc and enable/disable them one by one?
Appreciate any suggestions.
Mixing the data model (User entity) and the state of user interface (isSelected) is never a good idea.
This is how you can do it (replace T with your object, or create a column object that you can re-use):
Column<T, Boolean> checkColumn = new Column<T, Boolean>(new CheckboxCell()) {
#Override
public Boolean getValue(T object) {
return getSelectionModel().isSelected(object);
}
};
checkColumn.setFieldUpdater(new FieldUpdater<T, Boolean>() {
#Override
public void update(int index, T object, Boolean value) {
getSelectionModel().setSelected(object, value);
dataProvider.refresh();
}
});
myDataGrid.setSelectionModel(getSelectionModel(), DefaultSelectionEventManager.<T> createCheckboxManager(0));
Header<Boolean> selectAllHeader = new Header<Boolean>(new HeaderCheckbox()) {
#Override
public Boolean getValue() {
for (T item : getVisibleItems()) {
if (!getSelectionModel().isSelected(item)) {
return false;
}
}
return getVisibleItems().size() > 0;
}
};
selectAllHeader.setUpdater(new ValueUpdater<Boolean>() {
#Override
public void update(Boolean value) {
for (T object : getVisibleItems()) {
getSelectionModel().setSelected(object, value);
}
}
});
myDataGrid.addColumn(checkColumn, selectAllHeader);
I've figured out why my arraylist was always returning a size of 0 but I can't fix the problem. I have a custom model with an "addHuman(Human h) method that is intended to add to an array. Only problem is that it doesn't. Now, if I were to use the regular method, say model.add(index, object o) it would actually work and increment the size of my arraylist but doesn't display on my jtable. My question is how can I make this work for my custom addHuman method? Any help much appreciated!
and the following is the main class that uses that method. When I click on the button to addIndividual, it's supposed to add the human to my HumanListModel:
addIndividual.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
Human temp;
try {
temp = new Human();
modelx.addHuman(indexPoint, temp);
///the addHuman method does display on jtable but doesn't increment arraylist, meaning that the size is always 0 which creates many problems/////
//modelx.add(indexPoint, temp); does indeed increment the arraysize but then it doesn't display the values on the jtable////
indexPoint++;
System.out.println(modelx.size());
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
newbiex.revalidate(); ////is the jtable////
}
});
here is my custom HumanListModel:
public class HumanListModel extends DefaultListModel implements TableModel
{
private ArrayList<Human> data;
public HumanListModel()
{
super();
data = new ArrayList<Human>();
}
public void addHuman(int k, Human h)
{
data.add(k, h);
fireIntervalAdded(this, data.size(), data.size());
}
public Human getHuman(int o)
{
return data.get(o);
}
public void removeHuman(Human h)
{
data.remove(h);
}
public int getColumnCount()
{
// the number of columns you want to display
return 1;
}
public int getRowCount()
{
return data.size();
}
public Object getValueAt(int row, int col)
{
return (row < data.size()) ? data.get(row) : null;
}
public String getColumnName(int col)
{
return "Human";
}
public Class getColumnClass(int col)
{
return Human.class;
}
public void addTableModelListener(TableModelListener arg0) {
}
#Override
public boolean isCellEditable(int arg0, int arg1) {
return false;
}
public void removeTableModelListener(TableModelListener arg0) {
}
public void setValueAt(Object arg0, int arg1, int arg2) {
}
}
You have to fire model changes when you change the data underlying the
public void addHuman(Human h)
{
data.add(h);
fireIntervalAdded(this, data.size(), data.size());
}
Similar methods with similar events need calling whenever you change the underlying data to tell the List that it has to update the screen image.
For example, removeHuman() will need a similar call. Consult the javadoc at http://docs.oracle.com/javase/6/docs/api/javax/swing/AbstractListModel.html for the methods that do that. (In this example, fireIntervalRemoved() and the event will need to contain the index of the row removed.)
You are going to also need a getElementAt() method that returns the data element for that row. In your case, return the Human at that row but it will need a toString() method. Alternately, you could format a string from the Human
and return it.
Note - A previous version of this answer was based on my confusion, thinking this was a TableModel and not a ListModel. It's been fixed.
I have a simple OutlineView in the NetBeans editor area that shows two columns. The content of the cells of the second column shall be settable with a custom property editor via the PropertySupport. The custom property editor contains a JList that allows multiple selection of items.
The PropertySupport class looks like
public class CityProperty extends PropertySupport.ReadWrite<String> {
Customer c;
public CityProperty(Customer c, HashMap<String, Boolean> optionalCities) {
super("city", String.class, "City", "Name of City");
setValue("labelData", optionalCities);
this.c = c;
}
#Override
public String getValue() throws IllegalAccessException, InvocationTargetException {
return c.getCity();
}
#Override
public PropertyEditor getPropertyEditor() {
return new CityPropertyEditor(c);
}
#Override
public void setValue(String newValue) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
c.setCity(newValue);
}
}
The PropertyEditor looks like
public class CityPropertyEditor extends PropertyEditorSupport implements ExPropertyEditor {
Customer c;
PropertyEnv env;
public CityPropertyEditorPanel editor = null;
public CityPropertyEditor(Customer c) {
this.editor = new CityPropertyEditorPanel();
this.c = c;
}
#Override
public String getAsText() {
String s = (String) getValue();
if (s == null) {
return "No City Set";
}
return s;
}
#Override
public void setAsText(String s) {
setValue(s);
}
#Override
public void attachEnv(PropertyEnv env) {
this.env = env;
}
#Override
public Component getCustomEditor() {
HashMap<String, Boolean> cities = (HashMap<String, Boolean>) env.getFeatureDescriptor().getValue("labelData");
DefaultListModel model = new DefaultListModel();
/* selection in the gui */
int[] selectedIdxs = new int[cities.size()];
int idx = 0;
for (String str : cities.keySet()) {
model.addElement(str);
if (cities.get(str) == Boolean.FALSE) {
selectedIdxs[idx] = model.indexOf(str);
idx++;
}
}
if (selectedIdxs.length > 0){
editor.jList.setSelectedIndices(selectedIdxs);
}
editor.jList.setModel(model);
return editor;
}
#Override
public boolean supportsCustomEditor() {
return true;
}
#Override
public Object getValue() {
System.out.println("getValue(): " + editor.jList.getSelectedValuesList());
System.out.println("getValue(): " + editor.jtf.getText());
return super.getValue();
}
}
and the editor CityPropertyEditorPanel() itself is a simple JPanel with a JList and a JTextField.
My codes creates a nice custom editor with all the items listed, but it is not returning the new selected items from the list. My question is now, how do I get the selected items from the JList back to the CityProperty class? My try was to use
editor.jList.getSelectedValuesList());
in the getValue() method but the result is always empty. The same for the JTextField, where a new written value is also not transferred back.
What Am I doing wrong here?
I think I found a solution/workaround.
The CityPropertyEditor recognized the content of the "editor" object when I activated the PropertyEnv.STATE_NEEDS_VALIDATION feature. The code then in CityPropertyEditor should have to override the attacheEnv method and include the VetoableChangeListener
#Override
public void attachEnv(PropertyEnv env) {
this.env = env;
env.setState(PropertyEnv.STATE_NEEDS_VALIDATION);
env.addVetoableChangeListener(new VetoableChangeListener() {
#Override
public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
/* User has pushed OK */
for (Entry entry : editor.isoValNew.entrySet()){
isoVal.put((Double) entry.getKey(), (Boolean) entry.getValue());
}
}
});
}
while the Jlist in the CityPropertyEditorPanel() itself has a ListSelectionListener who updates the Map variable isoValNew
isoValueList.addListSelectionListener(new ListSelectionListener() {
#Override
public void valueChanged(ListSelectionEvent e) {
isoValNew.clear();
for (Object obj : isoValueList.getSelectedValues()) {
isoValNew.put((Double) obj, Boolean.TRUE);
}
}
});
I'm sure this is not a perfect solution, but it works fine in my case.
Hope this helps someone.