Improve icons and translations
[gpl/argeo-suite.git] / org.argeo.suite.e4 / src / org / argeo / suite / e4 / parts / DefaultDashboardEditor.java
1 package org.argeo.suite.e4.parts;
2
3 import java.util.ArrayList;
4 import java.util.Calendar;
5 import java.util.GregorianCalendar;
6 import java.util.List;
7
8 import javax.annotation.PostConstruct;
9 import javax.inject.Inject;
10 import javax.jcr.Node;
11 import javax.jcr.NodeIterator;
12 import javax.jcr.Property;
13 import javax.jcr.RepositoryException;
14
15 import org.apache.commons.logging.Log;
16 import org.apache.commons.logging.LogFactory;
17 import org.argeo.activities.ActivitiesNames;
18 import org.argeo.activities.ActivitiesService;
19 import org.argeo.activities.ActivitiesTypes;
20 import org.argeo.activities.ui.TaskViewerContextMenu;
21 import org.argeo.cms.auth.CurrentUser;
22 import org.argeo.cms.util.CmsUtils;
23 import org.argeo.connect.ConnectException;
24 import org.argeo.connect.ConnectNames;
25 import org.argeo.connect.ui.ConnectWorkbenchUtils;
26 import org.argeo.connect.ui.Refreshable;
27 import org.argeo.connect.util.ConnectJcrUtils;
28 import org.argeo.eclipse.ui.EclipseUiUtils;
29 import org.argeo.jcr.JcrUtils;
30 import org.argeo.node.NodeUtils;
31 import org.argeo.suite.e4.SuiteMsg;
32 import org.argeo.tracker.TrackerNames;
33 import org.argeo.tracker.TrackerService;
34 import org.argeo.tracker.core.TrackerUtils;
35 import org.argeo.tracker.ui.TaskListLabelProvider;
36 import org.argeo.tracker.ui.TaskVirtualListComposite;
37 import org.eclipse.e4.core.di.annotations.Optional;
38 import org.eclipse.jface.viewers.ColumnLabelProvider;
39 import org.eclipse.jface.viewers.DoubleClickEvent;
40 import org.eclipse.jface.viewers.IDoubleClickListener;
41 import org.eclipse.jface.viewers.ILabelProvider;
42 import org.eclipse.jface.viewers.IStructuredSelection;
43 import org.eclipse.jface.viewers.TableViewer;
44 import org.eclipse.swt.SWT;
45 import org.eclipse.swt.events.MouseAdapter;
46 import org.eclipse.swt.events.MouseEvent;
47 import org.eclipse.swt.events.SelectionAdapter;
48 import org.eclipse.swt.events.SelectionEvent;
49 import org.eclipse.swt.graphics.Color;
50 import org.eclipse.swt.graphics.Point;
51 import org.eclipse.swt.layout.GridData;
52 import org.eclipse.swt.layout.GridLayout;
53 import org.eclipse.swt.layout.RowLayout;
54 import org.eclipse.swt.widgets.Composite;
55 import org.eclipse.swt.widgets.Display;
56 import org.eclipse.swt.widgets.Group;
57 import org.eclipse.swt.widgets.Label;
58 import org.eclipse.swt.widgets.Link;
59
60 /** Argeo Suite Default Dashboard */
61 public class DefaultDashboardEditor extends AbstractSuiteDashboard implements Refreshable {
62 final static Log log = LogFactory.getLog(DefaultDashboardEditor.class);
63 // public final static String ID = AsUiPlugin.PLUGIN_ID +
64 // ".defaultDashboardEditor";
65
66 @Inject
67 private ActivitiesService activitiesService;
68
69 @Inject
70 @Optional
71 private TrackerService trackerService;
72
73 private String datePattern = "dd MMM yyyy";
74
75 private Composite headerCmp;
76 private Composite taskListCmp;
77 private TaskVirtualListComposite tvlc;
78
79 @PostConstruct
80 public void createPartControl(Composite parent) {
81 super.createPartControl(parent);
82 parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
83 Composite bodyCmp = new Composite(parent, SWT.NO_FOCUS);
84 bodyCmp.setLayoutData(EclipseUiUtils.fillAll());
85 bodyCmp.setLayout(new GridLayout());
86
87 headerCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
88 headerCmp.setLayoutData(EclipseUiUtils.fillWidth());
89
90 taskListCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
91 taskListCmp.setLayoutData(EclipseUiUtils.fillAll());
92 forceRefresh(null);
93 }
94
95 @Override
96 public void forceRefresh(Object object) {
97 CmsUtils.clear(headerCmp);
98 populateHeaderPart(headerCmp, NodeUtils.getUserHome(getSession()));
99
100 CmsUtils.clear(taskListCmp);
101 populateTaskListCmp(taskListCmp);
102
103 headerCmp.getParent().layout(true, true);
104 }
105
106 private void populateTaskListCmp(Composite parent) {
107 parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
108 NodeIterator nit = activitiesService.getMyTasks(getSession(), true);
109 if (!nit.hasNext()) {
110 Composite noTaskCmp = new Composite(parent, SWT.NO_FOCUS);
111 noTaskCmp.setLayoutData(EclipseUiUtils.fillAll());
112 noTaskCmp.setLayout(new GridLayout());
113
114 // Label noTaskLbl = new Label(noTaskCmp, SWT.CENTER);
115 // noTaskLbl.setText("<i> <big> You have no pending Task. </big> </i>");
116 // CmsUtils.markup(noTaskLbl);
117 // noTaskLbl.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, true));
118
119 } else {
120 TaskListLabelProvider labelProvider = new TaskListLabelProvider(getSystemAppService());
121 tvlc = new TaskVirtualListComposite(parent, SWT.NO_FOCUS, (ILabelProvider) labelProvider, 54);
122 tvlc.setLayoutData(EclipseUiUtils.fillAll());
123 final TableViewer viewer = tvlc.getTableViewer();
124 viewer.setInput(JcrUtils.nodeIteratorToList(nit).toArray());
125 final TaskViewerContextMenu contextMenu = new TaskViewerContextMenu(viewer, getSession(),
126 activitiesService) {
127 @Override
128 public boolean performAction(String actionId) {
129 boolean hasChanged = super.performAction(actionId);
130 if (hasChanged) {
131 viewer.getTable().setFocus();
132 forceRefresh(null);
133 // NodeIterator nit =
134 // activitiesService.getMyTasks(getSession(), true);
135 // viewer.setInput(JcrUtils.nodeIteratorToList(nit).toArray());
136 }
137 return hasChanged;
138 }
139 };
140 viewer.getTable().addMouseListener(new MouseAdapter() {
141 private static final long serialVersionUID = 6737579410648595940L;
142
143 @Override
144 public void mouseDown(MouseEvent e) {
145 if (e.button == 3) {
146 // contextMenu.setCurrFolderPath(currDisplayedFolder);
147 contextMenu.show(viewer.getTable(), new Point(e.x, e.y),
148 (IStructuredSelection) viewer.getSelection());
149 }
150 }
151 });
152 viewer.addDoubleClickListener(new IDoubleClickListener() {
153
154 @Override
155 public void doubleClick(DoubleClickEvent event) {
156 IStructuredSelection sel = (IStructuredSelection) viewer.getSelection();
157 Node task = (Node) sel.getFirstElement();
158 getSystemWorkbenchService().openEntityEditor(task);
159 }
160 });
161 }
162 }
163
164 private boolean isOverdue(Node node, String propName) {
165 try {
166 Calendar now = GregorianCalendar.getInstance();
167 return node.hasProperty(propName) && node.getProperty(propName).getDate().before(now);
168 } catch (RepositoryException e) {
169 throw new ConnectException("Cannot check overdue status with property " + propName + " on " + node, e);
170 }
171 }
172
173 private void populateHeaderPart(Composite bodyCmp, Node context) {
174 bodyCmp.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, true)));
175
176 Composite leftCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
177 leftCmp.setLayout(new GridLayout());
178 leftCmp.setLayoutData(EclipseUiUtils.fillWidth());
179 Composite rightCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
180 rightCmp.setLayout(new GridLayout());
181 rightCmp.setLayoutData(EclipseUiUtils.fillWidth());
182
183 // Title
184 Label titleLbl = new Label(leftCmp, SWT.WRAP | SWT.LEAD);
185 CmsUtils.markup(titleLbl);
186 String titleStr = "<big><b>" + CurrentUser.getDisplayName() + "</b></big>";
187 titleLbl.setText(titleStr);
188 GridData gd = new GridData(SWT.BEGINNING, SWT.TOP, false, false);
189 // gd.verticalIndent = 5;
190 // gd.horizontalIndent = 10;
191 titleLbl.setLayoutData(gd);
192
193 final Link createTaskLk = new Link(leftCmp, SWT.CENTER);
194 // createTaskLk.setText("<a>Create a task</a>");
195 createTaskLk.setText("<a>" + SuiteMsg.newTodo.lead() + "</a>");
196 gd = new GridData(SWT.BEGINNING, SWT.TOP, false, false);
197 // gd.verticalIndent = 5;
198 gd.horizontalIndent = 10;
199 createTaskLk.setLayoutData(gd);
200
201 createTaskLk.addSelectionListener(new SelectionAdapter() {
202 private static final long serialVersionUID = -9028457805156989935L;
203
204 @Override
205 public void widgetSelected(SelectionEvent e) {
206 // String mainMixin = TrackerTypes.TRACKER_TASK;
207 String mainMixin = ActivitiesTypes.ACTIVITIES_TASK;
208 String pathCreated = ConnectWorkbenchUtils.createAndConfigureEntity(createTaskLk.getShell(),
209 getSession(), getSystemAppService(), getSystemWorkbenchService(), mainMixin);
210 if (EclipseUiUtils.notEmpty(pathCreated))
211 forceRefresh(null);
212 }
213 });
214
215 NodeIterator nit = activitiesService.getMyTasks(getSession(), true);
216 if (nit.hasNext()) {
217 List<Node> overdueTasks = new ArrayList<>();
218 while (nit.hasNext()) {
219 Node currNode = nit.nextNode();
220 if (isOverdue(currNode, ActivitiesNames.ACTIVITIES_DUE_DATE))
221 overdueTasks.add(currNode);
222 }
223 if (!overdueTasks.isEmpty()) {
224 Composite overdueCmp = new Composite(leftCmp, SWT.NO_FOCUS);
225 long size = overdueTasks.size();
226 String overdueStr = "You have " + size + " overdue task" + (size > 1 ? "s" : "") + ": ";
227 populateMuliValueClickableList(overdueCmp, overdueTasks.toArray(new Node[0]), new TaskLp(), overdueStr);
228 }
229 }
230
231 if (trackerService != null) {
232 nit = trackerService.getMyMilestones(getSession(), true);
233 List<Node> openMilestones = new ArrayList<>();
234
235 if (nit.hasNext()) {
236 List<Node> overdueMilestones = new ArrayList<>();
237 while (nit.hasNext()) {
238 Node currNode = nit.nextNode();
239 openMilestones.add(currNode);
240 if (isOverdue(currNode, TrackerNames.TRACKER_TARGET_DATE))
241 overdueMilestones.add(currNode);
242 }
243 if (!overdueMilestones.isEmpty()) {
244 Composite overdueCmp = new Composite(leftCmp, SWT.NO_FOCUS);
245 long size = overdueMilestones.size();
246 String overdueStr = "You have " + size + " overdue milestone" + (size > 1 ? "s" : "") + ": ";
247 populateMuliValueClickableList(overdueCmp, overdueMilestones.toArray(new Node[0]),
248 new MilestoneLp(), overdueStr);
249 }
250 }
251
252 // My projects
253 List<Node> openProjects = JcrUtils.nodeIteratorToList(trackerService.getMyProjects(getSession(), true));
254 if (!openProjects.isEmpty()) {
255 Group myProjectsGp = new Group(rightCmp, SWT.NO_FOCUS);
256 myProjectsGp.setText("My open projects");
257 myProjectsGp.setLayoutData(EclipseUiUtils.fillWidth());
258 populateMuliValueClickableList(myProjectsGp, openProjects.toArray(new Node[0]), new ProjectLp(), null);
259 }
260
261 // My Milestones
262 if (!openMilestones.isEmpty()) {
263 Group myMilestoneGp = new Group(rightCmp, SWT.NO_FOCUS);
264 myMilestoneGp.setText("My open milestones");
265 myMilestoneGp.setLayoutData(EclipseUiUtils.fillWidth());
266 populateMuliValueClickableList(myMilestoneGp, openMilestones.toArray(new Node[0]), new MilestoneLp(),
267 null);
268 }
269 }
270 }
271
272 private class ProjectLp extends ColumnLabelProvider {
273 private static final long serialVersionUID = 7231233932794865555L;
274
275 @Override
276 public String getText(Object element) {
277 Node project = (Node) element;
278
279 String percent;
280 NodeIterator nit = TrackerUtils.getIssues(project, null, null, null, true);
281 long openNb = nit.getSize();
282
283 nit = TrackerUtils.getIssues(project, null, null, null, false);
284 long allNb = nit.getSize();
285
286 if (allNb < 1)
287 percent = "empty";
288 else {
289 double num = allNb - openNb;
290 double result = num / allNb * 100;
291 percent = String.format("%.1f", result) + "% done";
292 }
293 StringBuilder builder = new StringBuilder();
294 builder.append("<a>").append(ConnectJcrUtils.get(project, Property.JCR_TITLE)).append("</a>");
295 builder.append(" (").append(percent).append(")");
296
297 return builder.toString();
298 }
299 }
300
301 private class MilestoneLp extends ColumnLabelProvider {
302 private static final long serialVersionUID = 7231233932794865555L;
303
304 @Override
305 public String getText(Object element) {
306 Node milestone = (Node) element;
307 Node project = TrackerUtils.getRelatedProject(trackerService, milestone);
308 String dueDate = ConnectJcrUtils.getDateFormattedAsString(milestone, TrackerNames.TRACKER_TARGET_DATE,
309 datePattern);
310
311 String percent;
312 String propName = TrackerNames.TRACKER_MILESTONE_UID;
313 String muid = ConnectJcrUtils.get(milestone, ConnectNames.CONNECT_UID);
314 NodeIterator nit = TrackerUtils.getIssues(project, null, propName, muid, true);
315 long openNb = nit.getSize();
316
317 nit = TrackerUtils.getIssues(project, null, propName, muid, false);
318 long allNb = nit.getSize();
319
320 if (allNb < 1)
321 percent = "empty";
322 else {
323 double num = allNb - openNb;
324 double result = num / allNb * 100;
325 percent = String.format("%.1f", result) + "% done";
326 }
327 StringBuilder builder = new StringBuilder();
328 builder.append("<a>").append(ConnectJcrUtils.get(milestone, Property.JCR_TITLE)).append("</a>");
329 builder.append(" (");
330 if (EclipseUiUtils.notEmpty(dueDate))
331 builder.append("due to ").append(dueDate).append(", ");
332
333 builder.append(percent).append(")");
334 return builder.toString();
335 }
336
337 @Override
338 public Color getForeground(Object element) {
339 Node milestone = (Node) element;
340 Calendar dueDate = ConnectJcrUtils.getDateValue(milestone, TrackerNames.TRACKER_TARGET_DATE);
341 if (dueDate != null && dueDate.before(Calendar.getInstance()))
342 return Display.getCurrent().getSystemColor(SWT.COLOR_RED);
343 return null;
344 }
345 }
346
347 private class TaskLp extends ColumnLabelProvider {
348 private static final long serialVersionUID = 7231233932794865555L;
349
350 @Override
351 public String getText(Object element) {
352 Node task = (Node) element;
353 String dueDate = ConnectJcrUtils.getDateFormattedAsString(task, ActivitiesNames.ACTIVITIES_DUE_DATE,
354 datePattern);
355
356 StringBuilder builder = new StringBuilder();
357 builder.append("<a>").append(ConnectJcrUtils.get(task, Property.JCR_TITLE)).append("</a>");
358 if (EclipseUiUtils.notEmpty(dueDate))
359 builder.append(" (").append("due to ").append(dueDate).append(")");
360 return builder.toString();
361 }
362
363 @Override
364 public Color getForeground(Object element) {
365 Node milestone = (Node) element;
366 Calendar dueDate = ConnectJcrUtils.getDateValue(milestone, TrackerNames.TRACKER_TARGET_DATE);
367 if (dueDate != null && dueDate.before(Calendar.getInstance()))
368 return Display.getCurrent().getSystemColor(SWT.COLOR_RED);
369 return null;
370 }
371 }
372
373 // public void setActivitiesService(ActivitiesService activitiesService) {
374 // this.activitiesService = activitiesService;
375 // }
376 //
377 // public void setTrackerService(TrackerService trackerService) {
378 // this.trackerService = trackerService;
379 // }
380
381 // LOCAL HELPERS
382 private void populateMuliValueClickableList(Composite parent, Node[] nodes, ColumnLabelProvider lp,
383 String listLabel) {
384 CmsUtils.clear(parent);
385 RowLayout rl = new RowLayout(SWT.HORIZONTAL | SWT.WRAP);
386 rl.wrap = true;
387 rl.marginLeft = rl.marginTop = rl.marginBottom = 0;
388 rl.marginRight = 8;
389 parent.setLayout(rl);
390
391 if (EclipseUiUtils.notEmpty(listLabel)) {
392 Link link = new Link(parent, SWT.NONE);
393 link.setText(listLabel);
394 link.setFont(EclipseUiUtils.getBoldFont(parent));
395 }
396
397 int i = 1;
398 for (Node node : nodes) {
399 Link link = new Link(parent, SWT.NONE);
400 CmsUtils.markup(link);
401 link.setText(lp.getText(node) + (i != nodes.length ? ", " : ""));
402 i++;
403 // Color fc = lp.getForeground(node);
404 // if (fc != null)
405 // link.setForeground(fc);
406
407 link.addSelectionListener(new SelectionAdapter() {
408 private static final long serialVersionUID = 1L;
409
410 @Override
411 public void widgetSelected(final SelectionEvent event) {
412 // CommandUtils.callCommand(getSystemWorkbenchService().getOpenEntityEditorCmdId(),
413 // ConnectEditor.PARAM_JCR_ID, ConnectJcrUtils.getIdentifier(node));
414 getSystemWorkbenchService().openEntityEditor(node);
415 }
416 });
417 }
418 }
419
420 }