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.admincentral.shellapp.pulse.item.list;
35
36 import info.magnolia.i18nsystem.SimpleTranslator;
37 import info.magnolia.ui.admincentral.shellapp.pulse.item.detail.PulseItemCategory;
38 import info.magnolia.ui.admincentral.shellapp.pulse.item.detail.PulseItemCategoryNavigator;
39 import info.magnolia.ui.api.shell.Shell;
40 import info.magnolia.ui.vaadin.grid.MagnoliaTreeTable;
41
42 import java.util.Collection;
43 import java.util.HashSet;
44 import java.util.Set;
45
46 import javax.inject.Inject;
47
48 import com.vaadin.data.Container;
49 import com.vaadin.data.Container.ItemSetChangeEvent;
50 import com.vaadin.data.Item;
51 import com.vaadin.data.Property;
52 import com.vaadin.data.Property.ValueChangeEvent;
53 import com.vaadin.data.Property.ValueChangeListener;
54 import com.vaadin.event.ItemClickEvent;
55 import com.vaadin.event.MouseEvents.ClickEvent;
56 import com.vaadin.shared.ui.label.ContentMode;
57 import com.vaadin.ui.Component;
58 import com.vaadin.ui.Label;
59 import com.vaadin.ui.Table;
60 import com.vaadin.ui.Table.GeneratedRow;
61 import com.vaadin.ui.TreeTable;
62 import com.vaadin.ui.VerticalLayout;
63
64
65
66
67 public abstract class AbstractPulseListView implements PulseListView {
68
69 public static final String GROUP_PLACEHOLDER_ITEMID = "##SUBSECTION##";
70
71 private final String[] order;
72
73 private final String[] headers;
74
75 private final TreeTable itemTable = new MagnoliaTreeTable();
76
77 private final VerticalLayout root = new VerticalLayout();
78
79 private final PulseItemCategoryNavigator navigator;
80
81 private final SimpleTranslator i18n;
82
83 private PulseListView.Listener listener;
84
85 private Label emptyPlaceHolder;
86
87 private PulseListFooter footer;
88
89 private PulseItemCategory currentlySelectedCategory = PulseItemCategory.UNCLAIMED;
90
91 private boolean categoryFilterAlreadyApplied;
92
93 private Property.ValueChangeListener selectionListener = new Property.ValueChangeListener() {
94
95 private Set<Object> prevSelected = new HashSet<Object>();
96
97 @Override
98 public void valueChange(ValueChangeEvent event) {
99
100
101
102
103 itemTable.removeValueChangeListener(this);
104
105 @SuppressWarnings("unchecked")
106 Set<Object> currSelected = new HashSet<Object>((Set<Object>) event.getProperty().getValue());
107 Set<Object> added = new HashSet<Object>(currSelected);
108 Set<Object> removed = new HashSet<Object>(prevSelected);
109
110 added.removeAll(prevSelected);
111 removed.removeAll(currSelected);
112
113 prevSelected = currSelected;
114
115
116
117
118
119 selectChildren(added, true);
120 selectChildren(removed, false);
121
122
123 for (Object child : removed) {
124 Object parent = listener.getParent(child);
125 if (parent != null) {
126 itemTable.unselect(parent);
127 prevSelected.remove(parent);
128 }
129 }
130
131
132
133
134 for (Object child : added) {
135 Object parent = listener.getParent(child);
136 if (isAllChildrenSelected(parent)) {
137 itemTable.select(parent);
138 prevSelected.add(parent);
139 } else {
140 itemTable.unselect(parent);
141 prevSelected.remove(parent);
142 }
143 }
144
145 itemTable.addValueChangeListener(this);
146 footer.updateStatus();
147 }
148
149 private boolean isAllChildrenSelected(Object parent) {
150 if (parent == null) {
151 return false;
152 }
153
154 Collection<?> siblings = listener.getGroup(parent);
155 boolean allSelected = true;
156
157 if (siblings != null) {
158 for (Object sibling : siblings) {
159 if (!itemTable.isSelected(sibling)) {
160 allSelected = false;
161 }
162 }
163 } else {
164 return false;
165 }
166
167 return allSelected;
168 }
169
170 private void selectChildren(Set<Object> parents, boolean check) {
171 for (Object parent : parents) {
172 Collection<?> group = listener.getGroup(parent);
173 if (group != null) {
174 for (Object child : group) {
175 if (check) {
176 itemTable.select(child);
177 prevSelected.add(child);
178 } else {
179 itemTable.unselect(child);
180 prevSelected.remove(child);
181 }
182 }
183 }
184 }
185 }
186 };
187
188 private ValueChangeListener groupingListener = new ValueChangeListener() {
189
190 @Override
191 public void valueChange(ValueChangeEvent event) {
192 boolean checked = event.getProperty().getValue().equals(Boolean.TRUE);
193 doGrouping(checked);
194 }
195 };
196
197
198
199
200 private Table.RowGenerator groupingRowGenerator = new Table.RowGenerator() {
201
202 @Override
203 public GeneratedRow generateRow(Table table, Object itemId) {
204
205
206
207
208
209
210 if (itemId.toString().startsWith(GROUP_PLACEHOLDER_ITEMID)) {
211 Item item = table.getItem(itemId);
212 return generateGroupingRow(item);
213 }
214
215 return null;
216 }
217 };
218
219 @Inject
220 public AbstractPulseListView(Shell shell, SimpleTranslator i18n, String[] order, String[] headers, String emptyMessage, PulseItemCategory... categories) {
221 this.i18n = i18n;
222 this.order = order;
223 this.headers = headers;
224 navigator = PulseItemCategoryNavigator.createSubRowNavigator(i18n, categories);
225 root.setSizeFull();
226 construct(emptyMessage);
227 }
228
229 @Override
230 public void refresh() {
231
232 if ((currentlySelectedCategory != PulseItemCategory.ALL_MESSAGES || currentlySelectedCategory != PulseItemCategory.ALL_TASKS) && !categoryFilterAlreadyApplied) {
233 listener.filterByItemCategory(currentlySelectedCategory);
234 }
235
236 categoryFilterAlreadyApplied = false;
237 footer.updateStatus();
238 itemTable.sort();
239 doGrouping(false);
240 }
241
242 @Override
243 public void setDataSource(Container dataSource) {
244 itemTable.setContainerDataSource(dataSource);
245 itemTable.setVisibleColumns(order);
246 itemTable.setColumnHeaders(headers);
247 }
248
249 @Override
250 public void setListener(PulseListView.Listener listener) {
251 this.listener = listener;
252
253
254 this.footer.setMessagesListener(listener);
255 }
256
257 @Override
258 public void updateCategoryBadgeCount(PulseItemCategory category, int count) {
259 navigator.updateCategoryBadgeCount(category, count);
260 }
261
262 @Override
263 public Component asVaadinComponent() {
264 return root;
265 }
266
267 public void setFooter(PulseListFooter footer) {
268 this.footer = footer;
269 root.addComponent(footer);
270 footer.setHeight("60px");
271 }
272
273 private void construct(String emptyMessage) {
274 root.addComponent(navigator);
275 navigator.addCategoryChangeListener(new PulseItemCategoryNavigator.ItemCategoryChangedListener() {
276
277 @Override
278 public void itemCategoryChanged(PulseItemCategoryNavigator.CategoryChangedEvent event) {
279 final PulseItemCategory category = event.getCategory();
280 onItemCategoryChanged(category);
281 }
282 });
283
284 constructTable();
285
286 emptyPlaceHolder = new Label();
287 emptyPlaceHolder.setContentMode(ContentMode.HTML);
288 emptyPlaceHolder.setValue(String.format("<span class=\"icon-pulse\"></span><div class=\"message\">%s</div>", emptyMessage));
289 emptyPlaceHolder.addStyleName("emptyplaceholder");
290
291 root.addComponent(emptyPlaceHolder);
292
293
294 footer = new PulseListFooter(itemTable, i18n, false);
295 root.addComponent(footer);
296
297 setComponentVisibility(itemTable.getContainerDataSource());
298 }
299
300 private void constructTable() {
301 root.addComponent(itemTable);
302 root.setExpandRatio(itemTable, 1f);
303 itemTable.setSizeFull();
304 itemTable.addStyleName("message-table");
305 itemTable.setSelectable(true);
306 itemTable.setMultiSelect(true);
307 itemTable.setRowGenerator(groupingRowGenerator);
308
309 navigator.addGroupingListener(groupingListener);
310
311 itemTable.addItemClickListener(new ItemClickEvent.ItemClickListener() {
312 @Override
313 public void itemClick(ItemClickEvent event) {
314 onItemClicked(event, event.getItemId());
315 }
316 });
317
318 itemTable.addValueChangeListener(selectionListener);
319 itemTable.addItemSetChangeListener(new Container.ItemSetChangeListener() {
320 @Override
321 public void containerItemSetChange(ItemSetChangeEvent event) {
322 setComponentVisibility(event.getContainer());
323 }
324 });
325 }
326
327 private void doGrouping(boolean checked) {
328 listener.setGrouping(checked);
329
330 if (checked) {
331 for (Object itemId : itemTable.getItemIds()) {
332 itemTable.setCollapsed(itemId, false);
333 }
334 }
335 }
336
337 private void setComponentVisibility(Container container) {
338 boolean isEmptyList = container.getItemIds().size() == 0;
339 if (isEmptyList) {
340 root.setExpandRatio(emptyPlaceHolder, 1f);
341 } else {
342 root.setExpandRatio(emptyPlaceHolder, 0f);
343 }
344
345 itemTable.setVisible(!isEmptyList);
346 footer.setVisible(!isEmptyList);
347 emptyPlaceHolder.setVisible(isEmptyList);
348 }
349
350
351
352
353 protected GeneratedRow generateGroupingRow(Item item) {
354 return null;
355 }
356
357 protected SimpleTranslator getI18n() {
358 return i18n;
359 }
360
361 protected TreeTable getItemTable() {
362 return itemTable;
363 }
364
365 @Override
366 public void setTabActive(PulseItemCategory category) {
367 navigator.setActive(category);
368 onItemCategoryChanged(category);
369 }
370
371 protected PulseListFooter getFooter() {
372 return footer;
373 }
374
375 protected void onItemClicked(ClickEvent event, final Object itemId) {
376 String itemIdAsString = String.valueOf(itemId);
377
378 if (itemIdAsString.startsWith(GROUP_PLACEHOLDER_ITEMID)) {
379 return;
380 }
381 if (event.isDoubleClick()) {
382 listener.onItemClicked(itemIdAsString);
383 } else {
384 if (itemTable.isSelected(itemIdAsString)) {
385 itemTable.unselect(itemIdAsString);
386 }
387 }
388 }
389
390 private void onItemCategoryChanged(final PulseItemCategory category) {
391 currentlySelectedCategory = category;
392 listener.filterByItemCategory(category);
393 categoryFilterAlreadyApplied = true;
394
395 for (String id : (Set<String>) itemTable.getValue()) {
396 itemTable.unselect(id);
397 }
398
399 switch (category) {
400 case ALL_TASKS:
401 case ALL_MESSAGES:
402 navigator.enableGroupBy(true);
403 break;
404 default:
405 navigator.enableGroupBy(false);
406 }
407
408 refresh();
409 }
410 }