1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.workbench.tree;
35
36 import info.magnolia.ui.vaadin.grid.MagnoliaTreeTable;
37 import info.magnolia.ui.workbench.event.ItemEditedEvent;
38
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.Set;
43
44 import com.vaadin.data.Container;
45 import com.vaadin.data.Item;
46 import com.vaadin.data.Property;
47 import com.vaadin.event.ActionManager;
48 import com.vaadin.event.FieldEvents;
49 import com.vaadin.event.FieldEvents.BlurEvent;
50 import com.vaadin.event.ItemClickEvent;
51 import com.vaadin.ui.AbstractTextField;
52 import com.vaadin.ui.Component;
53 import com.vaadin.ui.DefaultFieldFactory;
54 import com.vaadin.ui.Field;
55 import com.vaadin.ui.TableFieldFactory;
56 import com.vaadin.ui.TextField;
57 import com.vaadin.ui.Tree.CollapseEvent;
58 import com.vaadin.ui.Tree.CollapseListener;
59 import com.vaadin.ui.Tree.ExpandEvent;
60 import com.vaadin.ui.Tree.ExpandListener;
61
62
63
64
65
66 public class InplaceEditingTreeTable extends MagnoliaTreeTable implements ItemClickEvent.ItemClickListener, ItemEditedEvent.Notifier {
67
68 private Object editingItemId;
69
70 private Object editingPropertyId;
71
72 private List<Object> editableColumns = new ArrayList<Object>();
73
74 private InplaceEditingFieldFactory fieldFactory;
75
76 private ColumnGenerator bypassedColumnGenerator;
77
78 private final List<ItemEditedEvent.Handler> listeners = new ArrayList<ItemEditedEvent.Handler>();
79
80 private ActionManager shortcutActionManager;
81
82 public InplaceEditingTreeTable() {
83 super();
84 fieldFactory = new InplaceEditingFieldFactory();
85 setTableFieldFactory(fieldFactory);
86 addItemClickListener(asItemClickListener());
87
88 addExpandListener(new ExpandListener() {
89
90 @Override
91 public void nodeExpand(ExpandEvent event) {
92 if (editingItemId != null) {
93 setEditing(null, null);
94 }
95 }
96 });
97
98 addCollapseListener(new CollapseListener() {
99
100 @Override
101 public void nodeCollapse(CollapseEvent event) {
102 if (editingItemId != null) {
103 setEditing(null, null);
104 }
105 }
106 });
107 }
108
109
110
111 public void setEditableColumns(Object... editablePropertyIds) {
112 editableColumns.clear();
113 editableColumns.addAll(Arrays.asList(editablePropertyIds));
114 }
115
116
117
118
119
120
121
122 public void setEditing(Object itemId, Object propertyId) {
123
124 if (getItem(itemId) == null) {
125 itemId = null;
126 propertyId = null;
127 }
128
129 if (itemId != null && propertyId != null) {
130 Item item = getItem(itemId);
131 Property<?> property = item.getItemProperty(propertyId);
132
133
134
135 if (property == null || property.getValue() instanceof List) {
136 return;
137 } else {
138 if ((bypassedColumnGenerator = getColumnGenerator(propertyId)) != null) {
139 removeGeneratedColumn(propertyId);
140 }
141 fieldFactory.createFieldAndRegister(getContainerDataSource(), itemId, propertyId, this);
142 }
143 } else {
144 if (bypassedColumnGenerator != null) {
145 addGeneratedColumn(editingPropertyId, bypassedColumnGenerator);
146 bypassedColumnGenerator = null;
147 }
148 focus();
149 }
150
151 this.editingItemId = itemId;
152 this.editingPropertyId = propertyId;
153
154 refreshRowCache();
155 }
156
157
158
159
160 @Override
161 protected int getFirstUpdatedItemIndex() {
162
163
164
165 return super.getFirstUpdatedItemIndex();
166 }
167
168 @Override
169 protected int getUpdatedRowCount() {
170
171
172
173 return super.getUpdatedRowCount();
174 }
175
176 @Override
177 protected int getFirstAddedItemIndex() {
178
179
180
181 return super.getFirstAddedItemIndex();
182 }
183
184 @Override
185 protected int getAddedRowCount() {
186
187
188
189 return super.getAddedRowCount();
190 }
191
192 @Override
193 protected boolean shouldHideAddedRows() {
194
195
196
197 return super.shouldHideAddedRows();
198 }
199
200 @Override
201 protected boolean isPartialRowUpdate() {
202 return
203
204 super.isPartialRowUpdate();
205 }
206
207
208
209
210
211
212 private class InplaceEditingFieldFactory implements TableFieldFactory {
213
214 private Field<?> inplaceEditingField;
215
216
217
218
219
220 public void createFieldAndRegister(Container container, Object itemId, Object propertyId, Component uiContext) {
221
222 Property<?> containerProperty = container.getContainerProperty(itemId, propertyId);
223
224 if (containerProperty == null) {
225 return;
226 }
227 Class<?> type = containerProperty.getType();
228 final Field<?> field = createFieldByPropertyType(type);
229 if (field != null) {
230 field.setCaption(DefaultFieldFactory.createCaptionByPropertyId(propertyId));
231 field.setSizeFull();
232 }
233
234
235 if (field instanceof AbstractTextField) {
236 final AbstractTextField tf = (AbstractTextField) field;
237 tf.addBlurListener(new FieldEvents.BlurListener() {
238
239 @Override
240 public void blur(BlurEvent event) {
241 fireItemEditedEvent(tf.getPropertyDataSource());
242 setEditing(null, null);
243 }
244 });
245 tf.focus();
246
247 tf.addValueChangeListener(new ValueChangeListener() {
248
249 @Override
250 public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
251 final Object text = event.getProperty().getValue();
252 if (text instanceof String) {
253 tf.selectAll();
254 }
255 tf.removeValueChangeListener(this);
256 }
257 });
258 }
259
260
261 InplaceEditingTreeTable.this.registerComponent(field);
262
263 inplaceEditingField = field;
264 }
265
266 @Override
267 public Field<?> createField(Container container, Object itemId, Object propertyId, Component uiContext) {
268
269
270 if (editableColumns.contains(propertyId) && itemId.equals(editingItemId) && propertyId.equals(editingPropertyId)) {
271 return inplaceEditingField;
272 }
273
274 return null;
275 }
276
277 private Field<?> createFieldByPropertyType(Class<?> type) {
278 if (type == null) {
279 return null;
280 }
281
282 return new TextField();
283 }
284
285 }
286
287
288
289
290
291 @Override
292 public void addItemEditedListener(ItemEditedEvent.Handler listener) {
293 listeners.add(listener);
294 }
295
296 @Override
297 public void removeItemEditedListener(ItemEditedEvent.Handler listener) {
298 if (listeners.contains(listener)) {
299 listeners.remove(listener);
300 }
301 }
302
303
304
305
306
307
308 public void fireItemEditedEvent(Property property) {
309
310 Item item = getContainerDataSource().getItem(editingItemId);
311 if (item == null) {
312 return;
313 }
314
315 Property itemProperty = item.getItemProperty(editingPropertyId);
316 if (itemProperty == null) {
317 return;
318 }
319
320 itemProperty.setValue(property.getValue());
321
322 ItemEditedEvent event = new ItemEditedEvent(item);
323 for (ItemEditedEvent.Handler listener : listeners) {
324 listener.onItemEdited(event);
325 }
326 }
327
328
329
330 @Override
331 public void itemClick(ItemClickEvent event) {
332 if (event.isDoubleClick() && editableColumns.contains(event.getPropertyId())) {
333 setEditing(event.getItemId(), event.getPropertyId());
334 }
335 }
336
337 private ItemClickEvent.ItemClickListener asItemClickListener() {
338 return this;
339 }
340
341
342
343
344 public void editNextCell(Field<?> field) {
345
346 TableCell nextCell = getNextEditableCandidate(editingItemId, editingPropertyId);
347
348 fireItemEditedEvent(field.getPropertyDataSource());
349
350 setEditing(nextCell.getItemId(), nextCell.getPropertyId());
351 }
352
353 public void editPreviousCell(Field<?> field) {
354
355 TableCell previousCell = getPreviousEditableCandidate(editingItemId, editingPropertyId);
356
357 fireItemEditedEvent(field.getPropertyDataSource());
358
359 setEditing(previousCell.getItemId(), previousCell.getPropertyId());
360 }
361
362 public void editFirstCellofFirstSelectedRow() {
363
364
365 Object firstSelectedId = getValue();
366 if (firstSelectedId instanceof Set && ((Set<?>) firstSelectedId).size() > 0) {
367 firstSelectedId = ((Set<?>) firstSelectedId).iterator().next();
368 }
369
370 Object propertyId = getVisibleColumns()[0];
371 if (!editableColumns.contains(propertyId)) {
372 propertyId = getNextEditableCandidate(firstSelectedId, propertyId).getPropertyId();
373 }
374 setEditing(firstSelectedId, propertyId);
375 }
376
377
378
379 private TableCell getNextEditableCandidate(Object itemId, Object propertyId) {
380
381 List<Object> visibleColumns = Arrays.asList(getVisibleColumns());
382 Object newItemId = itemId;
383 int newColumn = visibleColumns.indexOf(propertyId);
384 do {
385 if (newColumn == visibleColumns.size() - 1) {
386 newItemId = nextItemId(newItemId);
387 }
388 newColumn = (newColumn + 1) % visibleColumns.size();
389 } while (!editableColumns.contains(visibleColumns.get(newColumn)) && newItemId != null);
390
391 return new TableCell(newItemId, visibleColumns.get(newColumn));
392 }
393
394 private TableCell getPreviousEditableCandidate(Object itemId, Object propertyId) {
395
396 List<Object> visibleColumns = Arrays.asList(getVisibleColumns());
397 Object newItemId = itemId;
398 int newColumn = visibleColumns.indexOf(propertyId);
399 do {
400 if (newColumn == 0) {
401 newItemId = prevItemId(newItemId);
402 }
403 newColumn = (newColumn + visibleColumns.size() - 1) % visibleColumns.size();
404 } while (!editableColumns.contains(visibleColumns.get(newColumn)) && newItemId != null);
405
406 return new TableCell(newItemId, visibleColumns.get(newColumn));
407 }
408
409
410
411
412 private class TableCell {
413
414 private final Object itemId;
415
416 private final Object propertyId;
417
418 public TableCell(Object itemId, Object propertyId) {
419 this.itemId = itemId;
420 this.propertyId = propertyId;
421 }
422
423 public Object getItemId() {
424 return itemId;
425 }
426
427 public Object getPropertyId() {
428 return propertyId;
429 }
430 }
431
432 }