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