Revision d3be16a7
Added by Andreas Kohlbecker almost 7 years ago
src/main/java/eu/etaxonomy/vaadin/component/ToManyRelatedEntitiesListSelect.java | ||
---|---|---|
9 | 9 |
package eu.etaxonomy.vaadin.component; |
10 | 10 |
|
11 | 11 |
import java.util.ArrayList; |
12 |
import java.util.HashSet; |
|
12 |
import java.util.Collections; |
|
13 |
import java.util.LinkedList; |
|
13 | 14 |
import java.util.List; |
14 |
import java.util.Set; |
|
15 | 15 |
|
16 | 16 |
import org.apache.log4j.Logger; |
17 | 17 |
|
... | ... | |
30 | 30 |
import eu.etaxonomy.vaadin.mvp.AbstractCdmEditorPresenter; |
31 | 31 |
|
32 | 32 |
/** |
33 |
* Manages the a collection of items internally as LinkedList<V>. If the Collection to operate on is a Set a Converter must be |
|
34 |
* set. THe internally used fields are used in un-buffered mode. |
|
35 |
* |
|
33 | 36 |
* @author a.kohlbecker |
34 | 37 |
* @since May 11, 2017 |
35 | 38 |
* |
... | ... | |
50 | 53 |
|
51 | 54 |
private boolean withEditButton = false; |
52 | 55 |
|
56 |
private static final int GRID_X_FIELD = 0; |
|
57 |
|
|
58 |
protected boolean addEmptyRowOnInitContent = true; |
|
59 |
|
|
53 | 60 |
//NOTE: Managing the item |
54 | 61 |
// IDs makes BeanContainer more complex to use, but it is necessary in some cases where the |
55 |
// equals() or hashCode() methods have been reimplemented in the bean. |
|
56 |
// TODO CdmBase has a reimplemented equals method, do we need to use the BeanContainer instead? |
|
62 |
// equals() or hashCode() methods have been re-implemented in the bean.
|
|
63 |
// TODO CdmBase has a re-implemented equals method, do we need to use the BeanContainer instead?
|
|
57 | 64 |
private BeanItemContainer<V> beans; |
58 | 65 |
|
59 | 66 |
//private LinkedList<V> itemList = new LinkedList<>(); |
60 | 67 |
|
61 | 68 |
private int GRID_COLS = 2; |
62 | 69 |
|
63 |
private GridLayout grid = new GridLayout(GRID_COLS,1); |
|
64 |
|
|
65 |
private Set<F> nestedFields = new HashSet<>() ; |
|
70 |
private GridLayout grid = new GridLayout(GRID_COLS, 1); |
|
66 | 71 |
|
67 | 72 |
public ToManyRelatedEntitiesListSelect(Class<V> itemType, Class<F> fieldType, String caption){ |
68 | 73 |
this.fieldType = fieldType; |
69 | 74 |
this.itemType = itemType; |
70 | 75 |
setCaption(caption); |
71 | 76 |
beans = new BeanItemContainer<V>(itemType); |
72 |
} |
|
73 | 77 |
|
74 |
private Component buttonGroup(F field){ |
|
75 |
|
|
76 |
CssLayout buttonGroup = new CssLayout(); |
|
77 |
Button add = new Button(FontAwesome.PLUS); |
|
78 |
ClickListener addclickListerner = newRemoveButtonClicklistener(field); |
|
79 |
if(addclickListerner != null){ |
|
80 |
add.addClickListener(addclickListerner); |
|
81 |
} |
|
78 |
} |
|
82 | 79 |
|
83 |
if(withEditButton){ |
|
84 |
Button edit = new Button(FontAwesome.EDIT); |
|
85 |
ClickListener editClickListerner = newEditButtonClicklistener(field); |
|
86 |
if(editClickListerner != null){ |
|
87 |
edit.addClickListener(editClickListerner); |
|
80 |
/** |
|
81 |
* @param field |
|
82 |
* @return |
|
83 |
*/ |
|
84 |
protected Integer findRow(F field) { |
|
85 |
Integer row = null; |
|
86 |
for(int r = 0; r < grid.getRows(); r++){ |
|
87 |
if(grid.getComponent(GRID_X_FIELD, r).equals(field)){ |
|
88 |
row = r; |
|
89 |
break; |
|
88 | 90 |
} |
89 |
buttonGroup.addComponent(edit); |
|
90 |
addStyledComponents(edit); |
|
91 | 91 |
} |
92 |
return row; |
|
93 |
} |
|
92 | 94 |
|
93 |
Button remove = new Button(FontAwesome.MINUS);
|
|
94 |
ClickListener removeclickListerner = newRemoveButtonClicklistener(field);
|
|
95 |
if(removeclickListerner != null){
|
|
96 |
remove.addClickListener(removeclickListerner);
|
|
97 |
}
|
|
95 |
/**
|
|
96 |
* @param field
|
|
97 |
* @return
|
|
98 |
*/
|
|
99 |
private void addRowAfter(F field) {
|
|
98 | 100 |
|
99 |
buttonGroup.addComponent(add); |
|
100 |
buttonGroup.addComponent(remove); |
|
101 |
addStyledComponents(add, remove); |
|
102 |
if(isOrderedCollection){ |
|
103 |
Button moveUp = new Button(FontAwesome.ARROW_UP); |
|
104 |
Button moveDown = new Button(FontAwesome.ARROW_DOWN); |
|
105 |
buttonGroup.addComponents(moveUp, moveDown); |
|
106 |
addStyledComponents(moveUp, moveDown); |
|
101 |
Integer row = findRow(field); |
|
102 |
|
|
103 |
grid.insertRow(row + 1); |
|
104 |
|
|
105 |
addNewRow(row + 1, null); |
|
106 |
updateValue(); |
|
107 |
|
|
108 |
} |
|
109 |
|
|
110 |
/** |
|
111 |
* @param field |
|
112 |
* @return |
|
113 |
*/ |
|
114 |
private void removeRow(F field) { |
|
115 |
|
|
116 |
Integer row = findRow(field); |
|
117 |
grid.removeRow(row); |
|
118 |
updateValue(); |
|
119 |
updateButtonStates(); |
|
120 |
|
|
121 |
} |
|
122 |
|
|
123 |
/** |
|
124 |
* @param field |
|
125 |
* @return |
|
126 |
*/ |
|
127 |
private void moveRowDown(F field) { |
|
128 |
|
|
129 |
Integer row = findRow(field); |
|
130 |
swapRows(row); |
|
131 |
} |
|
132 |
|
|
133 |
/** |
|
134 |
* @param field |
|
135 |
* @return |
|
136 |
*/ |
|
137 |
private void moveRowUp(F field) { |
|
138 |
|
|
139 |
Integer row = findRow(field); |
|
140 |
swapRows(row - 1); |
|
141 |
} |
|
142 |
|
|
143 |
/** |
|
144 |
* @param i |
|
145 |
*/ |
|
146 |
private void swapRows(int i) { |
|
147 |
if(i >= 0 && i + 1 < grid.getRows()){ |
|
148 |
grid.replaceComponent(grid.getComponent(GRID_X_FIELD, i), grid.getComponent(GRID_X_FIELD, i + 1)); |
|
149 |
grid.replaceComponent(grid.getComponent(GRID_X_FIELD + 1 , i), grid.getComponent(GRID_X_FIELD + 1, i + 1)); |
|
150 |
updateButtonStates(); |
|
151 |
updateValue(); |
|
152 |
} else { |
|
153 |
throw new RuntimeException("Cannot swap rows out of the grid bounds"); |
|
107 | 154 |
} |
108 |
buttonGroup.setStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP);
|
|
155 |
}
|
|
109 | 156 |
|
110 |
return buttonGroup; |
|
157 |
/** |
|
158 |
* |
|
159 |
*/ |
|
160 |
protected void updateValue() { |
|
161 |
try { |
|
162 |
setValue(getValueFromNestedFields()); |
|
163 |
} catch (ReadOnlyException e){ |
|
164 |
logger.debug("datasource is readonly, only internal value was updated"); |
|
165 |
} |
|
111 | 166 |
} |
112 | 167 |
|
113 | 168 |
/** |
... | ... | |
138 | 193 |
@Override |
139 | 194 |
protected Component initContent() { |
140 | 195 |
grid.setColumnExpandRatio(0, 1.0f); |
196 |
// set internal value to null to add an empty row |
|
197 |
|
|
198 |
if(addEmptyRowOnInitContent){ |
|
199 |
// add an empty row |
|
200 |
setInternalValue(null); |
|
201 |
} |
|
141 | 202 |
return grid; |
142 | 203 |
} |
143 | 204 |
|
... | ... | |
154 | 215 |
*/ |
155 | 216 |
@Override |
156 | 217 |
protected void setInternalValue(List<V> newValue) { |
157 |
super.setInternalValue(newValue); |
|
158 | 218 |
|
159 |
// newValue is already converted, need to use the original value from the data source |
|
160 |
isOrderedCollection = List.class.isAssignableFrom(getPropertyDataSource().getValue().getClass()); |
|
219 |
grid.removeAllComponents(); |
|
220 |
grid.setRows(1); |
|
221 |
|
|
222 |
if(newValue != null){ |
|
223 |
// FIMXE is it really needed to backup as linked list? |
|
224 |
LinkedList<V> linkedList; |
|
225 |
if(newValue instanceof LinkedList){ |
|
226 |
linkedList = (LinkedList<V>) newValue; |
|
227 |
} else { |
|
228 |
linkedList = new LinkedList<>(newValue); |
|
229 |
} |
|
230 |
super.setInternalValue(linkedList); |
|
231 |
|
|
232 |
// newValue is already converted, need to use the original value from the data source |
|
233 |
isOrderedCollection = List.class.isAssignableFrom(getPropertyDataSource().getValue().getClass()); |
|
161 | 234 |
|
162 |
beans.addAll(newValue); |
|
235 |
//FIXME is beans really used? |
|
236 |
beans.addAll(linkedList); |
|
163 | 237 |
|
164 |
grid.setRows(newValue.size()); |
|
165 |
int row = 0; |
|
166 |
for(V val : newValue){ |
|
167 |
row = addNewRow(row, val); |
|
238 |
int row = 0; |
|
239 |
if(newValue.size() > 0){ |
|
240 |
for(V val : linkedList){ |
|
241 |
row = addNewRow(row, val); |
|
242 |
} |
|
243 |
} |
|
244 |
} |
|
245 |
|
|
246 |
if(newValue == null || newValue.isEmpty()) { |
|
247 |
// add an empty row |
|
248 |
addNewRow(0, null); |
|
168 | 249 |
} |
169 | 250 |
} |
170 | 251 |
|
... | ... | |
176 | 257 |
protected int addNewRow(int row, V val) { |
177 | 258 |
try { |
178 | 259 |
F field = newFieldInstance(val); |
179 |
nestedFields .add(field); |
|
180 | 260 |
addStyledComponent(field); |
181 |
grid.addComponent(field, 0, row); |
|
261 |
|
|
262 |
// important! all fields must be un-buffered |
|
263 |
field.setBuffered(false); |
|
264 |
|
|
265 |
if(getNestedFields().size() == grid.getRows()){ |
|
266 |
grid.setRows(grid.getRows() + 1); |
|
267 |
} |
|
268 |
grid.addComponent(field, GRID_X_FIELD, row); |
|
182 | 269 |
grid.addComponent(buttonGroup(field), 1, row); |
270 |
updateButtonStates(); |
|
183 | 271 |
nestFieldGroup(field); |
184 | 272 |
row++; |
185 | 273 |
} catch (InstantiationException e) { |
... | ... | |
192 | 280 |
return row; |
193 | 281 |
} |
194 | 282 |
|
283 |
private Component buttonGroup(F field){ |
|
284 |
|
|
285 |
CssLayout buttonGroup = new CssLayout(); |
|
286 |
Button add = new Button(FontAwesome.PLUS); |
|
287 |
add.addClickListener(e -> addRowAfter(field)); |
|
288 |
|
|
289 |
if(withEditButton){ |
|
290 |
Button edit = new Button(FontAwesome.EDIT); |
|
291 |
ClickListener editClickListerner = newEditButtonClicklistener(field); |
|
292 |
if(editClickListerner != null){ |
|
293 |
edit.addClickListener(editClickListerner); |
|
294 |
} |
|
295 |
buttonGroup.addComponent(edit); |
|
296 |
addStyledComponents(edit); |
|
297 |
} |
|
298 |
|
|
299 |
Button remove = new Button(FontAwesome.MINUS); |
|
300 |
remove.addClickListener(e -> removeRow(field)); |
|
301 |
|
|
302 |
|
|
303 |
buttonGroup.addComponent(add); |
|
304 |
buttonGroup.addComponent(remove); |
|
305 |
addStyledComponents(add, remove); |
|
306 |
if(isOrderedCollection){ |
|
307 |
Button moveUp = new Button(FontAwesome.ARROW_UP); |
|
308 |
moveUp.addClickListener(e -> moveRowUp(field)); |
|
309 |
Button moveDown = new Button(FontAwesome.ARROW_DOWN); |
|
310 |
moveDown.addClickListener(e -> moveRowDown(field)); |
|
311 |
|
|
312 |
|
|
313 |
buttonGroup.addComponents(moveUp, moveDown); |
|
314 |
addStyledComponents(moveUp, moveDown); |
|
315 |
} |
|
316 |
buttonGroup.setStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP); |
|
317 |
|
|
318 |
return buttonGroup; |
|
319 |
} |
|
320 |
|
|
321 |
private void updateButtonStates(){ |
|
322 |
|
|
323 |
int fieldsCount = getNestedFields().size(); |
|
324 |
for(int row = 0; row < fieldsCount; row++){ |
|
325 |
|
|
326 |
boolean isFirst = row == 0; |
|
327 |
boolean isLast = row == fieldsCount - 1; |
|
328 |
|
|
329 |
CssLayout buttonGroup = (CssLayout) grid.getComponent(GRID_X_FIELD + 1, row); |
|
330 |
|
|
331 |
// add |
|
332 |
buttonGroup.getComponent(0).setEnabled(isLast || isOrderedCollection); |
|
333 |
// remove |
|
334 |
buttonGroup.getComponent(1).setEnabled(!isFirst); |
|
335 |
// up |
|
336 |
if(buttonGroup.getComponentCount() > 2){ |
|
337 |
buttonGroup.getComponent(2).setEnabled(!isFirst); |
|
338 |
// down |
|
339 |
buttonGroup.getComponent(3).setEnabled(!isLast); |
|
340 |
} |
|
341 |
} |
|
342 |
} |
|
343 |
|
|
344 |
|
|
345 |
protected List<F> getNestedFields(){ |
|
346 |
List<F> nestedFields = new ArrayList<>(grid.getRows()); |
|
347 |
for(int r = 0; r < grid.getRows(); r++){ |
|
348 |
F f = (F) grid.getComponent(GRID_X_FIELD, r); |
|
349 |
if(f == null){ |
|
350 |
logger.debug(String.format("NULL field at %d,%d", GRID_X_FIELD, r)); |
|
351 |
} else { |
|
352 |
nestedFields.add(f); |
|
353 |
} |
|
354 |
} |
|
355 |
return Collections.unmodifiableList(nestedFields); |
|
356 |
} |
|
357 |
|
|
195 | 358 |
/** |
196 | 359 |
* |
197 | 360 |
* @param val |
... | ... | |
254 | 417 |
*/ |
255 | 418 |
@Override |
256 | 419 |
public void commit() throws SourceException, InvalidValueException { |
257 |
nestedFields.forEach(f -> f.commit());
|
|
420 |
getNestedFields().forEach(f -> f.commit());
|
|
258 | 421 |
// calling super.commit() is useless if operating on a transient property!! |
259 | 422 |
super.commit(); |
260 | 423 |
} |
... | ... | |
271 | 434 |
*/ |
272 | 435 |
public List<V> getValueFromNestedFields() { |
273 | 436 |
List<V> nestedValues = new ArrayList<>(); |
274 |
nestedFields.forEach(f -> nestedValues.add(f.getValue())); |
|
437 |
getNestedFields().forEach(f -> { |
|
438 |
logger.trace(String.format("getValueFromNestedFields() - %s:%s", |
|
439 |
f != null ? f.getClass().getSimpleName() : "null", |
|
440 |
f != null ? f.getValue() : "null" |
|
441 |
)); |
|
442 |
if(f != null){ |
|
443 |
nestedValues.add(f.getValue()); |
|
444 |
} |
|
445 |
}); |
|
275 | 446 |
return nestedValues; |
276 | 447 |
} |
277 | 448 |
|
Also available in: Unified diff
ref #6169 add, remove, move buttons in ToManyRelatedEntitiesListSelect operational and with correct state