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.list.ListViewImpl;
38
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.List;
43 import java.util.Set;
44
45 import com.vaadin.data.Item;
46 import com.vaadin.data.Property;
47 import com.vaadin.event.Action;
48 import com.vaadin.event.Action.Container;
49 import com.vaadin.event.Action.Handler;
50 import com.vaadin.event.FieldEvents.BlurEvent;
51 import com.vaadin.event.FieldEvents.BlurListener;
52 import com.vaadin.event.ItemClickEvent;
53 import com.vaadin.event.ItemClickEvent.ItemClickListener;
54 import com.vaadin.event.ShortcutAction;
55 import com.vaadin.event.dd.DropHandler;
56 import com.vaadin.ui.Field;
57 import com.vaadin.ui.Table;
58 import com.vaadin.ui.Table.ColumnGenerator;
59 import com.vaadin.ui.Table.TableDragMode;
60 import com.vaadin.ui.Tree.CollapseEvent;
61 import com.vaadin.ui.Tree.CollapseListener;
62 import com.vaadin.ui.Tree.ExpandEvent;
63 import com.vaadin.ui.Tree.ExpandListener;
64 import com.vaadin.ui.TreeTable;
65
66
67
68
69 public class TreeViewImpl extends ListViewImpl implements TreeView {
70
71 private MagnoliaTreeTable tree;
72
73 private boolean editable;
74 private final List<Object> editableColumns = new ArrayList<Object>();
75 private InplaceEditingFieldFactory fieldFactory;
76 private ExpandListener expandListener;
77 private CollapseListener collapseListener;
78 private Container shortcutActionManager;
79 private EditingKeyboardHandler editingKeyboardHandler;
80 private ColumnGenerator bypassedColumnGenerator;
81
82 @Override
83 protected MagnoliaTreeTable createTable(com.vaadin.data.Container container) {
84 tree = new MagnoliaTreeTable(container);
85 return tree;
86 }
87
88 @Override
89 protected void initializeTable(Table table) {
90 super.initializeTable(table);
91 tree.setSortEnabled(false);
92 int size = tree.size();
93 if (size > 0) {
94 tree.setCollapsed(tree.firstItemId(), false);
95 }
96 }
97
98 @Override
99 public void select(List<Object> itemIds) {
100 Object firstItemId = itemIds == null || itemIds.isEmpty() ? null : itemIds.get(0);
101 if (firstItemId == null || tree.isSelected(firstItemId)) {
102 return;
103 }
104 tree.focus();
105 expandTreeToNode(firstItemId, false);
106
107 tree.setValue(null);
108 for (Object id : itemIds) {
109 tree.select(id);
110 }
111 tree.setCurrentPageFirstItemId(firstItemId);
112 }
113
114 @Override
115 public void expand(Object itemId) {
116 expandTreeToNode(itemId, true);
117 }
118
119 private void expandTreeToNode(Object id, boolean expandNode) {
120 com.vaadin.data.Container.Hierarchical container = tree.getContainerDataSource();
121 Item item = container.getItem(id);
122
123 if (item == null) {
124 return;
125 }
126
127
128 Object node = null;
129 if (!container.areChildrenAllowed(id)) {
130 node = container.getParent(id);
131 } else {
132 if (expandNode) {
133 node = id;
134 } else {
135 Object parent = container.getParent(id);
136
137 if (parent != null) {
138 node = parent;
139 }
140 }
141 }
142
143
144 while (node != null) {
145 tree.setCollapsed(node, false);
146 node = container.getParent(node);
147 }
148
149 }
150
151 @Override
152 protected TreeView.Listener getListener() {
153 return (TreeView.Listener) super.getListener();
154 }
155
156 @Override
157 public TreeTable asVaadinComponent() {
158 return tree;
159 }
160
161 @Override
162 public void setEditable(boolean editable) {
163 if (editable) {
164
165 fieldFactory = new InplaceEditingFieldFactory();
166 fieldFactory.setFieldBlurListener(new BlurListener() {
167
168 @Override
169 public void blur(BlurEvent event) {
170 Object source = event.getSource();
171 if (source instanceof Field<?>) {
172 saveItemProperty(((Field<?>) source).getPropertyDataSource());
173 }
174 setEditing(null, null);
175 }
176 });
177 tree.setTableFieldFactory(fieldFactory);
178
179
180 expandListener = new ExpandListener() {
181
182 @Override
183 public void nodeExpand(ExpandEvent event) {
184 setEditing(null, null);
185 }
186 };
187 collapseListener = new CollapseListener() {
188
189 @Override
190 public void nodeCollapse(CollapseEvent event) {
191 setEditing(null, null);
192 }
193 };
194 tree.addExpandListener(expandListener);
195 tree.addCollapseListener(collapseListener);
196
197
198 ItemClickListener clickListener = new ItemClickListener() {
199
200 @Override
201 public void itemClick(ItemClickEvent event) {
202 if (event.isDoubleClick()) {
203 setEditing(event.getItemId(), event.getPropertyId());
204 }
205 }
206 };
207 tree.addItemClickListener(clickListener);
208
209
210 editingKeyboardHandler = new EditingKeyboardHandler(tree);
211 if (shortcutActionManager != null) {
212 shortcutActionManager.addActionHandler(editingKeyboardHandler);
213 }
214
215 } else {
216 tree.setTableFieldFactory(null);
217 fieldFactory = null;
218 tree.removeExpandListener(expandListener);
219 tree.removeCollapseListener(collapseListener);
220 expandListener = null;
221 collapseListener = null;
222 if (shortcutActionManager != null) {
223 shortcutActionManager.removeActionHandler(editingKeyboardHandler);
224 }
225 editingKeyboardHandler = null;
226 }
227
228 tree.setEditable(editable);
229 this.editable = editable;
230 }
231
232 @Override
233 public void setEditableColumns(Object... editablePropertyIds) {
234 editableColumns.clear();
235 editableColumns.addAll(Arrays.asList(editablePropertyIds));
236 }
237
238 private void setEditing(Object itemId, Object propertyId) {
239
240
241 if (bypassedColumnGenerator != null) {
242 tree.addGeneratedColumn(fieldFactory.getEditingPropertyId(), bypassedColumnGenerator);
243 bypassedColumnGenerator = null;
244 }
245
246 if (editable && editableColumns.contains(propertyId)) {
247 if (itemId == null || propertyId == null) {
248 tree.focus();
249 fieldFactory.setEditing(null, null);
250 } else {
251
252 if ((bypassedColumnGenerator = tree.getColumnGenerator(propertyId)) != null) {
253 tree.removeGeneratedColumn(propertyId);
254 }
255 fieldFactory.setEditing(itemId, propertyId);
256 }
257 } else {
258 fieldFactory.setEditing(null, null);
259 }
260 tree.refreshRowCache();
261 }
262
263 private void saveItemProperty(Property<?> propertyDataSource) {
264 getListener().onItemEdited(fieldFactory.getEditingItemId(), fieldFactory.getEditingPropertyId(), propertyDataSource);
265 }
266
267 @Override
268 public void setDragAndDropHandler(DropHandler dropHandler) {
269 if (dropHandler != null) {
270 tree.setDragMode(TableDragMode.ROW);
271 tree.setDropHandler(dropHandler);
272 } else {
273 tree.setDragMode(TableDragMode.NONE);
274 tree.setDropHandler(null);
275 }
276 }
277
278
279
280 @Override
281 public void setActionManager(Container shortcutActionManager) {
282 if (editable) {
283 shortcutActionManager.addActionHandler(editingKeyboardHandler);
284 }
285 this.shortcutActionManager = shortcutActionManager;
286 }
287
288
289
290
291 private class EditingKeyboardHandler implements Handler {
292
293 private final ShortcutAction enter = new ShortcutAction("Enter", ShortcutAction.KeyCode.ENTER, null);
294
295 private final ShortcutAction tabNext = new ShortcutAction("Tab", ShortcutAction.KeyCode.TAB, null);
296
297 private final ShortcutAction tabPrev = new ShortcutAction("Shift+Tab", ShortcutAction.KeyCode.TAB, new int[] { ShortcutAction.ModifierKey.SHIFT });
298
299 private final ShortcutAction escape = new ShortcutAction("Esc", ShortcutAction.KeyCode.ESCAPE, null);
300
301 private final TreeTable tree;
302
303 public EditingKeyboardHandler(TreeTable tree) {
304 this.tree = tree;
305 }
306
307 @Override
308 public Action[] getActions(Object target, Object sender) {
309
310 return new Action[] { enter, tabNext, tabPrev, escape };
311 }
312
313 @Override
314 public void handleAction(Action action, Object sender, Object target) {
315
316
317
318
319
320 if (!(action instanceof ShortcutAction)) {
321 return;
322 }
323 ShortcutAction shortcut = (ShortcutAction) action;
324
325 if (target != tree && target instanceof Field) {
326 Field<?> field = (Field<?>) target;
327
328 if (shortcut == enter || shortcut.getKeyCode() == enter.getKeyCode()) {
329 saveItemProperty(fieldFactory.getField().getPropertyDataSource());
330 setEditing(null, null);
331
332 } else if (action == tabNext) {
333 saveItemProperty(fieldFactory.getField().getPropertyDataSource());
334 editNextCell(fieldFactory.getEditingItemId(), fieldFactory.getEditingPropertyId());
335
336 } else if (action == tabPrev) {
337 saveItemProperty(fieldFactory.getField().getPropertyDataSource());
338 editPreviousCell(fieldFactory.getEditingItemId(), fieldFactory.getEditingPropertyId());
339
340 } else if (action == escape) {
341 setEditing(null, null);
342 }
343 } else if (target == tree) {
344 if (tree.getValue() == null) {
345 return;
346 }
347
348 if (shortcut == enter || shortcut.getKeyCode() == enter.getKeyCode()) {
349 editFirstCell();
350
351 }
352 }
353 }
354 }
355
356
357
358 private void editNextCell(Object itemId, Object propertyId) {
359
360 List<Object> visibleColumns = Arrays.asList(tree.getVisibleColumns());
361 Object newItemId = itemId;
362 int newColumn = visibleColumns.indexOf(propertyId);
363 do {
364 if (newColumn == visibleColumns.size() - 1) {
365 newItemId = tree.nextItemId(newItemId);
366 }
367 newColumn = (newColumn + 1) % visibleColumns.size();
368 } while (!editableColumns.contains(visibleColumns.get(newColumn)) && newItemId != null);
369
370 setEditing(newItemId, visibleColumns.get(newColumn));
371 }
372
373 public void editPreviousCell(Object itemId, Object propertyId) {
374
375 List<Object> visibleColumns = Arrays.asList(tree.getVisibleColumns());
376 Object newItemId = itemId;
377 int newColumn = visibleColumns.indexOf(propertyId);
378 do {
379 if (newColumn == 0) {
380 newItemId = tree.prevItemId(newItemId);
381 }
382 newColumn = (newColumn + visibleColumns.size() - 1) % visibleColumns.size();
383 } while (!editableColumns.contains(visibleColumns.get(newColumn)) && newItemId != null);
384
385 setEditing(newItemId, visibleColumns.get(newColumn));
386 }
387
388 public void editFirstCell() {
389
390
391 Object firstSelectedId = tree.getValue();
392 if (firstSelectedId instanceof Collection) {
393 if (((Collection<?>) firstSelectedId).size() > 0) {
394 firstSelectedId = ((Set<?>) firstSelectedId).iterator().next();
395 } else {
396 firstSelectedId = null;
397 }
398 }
399
400
401 Object propertyId = tree.getVisibleColumns()[0];
402 if (!editableColumns.contains(propertyId)) {
403 editNextCell(firstSelectedId, propertyId);
404 } else {
405 setEditing(firstSelectedId, propertyId);
406 }
407 }
408
409 }