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